From fe045962e7557772b15e73b5d9265f8bed48e1b8 Mon Sep 17 00:00:00 2001 From: fbsanchez Date: Fri, 15 Mar 2019 20:44:57 +0100 Subject: [PATCH 1/7] wip generate_dot Former-commit-id: f41d5a55ec9cd8f27124d6697e92c5fb3009a2d2 --- .../include/class/NetworkMap.class.php | 471 +++++++++++++++++- pandora_console/include/constants.php | 6 + .../include/functions_networkmap.php | 16 +- 3 files changed, 476 insertions(+), 17 deletions(-) diff --git a/pandora_console/include/class/NetworkMap.class.php b/pandora_console/include/class/NetworkMap.class.php index 8f153eb415..3e3110b285 100644 --- a/pandora_console/include/class/NetworkMap.class.php +++ b/pandora_console/include/class/NetworkMap.class.php @@ -31,6 +31,10 @@ global $config; require_once $config['homedir'].'/include/functions_networkmap.php'; enterprise_include_once('include/functions_networkmap.php'); +enterprise_include_once('include/functions_discovery.php'); + +// Avoid node overlapping. +define('GRAPHVIZ_RADIUS_CONVERSION_FACTOR', 20); /** * Manage networkmaps in Pandora FMS @@ -94,6 +98,13 @@ class NetworkMap */ public $nodes; + /** + * Useful to translate id_node to id_agent or id_module. + * + * @var array + */ + public $nodeMapping; + /** * Relationship map. * @@ -160,8 +171,8 @@ class NetworkMap 'x_offs' => 0, 'y_offs' => 0, 'z_dash' => 0.5, - 'node_sep' => 0.1, - 'rank_sep' => 0.1, + 'node_sep' => 3, + 'rank_sep' => 3, 'mindist' => 1, 'kval' => 0.1, ]; @@ -341,7 +352,7 @@ class NetworkMap if ($this->idTask) { $recon_task = db_get_row_filter( 'trecon_task', - ['id_rt' => $networkmap['source_data']] + ['id_rt' => $this->idTask] ); $this->network = $recon_task['subnet']; } @@ -356,6 +367,34 @@ class NetworkMap } + /** + * Retrieves node information using id_node as mapping instead element id. + * + * @param integer $id_node Target node. + * @param string $field Field to retrieve, if null, all are return. + * + * @return mixed Array (node data) or false if error. + */ + public function getNodeData(int $id_node, $field=null) + { + if (is_array($this->nodes) === false + || is_array($this->nodeMapping) === false + ) { + return false; + } + + if (is_array($this->nodes[$this->nodeMapping[$id_node]]) === true) { + if (isset($field) === false) { + return $this->nodes[$this->nodeMapping[$id_node]]; + } else { + return $this->nodes[$this->nodeMapping[$id_node]][$field]; + } + } else { + return false; + } + } + + /** * Return nodes of current map. * @@ -407,6 +446,112 @@ class NetworkMap } + /** + * Search for nodes in current map definition. + * + * @return array Nodes detected, internal variable also updated. + */ + public function calculateNodes() + { + // Calculate. + if (!$this->nodes) { + // Search. + if ($this->idTask) { + // Network map, based on discovery task. + $this->nodes = enterprise_hook( + 'get_discovery_agents', + [$this->idTask] + ); + } + + if ($this->network) { + // Network map, based on direct network. + $this->nodes = networkmap_get_new_nodes_from_ip_mask( + $this->network + ); + } else if ($this->mapOptions['map_filter']['empty_map']) { + // Empty map returns no data. + $this->nodes = []; + } else { + // Group map. + $this->nodes = agents_get_agents( + $filter, + [ + 'id_grupo', + 'nombre', + 'id_os', + 'id_parent', + 'id_agente', + 'normal_count', + 'warning_count', + 'critical_count', + 'unknown_count', + 'total_count', + 'notinit_count', + ], + 'AR', + [ + 'field' => 'id_parent', + 'order' => 'ASC', + ] + ); + } + } + + return $this->nodes; + } + + + /** + * Search for relations for a given node in current map definition. + * + * @param array $node Origin. + * @param array $id_node Id for source node. + * @param array $id_source Id for source data, agent or module. + * + * @return array Relations found for given node. + */ + public function calculateRelations( + $node, + $id_node, + $id_source + ) { + // Calculate. + if (is_array($node) === false) { + return false; + } + + $relations = []; + + if (isset($node['id_agente_modulo'])) { + // Module. + $relations = modules_get_relations( + [ + 'id_module' => $node['id_agente_modulo'], + ] + ); + } + + if (isset($node['id_agente'])) { + // Agent. + $relations = modules_get_relations( + [ + 'id_agent' => $node['id_agente'], + ] + ); + if ($relations === false) { + $relations = []; + } + + // Add also parent relationship. + $relations[$id_node] = $node['id_parent']; + } + + // Others. + return $relations; + } + + /** * Generates or loads nodes&relations array from data load * and stores it in $this->graph. @@ -474,6 +619,206 @@ class NetworkMap } + /** + * Generates a graph definition (header only) for dot graph. + * + * @return string Dot graph header. + */ + public function openDotFile() + { + global $config; + + $overlap = 'compress'; + + $map_filter = $this->mapOptions['map_filter']; + $nooverlap = $this->mapOptions['nooverlap']; + + if (isset($config['networkmap_max_width'])) { + $size_x = ($config['networkmap_max_width'] / 100); + $size_y = ($size_x * 0.8); + } else { + $size_x = 8; + $size_y = 5.4; + $size = ''; + } + + if ($zoom > 0) { + $size_x *= $zoom; + $size_y *= $zoom; + } + + $size = $size_x.','.$size_y; + + if ($size_canvas === null) { + $size = ($this->mapOptions['size_canvas']['x'] / 100); + $size .= ','.($this->mapOptions['size_canvas']['y'] / 100); + } + + // Graphviz custom values + if (isset($map_filter['node_sep'])) { + $node_sep = $map_filter['node_sep']; + } else { + $node_sep = 0.1; + } + + if (isset($map_filter['rank_sep'])) { + $rank_sep = $map_filter['rank_sep']; + } else { + if ($layout == 'radial') { + $rank_sep = 1.0; + } else { + $rank_sep = 0.5; + } + } + + if (isset($map_filter['mindist'])) { + $mindist = $map_filter['mindist']; + } else { + $mindist = 1.0; + } + + if (isset($map_filter['kval'])) { + $kval = $map_filter['kval']; + } else { + $kval = 0.1; + } + + // BEWARE: graphwiz DONT use single ('), you need double ("). + $head = 'graph networkmap { dpi=100; bgcolor="transparent"; labeljust=l; margin=0; pad="0.75,0.75";'; + if ($nooverlap != '') { + $head .= 'overlap=scale;'; + $head .= 'outputorder=first;'; + } + + if ($layout == 'flat' + || $layout == 'spring1' + || $layout == 'spring2' + ) { + if ($nooverlap != '') { + $head .= 'overlap="scalexy";'; + } + + if ($layout == 'flat') { + $head .= 'ranksep="'.$rank_sep.'";'; + } + + if ($layout == 'spring2') { + $head .= 'K="'.$kval.'";'; + } + } + + if ($layout == 'radial') { + $head .= 'ranksep="'.$rank_sep.'";'; + } + + if ($layout == 'circular') { + $head .= 'mindist="'.$mindist.'";'; + } + + $head .= 'ratio="fill";'; + $head .= 'root=0;'; + $head .= 'nodesep="'.$node_sep.'";'; + $head .= 'size="'.$size.'";'; + + $head .= "\n"; + + return $head; + } + + + /** + * Creates a node in dot format. + * Requirements: + * id_node + * id_source + * status => defines 'color' + * label + * image + * url + * + * @param array $data Node definition. + * + * @return string Dot node. + */ + public function createDotNode($data) + { + global $config; + global $hack_networkmap_mobile; + + $dot_str = ''; + + $color = COL_NORMAL; + $label = $data['label']; + $url = 'none'; + $parent = $data['parent']; + $font_size = $this->mapOptions['font_size']; + $radius = $this->mapOptions['map_filter']['node_radius']; + $radius /= GRAPHVIZ_RADIUS_CONVERSION_FACTOR; + + if (strlen($label) > 16) { + $label = ui_print_truncate_text($label, 16, false, true, false); + } + + // If radius is 0, set to 1 instead. + if ($radius <= 0) { + $radius = 1; + } + + // Simple node always. This kind of node is used only to + // retrieve X,Y positions from graphviz no for personalization. + $dot_str = $data['id_node'].' [ parent="'.$data['id_parent'].'"'; + $dot_str .= ', color="'.$color.'", fontsize='.$font_size; + $dot_str .= ', shape="doublecircle"'.$url_node_link; + $dot_str .= ', style="filled", fixedsize=true, width='.$radius.', height='.$radius; + $dot_str .= ', label="'.$label.'", tooltip="'.$ajax_prefix; + $dot_str .= 'ajax.php?page=operation/agentes/ver_agente'; + $dot_str .= '&get_agent_status_tooltip=1&id_agent='.$data['id_agente']; + $dot_str .= $meta_params.'"];'."\n"; + + return $dot_str; + } + + + /** + * Creates an edge in dot format. + * Requirements: + * from + * to + * + * @param array $data Edge content. + * + * @return string Dot code for given edge. + */ + public function createDotEdge($data) + { + if (is_array($data) === false) { + return ''; + } + + if (!isset($data['from']) || !isset($data['to'])) { + return ''; + } + + $edge = "\n".$data['from'].' -- '.$data['to']; + $edge .= '[len='.$this->mapOptions['map_filter']['rank_sep']; + $edge .= ', color="#BDBDBD", headclip=false, tailclip=false,'; + $edge .= ' edgeURL=""];'."\n"; + + return $edge; + } + + + /** + * Returns dot file end string. + * + * @return string Dot file end string. + */ + public function closeDotFile() + { + return '}'; + } + + /** * Generate a graphviz string structure to be used later. * @@ -481,9 +826,65 @@ class NetworkMap */ public function generateDotGraph() { + echo 'regenerar la net es: '.$this->network.'
'; if (!isset($this->dotGraph)) { // Generate dot file. - $this->dotGraph = networkmap_generate_dot( + $nodes = []; + $edges = []; + $graph = ''; + + // Search for nodes. + $nodes = $this->calculateNodes(); + + // Search for relations. + // Build dot structure. + // Open Graph. + $graph = $this->openDotFile(); + + // Create dot nodes. + $i = 0; + foreach ($nodes as $k => $node) { + $this->nodeMapping[$i] = $k; + $graph .= $this->createDotNode( + [ + 'id_node' => $i, + 'id_source' => $node['id_agente'], + 'status' => agents_get_status_from_counts($node), + 'label' => io_safe_output($node['alias']), + 'image' => null, + 'url' => 'none', + ] + ); + + // Keep reverse reference. + $this->nodes[$k]['id_node'] = $i; + + $edges[] = $this->calculateRelations($node, $i, $k); + + // Increase for next node. + $i++; + } + + hd($edges); + foreach ($edges as $rel) { + foreach ($rel as $from => $to) { + $graph .= $this->createDotEdge( + [ + 'from' => $from, + 'to' => $this->nodes[$to]['id_node'], + ] + ); + } + } + + // Create dot edges. + $graph .= $this->closeDotFile(); + + hd($graph); + $this->dotGraph = $graph; + + /* + $this->dotGraph = networkmap_generate_dot( get_product_name(), $this->idGroup, $this->mapOptions['simple'], @@ -508,7 +909,8 @@ class NetworkMap null, $this->mapOptions['old_mode'], $this->mapOptions['map_filter'] - ); + ); + */ } } @@ -554,10 +956,7 @@ class NetworkMap include_once 'include/functions_os.php'; - $map_filter = json_decode( - $this->map['filter'], - true - ); + $map_filter = $this->mapOptions['map_filter']; /* * Let graphviz place the nodes. @@ -692,6 +1091,55 @@ class NetworkMap $nodes_and_relations['nodes'][$index]['id'] = $node['id']; $nodes_and_relations['nodes'][$index]['id_map'] = $this->idMap; + // Retrieve properties. + $node['id_agent'] = $this->getNodeData( + $node['id'], + 'id_agente' + ); + $node['id_module'] = $this->getNodeData( + $node['id'], + 'id_agente_modulo' + ); + + // Specify node type and set values. + if (isset($node['id_agent']) === true) { + if (isset($node['id_module']) === false) { + // Agent. + $node['type'] = 'agent'; + $node['source_data'] = $node['id_agent']; + $node['text'] = $this->getNodeData( + $node['id'], + 'alias' + ); + + $node['image'] = ui_print_os_icon( + $this->getNodeData($node['id'], 'id_os'), + false, + true, + true, + true, + true, + true + ); + } else if (isset($node['id_module'])) { + // Module. + $node['type'] = 'module'; + $node['source_data'] = $node['id_module']; + $node['text'] = $this->getNodeData( + $node['id'], + 'nombre' + ); + + $node['image'] = ui_print_moduletype_icon( + $this->getNodeData($node['id'], 'id_tipo_modulo'), + true, + true, + false, + true + ); + } + } + $children_count = 0; foreach ($relation_nodes as $relation) { if (($relation['parent_type'] == 'agent') || ($relation['parent_type'] == '')) { @@ -705,6 +1153,7 @@ class NetworkMap } } + // Center map over node with most children. if (empty($node_center) || $node_center['counter'] < $children_count) { $node_center['x'] = (int) $node['coords'][0]; $node_center['y'] = (int) $node['coords'][1]; @@ -1509,7 +1958,9 @@ class NetworkMap } $networkmap = $this->map; - $networkmap['filter'] = json_decode($networkmap['filter'], true); + if (is_array($networkmap['filter']) === false) { + $networkmap['filter'] = json_decode($networkmap['filter'], true); + } $networkmap['filter']['l2_network_interfaces'] = 1; diff --git a/pandora_console/include/constants.php b/pandora_console/include/constants.php index a82d36c1d0..f54ca3d550 100644 --- a/pandora_console/include/constants.php +++ b/pandora_console/include/constants.php @@ -506,6 +506,12 @@ define('OPTION_COLOR_PICKER', 11); define('NODE_TYPE', 0); define('ARROW_TYPE', 1); +// Networkmap node types. +define('NODE_AGENT', 0); +define('NODE_MODULE', 1); +define('NODE_PANDORA', 2); +define('NODE_GENERIC', 3); + // SAML attributes constants. define('SAML_ROLE_AND_TAG', 'eduPersonEntitlement'); define('SAML_USER_DESC', 'commonName'); diff --git a/pandora_console/include/functions_networkmap.php b/pandora_console/include/functions_networkmap.php index fba71431bd..4f02ef3fdb 100644 --- a/pandora_console/include/functions_networkmap.php +++ b/pandora_console/include/functions_networkmap.php @@ -489,11 +489,11 @@ function networkmap_generate_dot( // Create void statistics array $stats = []; - - $count = 0; - $group_nodes = 10; - $graph .= networkmap_create_transparent_node($count); - foreach (array_keys($orphans) as $node) { + /* + $count = 0; + $group_nodes = 10; + $graph .= networkmap_create_transparent_node($count); + foreach (array_keys($orphans) as $node) { if ($group_nodes == 0) { $count++; $graph .= networkmap_create_transparent_node($count); @@ -507,7 +507,8 @@ function networkmap_generate_dot( ); $group_nodes--; - } + } + */ // Create nodes foreach ($nodes as $node_id => $node) { @@ -714,7 +715,7 @@ function networkmap_create_edge( // Option edgeURL allows node navigation. $edge = "\n".$head.' -- '.$tail; - $edge .= '[color="#BDBDBD", headclip=false, tailclip=false, edgeURL=""];'; + $edge .= '[len='.$ranksep.', color="#BDBDBD", headclip=false, tailclip=false, edgeURL=""];'; $edge .= "\n"; return $edge; @@ -2306,6 +2307,7 @@ function networkmap_loadfile( $relations_param[] = $row; } + hd($networkmap_nodes); return $networkmap_nodes; } From 1ddeda295c504ead1385839f6ca28f8b9760a0a6 Mon Sep 17 00:00:00 2001 From: fbsanchez Date: Sun, 17 Mar 2019 01:39:56 +0100 Subject: [PATCH 2/7] WIP networkmap class Former-commit-id: fbd4ed5e5784c510bc1ea66f1627e61fa176573c --- .../include/class/NetworkMap.class.php | 1314 ++++++++++++----- .../include/functions_networkmap.php | 64 +- 2 files changed, 997 insertions(+), 381 deletions(-) diff --git a/pandora_console/include/class/NetworkMap.class.php b/pandora_console/include/class/NetworkMap.class.php index 3e3110b285..e97b56cb33 100644 --- a/pandora_console/include/class/NetworkMap.class.php +++ b/pandora_console/include/class/NetworkMap.class.php @@ -37,20 +37,36 @@ enterprise_include_once('include/functions_discovery.php'); define('GRAPHVIZ_RADIUS_CONVERSION_FACTOR', 20); /** - * Manage networkmaps in Pandora FMS + * Manage networkmaps in Pandora FMS. + * + * General steps: + * Generate a list of nodes. + * For each node, calculate relationship and add several 'module' nodes + * representing interface nodes. + * Once the base arrays are formed (nodes and relations), this class + * calls graphviz to calculate X,Y positions for given nodes. + * Translates node - relationship - positioning data into processed + * 'nodes_and_relations'. + * When printMap is called. Several information is sent to browser: + * - Base DOM items where place target map. + * - JS controllers. + * - Data translated to JSON format. + * - Interface layer. */ class NetworkMap { /** - * Target map Id. + * Target map Id, from tmap. If the maps is being simulated + * then the idMap value will be uniqid. * * @var integer */ public $idMap; /** - * Content of tmap. + * Content of tmap. Map definition. If the map is being simulated + * then defaults to constructor received parameters. * * @var array */ @@ -78,7 +94,8 @@ class NetworkMap public $idTask; /** - * Graph definition + * Graph definition. Previously was 'nodes_and_relationships' + * Is the data format before be translated to JS variables. * * @var array */ @@ -86,6 +103,7 @@ class NetworkMap /** * Dot string with graph definition. + * Its contents will be send to graphviz to calculate node positions. * * @var string */ @@ -93,6 +111,8 @@ class NetworkMap /** * Node list. + * A simple list of nodes, could content information of agents, modules... + * Is the 'raw' information. * * @var array */ @@ -100,6 +120,7 @@ class NetworkMap /** * Useful to translate id_node to id_agent or id_module. + * Maps built nodes to original node information (agents, modules). * * @var array */ @@ -107,6 +128,13 @@ class NetworkMap /** * Relationship map. + * Each element contents: + * id_parent + * id_child + * parent_type + * child_type + * id_parent_source_data (from $this->nodes) + * id_child_source_data (from $this->nodes) * * @var array */ @@ -114,20 +142,58 @@ class NetworkMap /** * Mode simple or advanced. + * Not being used yet. * * @var integer */ public $mode; /** - * Array of map options - * height - * width + * Array of map options. Because how is built, the structure matches + * with tmap definition, where map_filter is the json-extracted data. + * Duplicate options appears since tmap stores information in different + * ways (simplifies process). + * If an idMap is defined, map is loaded into this structure and used along + * the class. + * generation_method + * simple + * font_size + * nooverlap + * z_dash + * ranksep + * center + * regen + * pure + * show_snmp_modules + * cut_names + * relative + * text_filter + * dont_show_subgroups + * strict_user + * size_canvas + * old_mode + * map_filter (array) + * dont_show_subgroups + * node_radius + * x_offs + * y_offs + * z_dash + * node_sep + * rank_sep + * mindist + * kval * * @var array */ public $mapOptions; + /** + * Filter (command) to use to calculate node positions. + * + * @var string + */ + private $filter; + /** * Base constructor. @@ -149,11 +215,12 @@ class NetworkMap // Default mapOptions values. // Defines the command to generate positions. $this->mapOptions['generation_method'] = LAYOUT_SPRING1; + $this->mapOptions['width'] = 0; + $this->mapOptions['height'] = 0; $this->mapOptions['simple'] = 0; $this->mapOptions['font_size'] = 12; $this->mapOptions['nooverlap'] = 1; $this->mapOptions['z_dash'] = 0.5; - $this->mapOptions['ranksep'] = 3; $this->mapOptions['center'] = 0; $this->mapOptions['regen'] = 0; $this->mapOptions['pure'] = 0; @@ -172,7 +239,7 @@ class NetworkMap 'y_offs' => 0, 'z_dash' => 0.5, 'node_sep' => 3, - 'rank_sep' => 3, + 'rank_sep' => 5, 'mindist' => 1, 'kval' => 0.1, ]; @@ -296,12 +363,62 @@ class NetworkMap 'center_y' => 0, ]; + if (isset($this->mapOptions['generation_method']) === false) { + $this->mapOptions['generation_method'] = LAYOUT_SPRING1; + } + + // Load filter. + $this->loadFilter(); + // Will be stored in $this->graph. $this->generateNetworkMap(); } + /** + * Update filter and layout based on generation_method selected. + * + * @return boolean True or false. + */ + private function loadFilter() + { + if (is_array($this->mapOptions) === false) { + return false; + } + + switch ($this->mapOptions['generation_method']) { + case LAYOUT_CIRCULAR: + $this->filter = 'circo'; + $this->mapOptions['layout'] = 'circular'; + break; + + case LAYOUT_FLAT: + $this->filter = 'dot'; + $this->mapOptions['layout'] = 'flat'; + break; + + case LAYOUT_RADIAL: + $this->filter = 'twopi'; + $this->mapOptions['layout'] = 'radial'; + break; + + case LAYOUT_SPRING1: + default: + $this->filter = 'neato'; + $this->mapOptions['layout'] = 'spring1'; + break; + + case LAYOUT_SPRING2: + $this->filter = 'fdp'; + $this->mapOptions['layout'] = 'spring2'; + break; + } + + return true; + } + + /** * Loads a map from a target map ID. * @@ -326,6 +443,9 @@ class NetworkMap $this->mapOptions[$k] = $v; } + // Load filter. + $this->loadFilter(); + // Retrieve data origin. $this->network = null; $this->idTask = null; @@ -373,14 +493,14 @@ class NetworkMap * @param integer $id_node Target node. * @param string $field Field to retrieve, if null, all are return. * - * @return mixed Array (node data) or false if error. + * @return mixed Array (node data) or null if error. */ public function getNodeData(int $id_node, $field=null) { if (is_array($this->nodes) === false || is_array($this->nodeMapping) === false ) { - return false; + return null; } if (is_array($this->nodes[$this->nodeMapping[$id_node]]) === true) { @@ -390,7 +510,7 @@ class NetworkMap return $this->nodes[$this->nodeMapping[$id_node]][$field]; } } else { - return false; + return null; } } @@ -449,102 +569,193 @@ class NetworkMap /** * Search for nodes in current map definition. * - * @return array Nodes detected, internal variable also updated. + * @return array Nodes detected, internal variable NOT updated. */ public function calculateNodes() { - // Calculate. - if (!$this->nodes) { - // Search. - if ($this->idTask) { - // Network map, based on discovery task. - $this->nodes = enterprise_hook( - 'get_discovery_agents', - [$this->idTask] - ); - } + global $config; - if ($this->network) { - // Network map, based on direct network. - $this->nodes = networkmap_get_new_nodes_from_ip_mask( - $this->network - ); - } else if ($this->mapOptions['map_filter']['empty_map']) { - // Empty map returns no data. - $this->nodes = []; - } else { - // Group map. - $this->nodes = agents_get_agents( - $filter, - [ - 'id_grupo', - 'nombre', - 'id_os', - 'id_parent', - 'id_agente', - 'normal_count', - 'warning_count', - 'critical_count', - 'unknown_count', - 'total_count', - 'notinit_count', - ], - 'AR', - [ - 'field' => 'id_parent', - 'order' => 'ASC', - ] - ); - } + // Calculate. + // Search. + if (enterprise_installed() && $this->idTask) { + // Network map, based on discovery task. + return enterprise_hook( + 'get_discovery_agents', + [$this->idTask] + ); } - return $this->nodes; + if ($this->network) { + // Network map, based on direct network. + $nodes = networkmap_get_new_nodes_from_ip_mask( + $this->network + ); + } else if ($this->mapOptions['map_filter']['empty_map']) { + // Empty map returns no data. + $nodes = []; + } else { + // Group map. + $nodes = agents_get_agents( + $filter, + [ + 'id_grupo', + 'nombre', + 'id_os', + 'id_parent', + 'id_agente', + 'normal_count', + 'warning_count', + 'critical_count', + 'unknown_count', + 'total_count', + 'notinit_count', + ], + 'AR', + [ + 'field' => 'id_parent', + 'order' => 'ASC', + ] + ); + } + + return $nodes; } /** * Search for relations for a given node in current map definition. + * Use id_parent in custom node definition to create an edge between + * two nodes. * - * @param array $node Origin. - * @param array $id_node Id for source node. - * @param array $id_source Id for source data, agent or module. + * Representation is to => from because from could be equal in multiple + * edges but no to (1 origin, multiple targets). + * + * @param array $id_source Id for source data, agent, module or custom. * * @return array Relations found for given node. */ public function calculateRelations( - $node, - $id_node, $id_source ) { // Calculate. + $node = $this->nodes[$id_source]; if (is_array($node) === false) { return false; } $relations = []; + switch ($node['node_type']) { + case NODE_AGENT: + // Search for agent parent and module relationships. + $module_relations = modules_get_relations( + [ + 'id_agent' => $node['id_agente'], + ] + ); + if ($module_relations !== false) { + foreach ($module_relations as $rel) { + $from = NODE_MODULE.'_'.$rel['module_a']; + $from_id = $this->nodes[$from]['id_node']; - if (isset($node['id_agente_modulo'])) { - // Module. - $relations = modules_get_relations( - [ - 'id_module' => $node['id_agente_modulo'], - ] - ); - } + $to = NODE_MODULE.'_'.$rel['module_b']; + $to_id = $this->nodes[$to]['id_node']; - if (isset($node['id_agente'])) { - // Agent. - $relations = modules_get_relations( - [ - 'id_agent' => $node['id_agente'], - ] - ); - if ($relations === false) { - $relations = []; - } + if ($from_id && $to_id) { + // Both module nodes exist. + $relations[$from_id] = $to_id; + continue; + } else if ($from_id) { + // Only source module node exists. + $to = NODE_AGENT.'_'.modules_get_agentmodule_agent( + $rel['module_b'] + ); + $to_id = $this->nodes[$to]['id_node']; + } else if ($to_id) { + // Only target module node exists. + $from_id = $node['id_node']; + } else { + // Module nodes does not exist. + // Simulate node to node relationship. + $to = NODE_AGENT.'_'.modules_get_agentmodule_agent( + $rel['module_b'] + ); + $to_id = $this->nodes[$to]['id_node']; + } - // Add also parent relationship. - $relations[$id_node] = $node['id_parent']; + $relations[$to_id] = $from_id; + } + } + + // Add also parent relationship. + $parent_id = NODE_AGENT.'_'.$node['id_parent']; + $parent_node = $this->nodes[$parent_id]['id_node']; + + // Store relationship. + if ($parent_node) { + $relations[$parent_node] = $node['id_node']; + } + break; + + case NODE_MODULE: + // Search for module relationships. + // Module. + $module_relations = modules_get_relations( + [ + 'id_module' => $node['id_agente_modulo'], + ] + ); + if ($module_relations !== false) { + foreach ($module_relations as $rel) { + $from = NODE_MODULE.'_'.$rel['module_a']; + $from_id = $this->nodes[$from]['id_node']; + + $to = NODE_MODULE.'_'.$rel['module_b']; + $to_id = $this->nodes[$to]['id_node']; + + if ($from_id && $to_id) { + // Both module nodes exist. + $relations[$from_id] = $to_id; + continue; + } else if ($from_id) { + // Only source module node exists. + $to = NODE_AGENT.'_'.modules_get_agentmodule_agent( + $rel['module_b'] + ); + $to_id = $this->nodes[$to]['id_node']; + } else if ($to_id) { + // Only target module node exists. + // Should not ocurr. + $from_id = $node['id_node']; + } else { + // Module nodes does not exist. + // Simulate node to node relationship. + $to = NODE_AGENT.'_'.modules_get_agentmodule_agent( + $rel['module_b'] + ); + $to_id = $this->nodes[$to]['id_node']; + } + + $relations[$to_id] = $from_id; + } + } + break; + + case NODE_GENERIC: + // Handmade ones. + // Add also parent relationship. + $parent_id = $node['id_parent']; + $parent_node = $this->nodes[$parent_id]['id_node']; + + // Store relationship. + if ($parent_node) { + $relations[$parent_node] = $node['id_node']; + } + break; + + case NODE_PANDORA: + default: + // Ignore. + break; } // Others. @@ -602,16 +813,18 @@ class NetworkMap $nodes_and_relations['relations'] = []; $index_relations = 0; - foreach ($relations as $relation) { - $nodes_and_relations['relations'][$index_relations]['id_map'] = $relation['id_map']; - $nodes_and_relations['relations'][$index_relations]['id_parent'] = $relation['id_parent']; - $nodes_and_relations['relations'][$index_relations]['id_child'] = $relation['id_child']; - $nodes_and_relations['relations'][$index_relations]['parent_type'] = $relation['parent_type']; - $nodes_and_relations['relations'][$index_relations]['child_type'] = $relation['child_type']; - $nodes_and_relations['relations'][$index_relations]['id_parent_source_data'] = $relation['id_parent_source_data']; - $nodes_and_relations['relations'][$index_relations]['id_child_source_data'] = $relation['id_child_source_data']; + if (is_array($relations)) { + foreach ($relations as $relation) { + $nodes_and_relations['relations'][$index_relations]['id_map'] = $relation['id_map']; + $nodes_and_relations['relations'][$index_relations]['id_parent'] = $relation['id_parent']; + $nodes_and_relations['relations'][$index_relations]['id_child'] = $relation['id_child']; + $nodes_and_relations['relations'][$index_relations]['parent_type'] = $relation['parent_type']; + $nodes_and_relations['relations'][$index_relations]['child_type'] = $relation['child_type']; + $nodes_and_relations['relations'][$index_relations]['id_parent_source_data'] = $relation['id_parent_source_data']; + $nodes_and_relations['relations'][$index_relations]['id_child_source_data'] = $relation['id_child_source_data']; - $index_relations++; + $index_relations++; + } } $this->graph = $nodes_and_relations; @@ -654,7 +867,7 @@ class NetworkMap $size .= ','.($this->mapOptions['size_canvas']['y'] / 100); } - // Graphviz custom values + // Graphviz custom values. if (isset($map_filter['node_sep'])) { $node_sep = $map_filter['node_sep']; } else { @@ -743,7 +956,10 @@ class NetworkMap public function createDotNode($data) { global $config; - global $hack_networkmap_mobile; + + if (is_array($data) === false) { + return ''; + } $dot_str = ''; @@ -769,16 +985,260 @@ class NetworkMap $dot_str = $data['id_node'].' [ parent="'.$data['id_parent'].'"'; $dot_str .= ', color="'.$color.'", fontsize='.$font_size; $dot_str .= ', shape="doublecircle"'.$url_node_link; - $dot_str .= ', style="filled", fixedsize=true, width='.$radius.', height='.$radius; - $dot_str .= ', label="'.$label.'", tooltip="'.$ajax_prefix; - $dot_str .= 'ajax.php?page=operation/agentes/ver_agente'; - $dot_str .= '&get_agent_status_tooltip=1&id_agent='.$data['id_agente']; - $dot_str .= $meta_params.'"];'."\n"; + $dot_str .= ', style="filled", fixedsize=true, width='.$radius; + $dot_str .= ', height='.$radius.', label="'.$label.'"]'."\n"; return $dot_str; } + /** + * Returns target color to be used based on the status received. + * + * @param integer $status Source information. + * + * @return string HTML tag for color. + */ + public static function getColorByStatus($status) + { + if (isset($status) === false) { + return COL_IGNORED; + } + + switch ($status) { + case AGENT_MODULE_STATUS_NORMAL: + case AGENT_STATUS_NORMAL: + return COL_NORMAL; + + case AGENT_MODULE_STATUS_NOT_INIT: + case AGENT_STATUS_NOT_INIT: + return COL_NOTINIT; + + case AGENT_MODULE_STATUS_CRITICAL_BAD: + case AGENT_STATUS_CRITICAL: + return COL_CRITICAL; + + case AGENT_MODULE_STATUS_WARNING: + case AGENT_STATUS_WARNING: + return COL_WARNING; + + case AGENT_MODULE_STATUS_CRITICAL_ALERT: + case AGENT_MODULE_STATUS_WARNING_ALERT: + case AGENT_STATUS_ALERT_FIRED: + return COL_ALERTFIRED; + + case AGENT_MODULE_STATUS_UNKNOWN: + case AGENT_STATUS_UNKNOWN: + return COL_UNKNOWN; + + default: + // Ignored. + break; + } + + return COL_IGNORED; + + } + + + /** + * Translates a standard node into a JS node with following attributes: + * + * @param array $node Input array (standard node structure). + * id_map. + * id_in_db. + * type. + * source_data. + * x. + * y. + * z. + * state. + * deleted. + * style. + * shape. + * image. + * label. + * id_agent. + * id_networkmap. + * + * @return array Object ready to be dump to JS. + * * Output array (translated): + * id. + * id_db. + * type. + * id_agent. + * id_module. + * fixed. + * x. + * y. + * px. + * py. + * z. + * state. + * deleted. + * image_url. + * image_width. + * image_height. + * raw_text. + * text. + * shape. + * color. + * map_id. + * networkmap_id. + */ + public function nodeToJS($node) + { + global $config; + + $item = []; + $item['id'] = $node['id']; + + // Id titem. + if (enterprise_installed() + && $this->map['__simulated'] === false + ) { + $item['id_db'] = $node['id_db']; + } + + $source_data = $this->getNodeData($node['id']); + + if (enterprise_installed() && $simulated === false) { + enterprise_include_once('include/functions_networkmap.php'); + $item['id_db'] = $node['id_in_db']; + } else { + $item['id_db'] = (int) $node['id']; + } + + $item['type'] = $node['type']; + $item['fixed'] = true; + $item['x'] = (int) $node['x']; + $item['y'] = (int) $node['y']; + $item['z'] = (int) $node['z']; + + // X,Y aliases for D3. + $item['px'] = $item['x']; + $item['py'] = $item['y']; + + // Status represents the status of the node (critical, warning...). + // State represents state of node in map (in holding_area or not). + $item['state'] = $node['state']; + $item['deleted'] = $node['deleted']; + + // Node color. + $item['color'] = $this->getColorByStatus($source_data['status']); + switch ($node['type']) { + case NODE_AGENT: + $item['id_agent'] = $node['source_data']; + break; + + case NODE_MODULE: + $item['id_module'] = $node['source_data']; + break; + + case NODE_PANDORA: + $item['color'] = COL_IGNORED; + $node['style']['image'] = ui_get_logo_to_center_networkmap(); + break; + + case NODE_GENERIC: + default: + $item['color'] = $node['color']; + break; + } + + // Calculate values. + // 40 => DEFAULT NODE RADIUS. + // 30 => alignment factor. + $holding_area_max_y = ($this->mapOptions['height'] + 30 + $this->mapOptions['map_filter']['node_radius'] * 2 - $this->mapOptions['map_filter']['holding_area'][1] + 10 * $this->mapOptions['map_filter']['node_radius']); + + // Update position if node must be stored in holding_area. + if ($item['state'] == 'holding_area') { + $holding_area_x = ($this->mapOptions['width'] + 30 + $this->mapOptions['map_filter']['node_radius'] * 2 - $this->mapOptions['map_filter']['holding_area'][0] + ($count_item_holding_area % 11) * $this->mapOptions['map_filter']['node_radius']); + $holding_area_y = ($this->mapOptions['height'] + 30 + $this->mapOptions['map_filter']['node_radius'] * 2 - $this->mapOptions['map_filter']['holding_area'][1] + (int) (($count_item_holding_area / 11)) * $this->mapOptions['map_filter']['node_radius']); + + // Keep holding area nodes in holding area. + if ($holding_area_max_y <= $holding_area_y) { + $holding_area_y = $holding_area_max_y; + } + + $item['x'] = $holding_area_x; + $item['y'] = $holding_area_y; + + // Increment for the next node in holding area. + $count_item_holding_area++; + } + + // Node image. + $item['image_url'] = ''; + $item['image_width'] = 0; + $item['image_height'] = 0; + if (empty($node['style']['image']) === false) { + $item['image_url'] = ui_get_full_url( + $node['style']['image'] + ); + $image_size = getimagesize( + $config['homedir'].'/'.$node['style']['image'] + ); + $item['image_width'] = (int) $image_size[0]; + $item['image_height'] = (int) $image_size[1]; + } + + $item['raw_text'] = $node['style']['label']; + $item['text'] = io_safe_output($node['style']['label']); + $item['shape'] = $node['style']['shape']; + $item['map_id'] = $node['id_map']; + if (!isset($node['style']['id_networkmap']) + || $node['style']['id_networkmap'] == '' + || $node['style']['id_networkmap'] == 0 + ) { + $item['networkmap_id'] = 0; + } else { + $item['networkmap_id'] = $node['style']['id_networkmap']; + } + + return $item; + } + + + /** + * Transforms an edge relationship into a JS array to be dumped. + * + * @param array $edge Edge information. + * + * * Input structure: + * parent_type. + * child_type. + * id_parent_source_data. + * id_child_source_data. + * + * @return array Edge translated to JS object. + * + * * Output structure: + * arrow_start. + * arrow_end. + * status_start. + * status_end. + * id_module_start. + * id_agent_start. + * id_module_end. + * id_agent_end. + * link_color. + * target. + * source. + * deleted. + * target_id_db. + * source_id_db. + * text_start. + * text_end. + */ + public function edgeToJS($edge) + { + // TODO: migrate networkmap_links_to_js_links here. + hd($edge); + return []; + } + + /** * Creates an edge in dot format. * Requirements: @@ -822,100 +1282,306 @@ class NetworkMap /** * Generate a graphviz string structure to be used later. * + * Usage: + * To create a new handmade graph: + * Define node struture + * key => node source data (agent/module row or custom) + * + * Minimum required fields in array: + * label + * status + * id + * + * @param array $nodes Generate dotgraph using defined nodes. + * * @return void */ - public function generateDotGraph() + public function generateDotGraph($nodes=false) { echo 'regenerar la net es: '.$this->network.'
'; if (!isset($this->dotGraph)) { // Generate dot file. - $nodes = []; + $this->nodes = []; $edges = []; $graph = ''; - // Search for nodes. - $nodes = $this->calculateNodes(); + if ($nodes === false) { + // Search for nodes. + $nodes = $this->calculateNodes(); + } // Search for relations. // Build dot structure. // Open Graph. $graph = $this->openDotFile(); + // Create empty pandora node to link orphans. + $this->nodes[0] = [ + 'label' => get_product_name(), + 'id_node' => 0, + 'id_agente' => 0, + 'id_agente_modulo' => 0, + 'node_type' => NODE_PANDORA, + ]; + + $this->nodeMapping[0] = 0; + + $graph .= $this->createDotNode( + $this->nodes[0] + ); + // Create dot nodes. - $i = 0; + $i = 1; + $orphans = []; foreach ($nodes as $k => $node) { - $this->nodeMapping[$i] = $k; + if (isset($node['id_agente']) === true + && $node['id_agente'] > 0 + ) { + // Origin is agent or module. + if (isset($node['id_agente_modulo']) === true + && $node['id_agente_modulo'] > 0 + ) { + $k = NODE_MODULE.'_'.$k; + // Origin is module. + $id_source = $node['id_agente_modulo']; + $label = io_safe_output($node['nombre']); + $status = modules_get_agentmodule_status($node); + $this->nodes[$k]['node_type'] = NODE_MODULE; + + $url = 'index.php?sec=estado&sec2=operation/agentes/ver_agente&id_agente='.$node['id_agente']; + $url_tooltip = 'ajax.php?page=operation/agentes/ver_agente&get_agentmodule_status_tooltip=1&id_module='.$node['id_agente_modulo']; + } else { + // Origin is agent. + $k = NODE_AGENT.'_'.$k; + $id_source = $node['id_agente']; + $label = io_safe_output($node['alias']); + $status = agents_get_status_from_counts($node); + $this->nodes[$k]['node_type'] = NODE_AGENT; + + $url = 'index.php?sec=estado&sec2=operation/agentes/ver_agente&id_agente='.$node['id_agente']; + $url_tooltip = 'ajax.php?page=operation/agentes/ver_agente&get_agent_status_tooltip=1&id_agent='.$node['id_agente']; + } + } else { + // Handmade node. + $k = NODE_GENERIC.'_'.$k; + $id_source = $node['id']; + $label = $node['label']; + $status = $node['status']; + $this->nodes[$k]['node_type'] = NODE_GENERIC; + // In handmade nodes, edges are defined by using id_parent + // Referencing target parent 'id'. + $this->nodes[$k]['id_parent'] = $node['id_parent']; + } + + $this->nodes[$k]['url'] = $url; + $this->nodes[$k]['url_tooltip'] = $url_tooltip; + + // Fullfill data. + // If url is defined in node will be overwritten. + foreach ($node as $key => $value) { + $this->nodes[$k][$key] = $value; + } + $graph .= $this->createDotNode( [ 'id_node' => $i, - 'id_source' => $node['id_agente'], - 'status' => agents_get_status_from_counts($node), - 'label' => io_safe_output($node['alias']), + 'id_source' => $id_source, + 'status' => $status, + 'label' => $label, 'image' => null, - 'url' => 'none', ] ); // Keep reverse reference. + $this->nodeMapping[$i] = $k; $this->nodes[$k]['id_node'] = $i; + $this->nodes[$k]['status'] = $status; - $edges[] = $this->calculateRelations($node, $i, $k); + $edges[$i] = $this->calculateRelations($k); + + // Adopt orphans. + if (empty($edges[$i])) { + $orphans[$i] = 0; + } // Increase for next node. $i++; } - hd($edges); foreach ($edges as $rel) { - foreach ($rel as $from => $to) { + foreach ($rel as $to => $from) { $graph .= $this->createDotEdge( [ 'from' => $from, - 'to' => $this->nodes[$to]['id_node'], + 'to' => $to, ] ); + // Remove parents from orphans. + unset($orphans[$from]); } } - // Create dot edges. + // Add missed edges. + foreach ($orphans as $to => $from) { + $graph .= $this->createDotEdge( + [ + 'from' => $from, + 'to' => $to, + ] + ); + } + + // Store relationships. + $this->relations = $edges; + + // Close dot file. $graph .= $this->closeDotFile(); - hd($graph); $this->dotGraph = $graph; - - /* - $this->dotGraph = networkmap_generate_dot( - get_product_name(), - $this->idGroup, - $this->mapOptions['simple'], - $this->mapOptions['font_size'], - $this->mapOptions['layout'], - $this->mapOptions['nooverlap'], - $this->mapOptions['z_dash'], - $this->mapOptions['ranksep'], - $this->mapOptions['center'], - $this->mapOptions['regen'], - $this->mapOptions['pure'], - $this->mapOptions['id'], - $this->mapOptions['show_snmp_modules'], - $this->mapOptions['cut_names'], - $this->mapOptions['relative'], - $this->mapOptions['text_filter'], - $this->network, - $this->mapOptions['dont_show_subgroups'], - // Strict user (strict_user). - false, - // Canvas size (size_canvas). - null, - $this->mapOptions['old_mode'], - $this->mapOptions['map_filter'] - ); - */ } } + /** + * Extracts node coordinates and relationships built by graphviz. + * + * @param string $graphviz_file Graphviz output file path. + * + * @return mixed Nodes and relations if success. False if not. + */ + private function parseGraphvizMapFile($graphviz_file) + { + if (isset($graphviz_file) === false + || is_file($graphviz_file) === false + ) { + return false; + } + + $content = file($graphviz_file); + + $nodes = []; + $relations = []; + foreach ($content as $key => $line) { + // Reduce blank spaces. + $line = preg_replace('/\ +/', ' ', $line); + + if (preg_match('/^graph.*$/', $line) != 0) { + // Graph definition. + $fields = explode(' ', $line); + + $this->map['width'] = ($fields[2] * 100); + $this->map['height'] = ($fields[3] * 100); + } else if (preg_match('/^node.*$/', $line) != 0) { + // Node. + $fields = explode(' ', $line); + $id = $fields[1]; + $nodes[$id]['x'] = (($fields[2] * $this->mapOptions['map_filter']['node_radius']) - $this->mapOptions['map_filter']['rank_sep'] * GRAPHVIZ_RADIUS_CONVERSION_FACTOR); + $nodes[$id]['y'] = (($fields[3] * $this->mapOptions['map_filter']['node_radius']) - $this->mapOptions['map_filter']['rank_sep'] * GRAPHVIZ_RADIUS_CONVERSION_FACTOR); + } else if (preg_match('/^edge.*$/', $line) != 0) { + // Edge. + // This is really not needed, because is already defined + // in $this->relations. Only for debug purposes. + $fields = explode(' ', $line); + + if (strpos($fields[1], 'transp_') !== false + || strpos($fields[2], 'transp_') !== false + ) { + // Skip transparent nodes relationships. + continue; + } + + $relations[] = [ + 'from' => $fields[2], + 'to' => $fields[1], + ]; + } + } + + return [ + 'nodes' => $nodes, + 'relations' => $relations, + ]; + + } + + + /** + * Calculates X,Y positions foreach element defined in dotGraph. + * + * @return array Structure parsed. + */ + public function calculateCoords() + { + switch (PHP_OS) { + case 'WIN32': + case 'WINNT': + case 'Windows': + $filename_dot = sys_get_temp_dir()."\\networkmap_".$filter; + break; + + default: + $filename_dot = sys_get_temp_dir().'/networkmap_'.$filter; + break; + } + + if ($this->mapOptions['simple']) { + $filename_dot .= '_simple'; + } + + if ($this->mapOptions['nooverlap']) { + $filename_dot .= '_nooverlap'; + } + + $filename_dot .= uniqid().'_'.$this->idMap.'.dot'; + + file_put_contents($filename_dot, $this->dotGraph); + + $plain_file = 'plain'.uniqid().'.txt'; + switch (PHP_OS) { + case 'WIN32': + case 'WINNT': + case 'Windows': + $filename_plain = sys_get_temp_dir().'\\'.$plain_file; + + $cmd = io_safe_output( + $config['graphviz_bin_dir'].'\\'.$this->filter.'.exe -Tplain -o '.$filename_plain.' '.$filename_dot + ); + break; + + default: + $filename_plain = sys_get_temp_dir().'/'.$plain_file; + + $cmd = $this->filter.' -Tplain -o '.$filename_plain.' '.$filename_dot; + break; + } + + $retval = 0; + $r = system($cmd, $retval); + + if ($retval != 0) { + ui_print_error_message( + __('Failed to generate dotmap, please select different layout schema') + ); + return []; + } + + unlink($filename_dot); + + $graph = $this->parseGraphvizMapFile( + $filename_plain + ); + + unlink($filename_plain); + + /* + * Graphviz section ends here. + */ + + return $graph; + } + + /** * Creates an empty dot graph (with only base node) * @@ -924,26 +1590,58 @@ class NetworkMap public function generateEmptyDotGraph() { // Create an empty map dot structure. - $graph = networkmap_open_graph( - $this->mapOptions['layout'], - $this->mapOptions['nooverlap'], - $this->mapOptions['pure'], - $this->mapOptions['z_dash'], - $this->mapOptions['ranksep'], - $this->mapOptions['font_size'], - null + $graph = $this->openDotFile(); + + $this->nodes[0] = [ + 'label' => get_product_name(), + 'id_node' => 0, + 'id_agente' => 0, + 'id_agente_modulo' => 0, + 'node_type' => NODE_PANDORA, + ]; + + $this->nodeMapping[0] = 0; + + $graph .= $this->createDotNode( + $this->nodes[0] ); - $graph .= networkmap_create_pandora_node( - get_product_name(), - $this->mapOptions['font_size'], - $this->mapOptions['simple'] - ); - $graph .= networkmap_close_graph(); + + $graph .= $this->closeDotFile(); $this->dotGraph = $graph; } + /** + * Returns the most representative ID based on the tipe of node received. + * + * @param array $node Source data. + * + * @return integer Source id. + */ + private function auxGetIdByType($node) + { + if (!is_array($node)) { + return 0; + } + + switch ($to_source['node_type']) { + case NODE_MODULE: + return $node['id_agente_modulo']; + + case NODE_AGENT: + return $node['id_agente']; + + case NODE_GENERIC: + return $node['id_node']; + + case NODE_PANDORA: + default: + return 0; + } + } + + /** * Generates a nodes - relationships array using graphviz dot * schema and stores nodes&relations into $this->graph. @@ -962,108 +1660,28 @@ class NetworkMap * Let graphviz place the nodes. */ - switch ($this->mapOptions['generation_method']) { - case LAYOUT_CIRCULAR: - $filter = 'circo'; - $this->mapOptions['layout'] = 'circular'; - break; - - case LAYOUT_FLAT: - $filter = 'dot'; - $this->mapOptions['layout'] = 'flat'; - break; - - case LAYOUT_RADIAL: - $filter = 'twopi'; - $this->mapOptions['layout'] = 'radial'; - break; - - case LAYOUT_SPRING1: - default: - $filter = 'neato'; - $this->mapOptions['layout'] = 'spring1'; - break; - - case LAYOUT_SPRING2: - $filter = 'fdp'; - $this->mapOptions['layout'] = 'spring2'; - break; - } - if ($map_filter['empty_map']) { $this->generateEmptyDotGraph(); } else if (!isset($this->dotGraph)) { $this->generateDotGraph(); } - switch (PHP_OS) { - case 'WIN32': - case 'WINNT': - case 'Windows': - $filename_dot = sys_get_temp_dir()."\\networkmap_".$filter; - break; + /* + * Calculate X,Y positions. + */ - default: - $filename_dot = sys_get_temp_dir().'/networkmap_'.$filter; - break; - } + $graph = $this->calculateCoords(); - if ($simple) { - $filename_dot .= '_simple'; - } - - if ($nooverlap) { - $filename_dot .= '_nooverlap'; - } - - $filename_dot .= uniqid().'_'.$this->idMap.'.dot'; - - file_put_contents($filename_dot, $this->dotGraph); - - $plain_file = 'plain'.uniqid().'.txt'; - switch (PHP_OS) { - case 'WIN32': - case 'WINNT': - case 'Windows': - $filename_plain = sys_get_temp_dir().'\\'.$plain_file; - - $cmd = io_safe_output( - $config['graphviz_bin_dir'].'\\'.$filter.'.exe -Tplain -o '.$filename_plain.' '.$filename_dot - ); - break; - - default: - $filename_plain = sys_get_temp_dir().'/'.$plain_file; - - $cmd = $filter.' -Tplain -o '.$filename_plain.' '.$filename_dot; - break; - } - - $retval = 0; - $r = system($cmd, $retval); - - if ($retval != 0) { + if (is_array($graph) === true) { + $nodes = $graph['nodes']; + $relations = $graph['relations']; + } else { ui_print_error_message( - __('Failed to generate dotmap, please select different layout schema') + __('Failed to retrieve graph data.') ); return; } - unlink($filename_dot); - - $nodes = networkmap_loadfile( - $this->idMap, - $filename_plain, - $relation_nodes, - $this->dotGraph - ); - - unlink($filename_plain); - - /* - * Graphviz section ends here. - */ - /* * Calculate references. */ @@ -1084,36 +1702,42 @@ class NetworkMap } } - $nodes_and_relations['nodes'] = []; $index = 0; $node_center = []; - foreach ($nodes as $key => $node) { - $nodes_and_relations['nodes'][$index]['id'] = $node['id']; - $nodes_and_relations['nodes'][$index]['id_map'] = $this->idMap; - // Retrieve properties. - $node['id_agent'] = $this->getNodeData( - $node['id'], - 'id_agente' - ); - $node['id_module'] = $this->getNodeData( - $node['id'], - 'id_agente_modulo' - ); + $graph = []; + $graph['nodes'] = []; - // Specify node type and set values. - if (isset($node['id_agent']) === true) { - if (isset($node['id_module']) === false) { - // Agent. - $node['type'] = 'agent'; - $node['source_data'] = $node['id_agent']; - $node['text'] = $this->getNodeData( - $node['id'], - 'alias' - ); + // Prepare graph nodes. + foreach ($nodes as $id => $coords) { + $node_tmp['id_map'] = $this->idMap; + $node_tmp['id'] = $id; - $node['image'] = ui_print_os_icon( - $this->getNodeData($node['id'], 'id_os'), + $source = $this->getNodeData($id); + + $node_tmp['id_agent'] = $source['id_agente']; + $node_tmp['id_module'] = $source['id_module']; + $node_tmp['type'] = $source['node_type']; + $node_tmp['x'] = $coords['x']; + $node_tmp['y'] = $coords['y']; + + $node_tmp['width'] = $this->mapOptions['map_filter']['node_radius']; + $node_tmp['height'] = $this->mapOptions['map_filter']['node_radius']; + + if (isset($source['width'])) { + $node_tmp['width'] = $source['width']; + } + + if (isset($source['height'])) { + $node_tmp['height'] = $source['height']; + } + + switch ($node_tmp['type']) { + case NODE_AGENT: + $node_tmp['source_data'] = $source['id_agente']; + $node_tmp['text'] = $source['alias']; + $node_tmp['image'] = ui_print_os_icon( + $source['id_os'], false, true, true, @@ -1121,107 +1745,81 @@ class NetworkMap true, true ); - } else if (isset($node['id_module'])) { - // Module. - $node['type'] = 'module'; - $node['source_data'] = $node['id_module']; - $node['text'] = $this->getNodeData( - $node['id'], - 'nombre' - ); + break; - $node['image'] = ui_print_moduletype_icon( - $this->getNodeData($node['id'], 'id_tipo_modulo'), + case NODE_MODULE: + $node_tmp['source_data'] = $source['id_agente_modulo']; + $node_tmp['text'] = $source['nombre']; + $node_tmp['image'] = ui_print_moduletype_icon( + $this->getNodeData($id, 'id_tipo_modulo'), true, true, false, true ); - } - } + break; - $children_count = 0; - foreach ($relation_nodes as $relation) { - if (($relation['parent_type'] == 'agent') || ($relation['parent_type'] == '')) { - if ($nodes[$relation['id_parent']]['id_agent'] == $node['id_agent']) { - $children_count++; - } - } else if ($relation['parent_type'] == 'module') { - if ($nodes[$relation['id_parent']]['id_module'] == $node['id_module']) { - $children_count++; - } - } - } + case NODE_PANDORA: + $node_tmp['text'] = $source['label']; + $node_tmp['id_agent'] = $source['id_agente']; + $node_tmp['id_module'] = $source['id_agente_modulo']; + $node_tmp['source_data'] = 0; + break; - // Center map over node with most children. - if (empty($node_center) || $node_center['counter'] < $children_count) { - $node_center['x'] = (int) $node['coords'][0]; - $node_center['y'] = (int) $node['coords'][1]; - $node_center['counter'] = $children_count; - } - - $nodes_and_relations['nodes'][$index]['x'] = (int) $node['coords'][0]; - $nodes_and_relations['nodes'][$index]['y'] = (int) $node['coords'][1]; - - if (($node['type'] == 'agent') || ($node['type'] == '')) { - $nodes_and_relations['nodes'][$index]['source_data'] = $node['id_agent']; - $nodes_and_relations['nodes'][$index]['type'] = 0; - } else { - $nodes_and_relations['nodes'][$index]['source_data'] = $node['id_module']; - $nodes_and_relations['nodes'][$index]['id_agent'] = $node['id_agent']; - $nodes_and_relations['nodes'][$index]['type'] = 1; + case NODE_GENERIC: + default: + $node_tmp['text'] = $source['label']; + $node_tmp['id_agent'] = $source['id_agente']; + $node_tmp['id_module'] = $source['id_agente_modulo']; + break; } $style = []; - $style['shape'] = 'circle'; - $style['image'] = $node['image']; - $style['width'] = $node['width']; - $style['height'] = $node['height']; - $style['label'] = $node['text']; - $nodes_and_relations['nodes'][$index]['style'] = json_encode($style); + $style['shape'] = $source['shape']; + if (isset($style['shape']) === false) { + $style['shape'] = 'circle'; + } + $style['image'] = $node_tmp['image']; + $style['width'] = $node_tmp['width']; + $style['height'] = $node_tmp['height']; + $style['label'] = $node_tmp['text']; + + $node_tmp['style'] = json_encode($style); + + $graph['nodes'][$index] = $node_tmp; $index++; } - $nodes_and_relations['relations'] = []; - $index = 0; + // Prepare graph edges. + $graph['relations'] = []; - foreach ($relation_nodes as $relation) { - $nodes_and_relations['relations'][$index]['id_map'] = $this->idMap; + // Edges from and to references id_nodes. Retrieve source data + // before link them. + foreach ($relations as $rel) { + // Parent. + $from_source = $this->getNodeData($rel['from']); + // Child. + $to_source = $this->getNodeData($rel['to']); - if (($relation['parent_type'] == 'agent') || ($relation['parent_type'] == '')) { - $nodes_and_relations['relations'][$index]['id_parent'] = $relation['id_parent']; - $nodes_and_relations['relations'][$index]['id_parent_source_data'] = $nodes[$relation['id_parent']]['id_agent']; - $nodes_and_relations['relations'][$index]['parent_type'] = 0; - } else if ($relation['parent_type'] == 'module') { - $nodes_and_relations['relations'][$index]['id_parent'] = $relation['id_parent']; - $nodes_and_relations['relations'][$index]['id_parent_source_data'] = $nodes[$relation['id_parent']]['id_module']; - $nodes_and_relations['relations'][$index]['parent_type'] = 1; - } else { - $nodes_and_relations['relations'][$index]['id_parent'] = $relation['id_parent']; - $nodes_and_relations['relations'][$index]['id_child_source_data'] = -2; - $nodes_and_relations['relations'][$index]['parent_type'] = 3; - } + $edge = []; + $edge['id_map'] = $this->idMap; + $edge['id_parent'] = $rel['from']; + $edge['id_child'] = $rel['to']; + $edge['parent_type'] = $from_source['node_type']; + $edge['child_type'] = $to_source['node_type']; + $edge['id_child_source_data'] = $this->auxGetIdByType( + $to_source + ); + $edge['id_parent_source_data'] = $this->auxGetIdByType( + $from_source + ); - if (($relation['child_type'] == 'agent') || ($relation['child_type'] == '')) { - $nodes_and_relations['relations'][$index]['id_child'] = $relation['id_child']; - $nodes_and_relations['relations'][$index]['id_child_source_data'] = $nodes[$relation['id_child']]['id_agent']; - $nodes_and_relations['relations'][$index]['child_type'] = 0; - } else if ($relation['child_type'] == 'module') { - $nodes_and_relations['relations'][$index]['id_child'] = $relation['id_child']; - $nodes_and_relations['relations'][$index]['id_child_source_data'] = $nodes[$relation['id_child']]['id_module']; - $nodes_and_relations['relations'][$index]['child_type'] = 1; - } else { - $nodes_and_relations['relations'][$index]['id_child'] = $relation['id_child']; - $nodes_and_relations['relations'][$index]['id_child_source_data'] = -2; - $nodes_and_relations['relations'][$index]['child_type'] = 3; - } - - $index++; + $graph['relations'][] = $edge; } if ($this->idMap > 0 && (!isset($this->map['__simulated']))) { - if (enterprise_installed()) { + if (!enterprise_installed()) { $nodes_and_relations = enterprise_hook( 'save_generate_nodes', [ @@ -1251,7 +1849,7 @@ class NetworkMap $this->map['center_y'] = $node_center['y']; } - $this->graph = $nodes_and_relations; + $this->graph = $graph; } @@ -1355,12 +1953,16 @@ class NetworkMap $node['type'] = ''; } - $item = networkmap_db_node_to_js_node( - $node, - $count, - $count_item_holding_area, - $simulate + $item = $this->nodeToJS( + $node ); + + /* + * $count, + * $count_item_holding_area, + * $simulate + */ + if ($item['deleted']) { continue; } diff --git a/pandora_console/include/functions_networkmap.php b/pandora_console/include/functions_networkmap.php index 4f02ef3fdb..39d4ee95ab 100644 --- a/pandora_console/include/functions_networkmap.php +++ b/pandora_console/include/functions_networkmap.php @@ -1329,10 +1329,18 @@ function networkmap_get_new_nodes_from_ip_mask( ) { $list_ip_masks = explode(',', $ip_mask); + if (empty($list_ip_masks)) { + return []; + } + $agents = []; foreach ($list_ip_masks as $subnet) { $net = explode('/', $subnet); + // Calculate real network address. Avoid user bad input. + $mask = ~((1 << (32 - $net[1])) - 1); + $network = long2ip(ip2long($net[0]) & $mask); + $sql = sprintf( 'SELECT * FROM `tagente` @@ -1350,7 +1358,7 @@ function networkmap_get_new_nodes_from_ip_mask( ) t_res ON t_res.`id_agent` = `tagente`.`id_agente`', (32 - $net[1]), - $net[0] + $network ); $subnet_agents = db_get_all_rows_sql($sql); @@ -1554,6 +1562,7 @@ function networkmap_db_node_to_js_node( $item['raw_text'] = $node['style']['label']; $item['text'] = io_safe_output($node['style']['label']); $item['shape'] = $node['style']['shape']; + switch ($node['type']) { case 0: $color = get_status_color_networkmap($node['source_data']); @@ -1690,7 +1699,9 @@ function networkmap_links_to_js_links( $count = 0; foreach ($relations as $key => $relation) { - if (($relation['parent_type'] == 1) && ($relation['child_type'] == 1)) { + if (($relation['parent_type'] == NODE_MODULE) + && ($relation['child_type'] == NODE_MODULE) + ) { $id_target_agent = agents_get_agent_id_by_module_id( $relation['id_parent_source_data'] ); @@ -1699,16 +1710,16 @@ function networkmap_links_to_js_links( ); $id_target_module = $relation['id_parent_source_data']; $id_source_module = $relation['id_child_source_data']; - } else if (($relation['parent_type'] == 1) - && ($relation['child_type'] == 0) + } else if (($relation['parent_type'] == NODE_MODULE) + && ($relation['child_type'] == NODE_AGENT) ) { $id_target_agent = agents_get_agent_id_by_module_id( $relation['id_parent_source_data'] ); $id_target_module = $relation['id_parent_source_data']; $id_source_agent = $relation['id_child_source_data']; - } else if (($relation['parent_type'] == 0) - && ($relation['child_type'] == 1) + } else if (($relation['parent_type'] == NODE_AGENT) + && ($relation['child_type'] == NODE_MODULE) ) { $id_target_agent = $relation['id_parent_source_data']; $id_source_module = $relation['id_child_source_data']; @@ -1748,10 +1759,10 @@ function networkmap_links_to_js_links( $item['target_id_db'] = (int) $target_and_source['target']; $item['source_id_db'] = (int) $target_and_source['source']; } else { - if (($relation['parent_type'] == 1) && ($relation['child_type'] == 1)) { + if (($relation['parent_type'] == NODE_MODULE) && ($relation['child_type'] == NODE_MODULE)) { $item['target_id_db'] = $id_target_agent; $item['source_id_db'] = $id_source_agent; - } else if (($relation['parent_type'] == 0) && ($relation['child_type'] == 0)) { + } else if (($relation['parent_type'] == NODE_AGENT) && ($relation['child_type'] == NODE_AGENT)) { $item['target_id_db'] = (int) $relation['id_parent_source_data']; $item['source_id_db'] = $id_source_agent; } else { @@ -1776,7 +1787,7 @@ function networkmap_links_to_js_links( } } - if ($relation['child_type'] == 1) { + if ($relation['child_type'] == NODE_MODULE) { $item['arrow_start'] = 'module'; $item['status_start'] = modules_get_agentmodule_status((int) $id_source_module, false, false, null); $item['id_module_start'] = (int) $id_source_module; @@ -1794,7 +1805,7 @@ function networkmap_links_to_js_links( $control1 = false; $control2 = false; - if (($relation['parent_type'] == 1) && ($relation['child_type'] == 1)) { + if (($relation['parent_type'] == NODE_MODULE) && ($relation['child_type'] == NODE_MODULE)) { if (($item['status_start'] == AGENT_MODULE_STATUS_CRITICAL_BAD) || ($item['status_end'] == AGENT_MODULE_STATUS_CRITICAL_BAD)) { $item['link_color'] = '#FC4444'; } else if (($item['status_start'] == AGENT_MODULE_STATUS_WARNING) || ($item['status_end'] == AGENT_MODULE_STATUS_WARNING)) { @@ -1824,7 +1835,7 @@ function networkmap_links_to_js_links( } } } - } else if ($relation['child_type'] == 1) { + } else if ($relation['child_type'] == NODE_MODULE) { if ($item['status_start'] == AGENT_MODULE_STATUS_CRITICAL_BAD) { $item['link_color'] = '#FC4444'; } else if ($item['status_start'] == AGENT_MODULE_STATUS_WARNING) { @@ -1851,7 +1862,7 @@ function networkmap_links_to_js_links( } } } - } else if ($relation['parent_type'] == 1) { + } else if ($relation['parent_type'] == NODE_MODULE) { if ($item['status_end'] == AGENT_MODULE_STATUS_CRITICAL_BAD) { $item['link_color'] = '#FC4444'; } else if ($item['status_end'] == AGENT_MODULE_STATUS_WARNING) { @@ -1878,8 +1889,8 @@ function networkmap_links_to_js_links( } } } - } else if (($relation['parent_type'] == 3) - && ($relation['child_type'] == 3) + } else if (($relation['parent_type'] == NODE_GENERIC) + && ($relation['child_type'] == NODE_GENERIC) ) { foreach ($nodes_graph as $key2 => $node) { if ($relation['id_parent'] == $node['id_db']) { @@ -1892,10 +1903,10 @@ function networkmap_links_to_js_links( $agent2 = $node['id_db']; } } - } else if (($relation['parent_type'] == 3) - || ($relation['child_type'] == 3) + } else if (($relation['parent_type'] == NODE_GENERIC) + || ($relation['child_type'] == NODE_GENERIC) ) { - if ($relation['parent_type'] == 3) { + if ($relation['parent_type'] == NODE_GENERIC) { foreach ($nodes_graph as $key2 => $node) { if ($relation['id_parent'] == $node['id_db']) { $agent = $node['id_db']; @@ -1903,7 +1914,7 @@ function networkmap_links_to_js_links( $agent2 = $node['id_db']; } } - } else if ($relation['child_type'] == 3) { + } else if ($relation['child_type'] == NODE_GENERIC) { foreach ($nodes_graph as $key2 => $node) { if ($relation['id_child'] == $node['id_db']) { $agent2 = $node['id_db']; @@ -1933,8 +1944,8 @@ function networkmap_links_to_js_links( } if ((($item['target'] == -1) || ($item['source'] == -1)) - && $relation['parent_type'] == 1 - && $relation['child_type'] == 1 + && $relation['parent_type'] == NODE_MODULE + && $relation['child_type'] == NODE_MODULE ) { continue; } @@ -2191,10 +2202,11 @@ function networkmap_loadfile( if (preg_match('/^node.*$/', $line) != 0) { $items = explode(' ', $line); $node_id = $items[1]; + + // 200 for larger maps $node_x = ($items[2] * 100); - // 200 is for show more big $node_y = ($height_map - $items[3] * 100); - // 200 is for show more big + // 200 for larger maps $data['id'] = $node_id; $data['text'] = ''; $data['image'] = ''; @@ -2307,8 +2319,10 @@ function networkmap_loadfile( $relations_param[] = $row; } - hd($networkmap_nodes); - return $networkmap_nodes; + return [ + 'nodes' => $networkmap_nodes, + 'relations' => $relations_param, + ]; } @@ -2751,4 +2765,4 @@ function networkmap_load_cluetip() }); Date: Mon, 18 Mar 2019 18:11:54 +0100 Subject: [PATCH 3/7] wip relationships Former-commit-id: 7f582f8c0197f488438226120eaea4c4dcc1646e --- .../include/class/NetworkMap.class.php | 354 +++++++++++------- 1 file changed, 213 insertions(+), 141 deletions(-) diff --git a/pandora_console/include/class/NetworkMap.class.php b/pandora_console/include/class/NetworkMap.class.php index e97b56cb33..6282b9c980 100644 --- a/pandora_console/include/class/NetworkMap.class.php +++ b/pandora_console/include/class/NetworkMap.class.php @@ -35,6 +35,9 @@ enterprise_include_once('include/functions_discovery.php'); // Avoid node overlapping. define('GRAPHVIZ_RADIUS_CONVERSION_FACTOR', 20); +define('MAP_X_CORRECTION', 600); +define('MAP_Y_CORRECTION', 150); + /** * Manage networkmaps in Pandora FMS. @@ -597,19 +600,7 @@ class NetworkMap // Group map. $nodes = agents_get_agents( $filter, - [ - 'id_grupo', - 'nombre', - 'id_os', - 'id_parent', - 'id_agente', - 'normal_count', - 'warning_count', - 'critical_count', - 'unknown_count', - 'total_count', - 'notinit_count', - ], + ['*'], 'AR', [ 'field' => 'id_parent', @@ -644,45 +635,67 @@ class NetworkMap } $relations = []; + $i = 0; + $from_type = NODE_AGENT; + $to_type = NODE_AGENT; switch ($node['node_type']) { case NODE_AGENT: // Search for agent parent and module relationships. $module_relations = modules_get_relations( - [ - 'id_agent' => $node['id_agente'], - ] + ['id_agent' => $node['id_agente']] ); + if ($module_relations !== false) { - foreach ($module_relations as $rel) { - $from = NODE_MODULE.'_'.$rel['module_a']; - $from_id = $this->nodes[$from]['id_node']; + // Module relation exist. + foreach ($module_relations as $mod_rel) { + // Check if target referenced agent is defined in + // current map. + $agent_a = modules_get_agentmodule_agent( + $mod_rel['module_a'] + ); + $module_a = $mod_rel['module_a']; + $agent_b = modules_get_agentmodule_agent( + $mod_rel['module_b'] + ); + $module_b = $mod_rel['module_b']; - $to = NODE_MODULE.'_'.$rel['module_b']; - $to_id = $this->nodes[$to]['id_node']; + // Calculate target. + $module_to = $module_a; + $agent_to = $agent_a; + $module_from = $module_b; + $agent_from = $agent_b; - if ($from_id && $to_id) { - // Both module nodes exist. - $relations[$from_id] = $to_id; - continue; - } else if ($from_id) { - // Only source module node exists. - $to = NODE_AGENT.'_'.modules_get_agentmodule_agent( - $rel['module_b'] - ); - $to_id = $this->nodes[$to]['id_node']; - } else if ($to_id) { - // Only target module node exists. - $from_id = $node['id_node']; - } else { - // Module nodes does not exist. - // Simulate node to node relationship. - $to = NODE_AGENT.'_'.modules_get_agentmodule_agent( - $rel['module_b'] - ); - $to_id = $this->nodes[$to]['id_node']; + // Module relations does not have from and to, + // If current agent_a is current node, reverse relation. + if ($agent_a == $node['id_agente']) { + $module_to = $module_b; + $agent_to = $agent_b; + $module_from = $module_a; + $agent_from = $agent_a; } - $relations[$to_id] = $from_id; + $target_node = $this->nodes[NODE_AGENT.'_'.$agent_to]; + + if (isset($target_node) === false) { + // Agent is not present in this map. + continue; + } + + $rel = []; + // Node reference (child). + $rel['id_child'] = $node['id_node']; + $rel['child_type'] = NODE_MODULE; + $rel['id_child_source_data'] = $module_from; + $rel['id_child_agent'] = $agent_from; + + // Node reference (parent). + $rel['id_parent'] = $target_node['id_node']; + $rel['parent_type'] = NODE_MODULE; + $rel['id_parent_source_data'] = $module_to; + $rel['id_parent_agent'] = $agent_to; + + // Store relation. + $relations[] = $rel; } } @@ -692,50 +705,80 @@ class NetworkMap // Store relationship. if ($parent_node) { - $relations[$parent_node] = $node['id_node']; + $rel = []; + + // Node reference (parent). + $rel['id_parent'] = $parent_node; + $rel['parent_type'] = NODE_AGENT; + $rel['id_parent_source_data'] = $node['id_parent']; + + // Node reference (child). + $rel['id_child'] = $node['id_node']; + $rel['child_type'] = NODE_AGENT; + $rel['id_child_source_data'] = $node['id_agente']; + + // Store relation. + $relations[] = $rel; } break; case NODE_MODULE: // Search for module relationships. - // Module. $module_relations = modules_get_relations( - [ - 'id_module' => $node['id_agente_modulo'], - ] + ['id_module' => $node['id_agente_modulo']] ); + if ($module_relations !== false) { - foreach ($module_relations as $rel) { - $from = NODE_MODULE.'_'.$rel['module_a']; - $from_id = $this->nodes[$from]['id_node']; + // Module relation exist. + foreach ($module_relations as $mod_rel) { + // Check if target referenced agent is defined in + // current map. + $agent_a = modules_get_agentmodule_agent( + $mod_rel['module_a'] + ); + $module_a = $mod_rel['module_a']; + $agent_b = modules_get_agentmodule_agent( + $mod_rel['module_b'] + ); + $module_b = $mod_rel['module_b']; - $to = NODE_MODULE.'_'.$rel['module_b']; - $to_id = $this->nodes[$to]['id_node']; + // Calculate target. + $module_to = $module_a; + $agent_to = $agent_a; + $module_from = $module_b; + $agent_from = $agent_b; - if ($from_id && $to_id) { - // Both module nodes exist. - $relations[$from_id] = $to_id; - continue; - } else if ($from_id) { - // Only source module node exists. - $to = NODE_AGENT.'_'.modules_get_agentmodule_agent( - $rel['module_b'] - ); - $to_id = $this->nodes[$to]['id_node']; - } else if ($to_id) { - // Only target module node exists. - // Should not ocurr. - $from_id = $node['id_node']; - } else { - // Module nodes does not exist. - // Simulate node to node relationship. - $to = NODE_AGENT.'_'.modules_get_agentmodule_agent( - $rel['module_b'] - ); - $to_id = $this->nodes[$to]['id_node']; + // Module relations does not have from and to, + // If current agent_a is current node, reverse relation. + if ($agent_a == $node['id_agente']) { + $module_to = $module_b; + $agent_to = $agent_b; + $module_from = $module_a; + $agent_from = $agent_a; } - $relations[$to_id] = $from_id; + $target_node = $this->nodes[NODE_AGENT.'_'.$agent_to]; + + if (isset($target_node) === false) { + // Agent is not present in this map. + continue; + } + + $rel = []; + // Node reference (child). + $rel['id_child'] = $node['id_node']; + $rel['child_type'] = NODE_MODULE; + $rel['id_child_source_data'] = $module_from; + $rel['id_child_agent'] = $agent_from; + + // Node reference (parent). + $rel['id_parent'] = $target_node['id_node']; + $rel['parent_type'] = NODE_MODULE; + $rel['id_parent_source_data'] = $module_to; + $rel['id_parent_agent'] = $agent_to; + + // Store relation. + $relations[] = $rel; } } break; @@ -963,6 +1006,8 @@ class NetworkMap $dot_str = ''; + // Color is being printed by D3, not graphviz. + // Used only for positioning. $color = COL_NORMAL; $label = $data['label']; $url = 'none'; @@ -1363,6 +1408,7 @@ class NetworkMap } } else { // Handmade node. + // Store user node definitions. $k = NODE_GENERIC.'_'.$k; $id_source = $node['id']; $label = $node['label']; @@ -1371,6 +1417,12 @@ class NetworkMap // In handmade nodes, edges are defined by using id_parent // Referencing target parent 'id'. $this->nodes[$k]['id_parent'] = $node['id_parent']; + $this->nodes[$k]['width'] = $node['width']; + $this->nodes[$k]['height'] = $node['height']; + $this->nodes[$k]['id_source'] = $node['id_source']; + $this->nodes[$k]['shape'] = $node['shape']; + $url = $this->node['url']; + $url_tooltip = $this->node['url_tooltip']; } $this->nodes[$k]['url'] = $url; @@ -1386,7 +1438,6 @@ class NetworkMap [ 'id_node' => $i, 'id_source' => $id_source, - 'status' => $status, 'label' => $label, 'image' => null, ] @@ -1394,52 +1445,61 @@ class NetworkMap // Keep reverse reference. $this->nodeMapping[$i] = $k; + $this->nodes[$k]['id_source_data'] = $id_source; $this->nodes[$k]['id_node'] = $i; $this->nodes[$k]['status'] = $status; - $edges[$i] = $this->calculateRelations($k); - - // Adopt orphans. - if (empty($edges[$i])) { - $orphans[$i] = 0; - } - // Increase for next node. $i++; } - foreach ($edges as $rel) { - foreach ($rel as $to => $from) { - $graph .= $this->createDotEdge( - [ - 'from' => $from, - 'to' => $to, - ] - ); - // Remove parents from orphans. - unset($orphans[$from]); + // Search for relations. + foreach ($this->nodes as $k => $item) { + $target = $this->calculateRelations($k); + + // Adopt orphans. + if (empty($target)) { + $rel = []; + $rel['id_parent'] = 0; + $rel['id_child'] = $item['id_node']; + $rel['parent_type'] = NODE_PANDORA; + $rel['child_type'] = $item['node_type']; + $rel['child_source_data'] = $item['id_source_data']; + $orphans[] = $rel; + } else { + // Flattern edges. + foreach ($target as $rel) { + $edges[] = $rel; + } } } - // Add missed edges. - foreach ($orphans as $to => $from) { + foreach ($edges as $rel) { $graph .= $this->createDotEdge( [ - 'from' => $from, - 'to' => $to, + 'to' => $rel['id_child'], + 'from' => $rel['id_parent'], + ] + ); + } + + // Add missed edges. + foreach ($orphans as $rel) { + $graph .= $this->createDotEdge( + [ + 'from' => $rel['id_child'], + 'to' => $rel['id_parent'], ] ); } // Store relationships. - $this->relations = $edges; + $this->relations = array_merge($edges, $orphans); // Close dot file. $graph .= $this->closeDotFile(); - $this->dotGraph = $graph; } - } @@ -1478,7 +1538,9 @@ class NetworkMap $id = $fields[1]; $nodes[$id]['x'] = (($fields[2] * $this->mapOptions['map_filter']['node_radius']) - $this->mapOptions['map_filter']['rank_sep'] * GRAPHVIZ_RADIUS_CONVERSION_FACTOR); $nodes[$id]['y'] = (($fields[3] * $this->mapOptions['map_filter']['node_radius']) - $this->mapOptions['map_filter']['rank_sep'] * GRAPHVIZ_RADIUS_CONVERSION_FACTOR); - } else if (preg_match('/^edge.*$/', $line) != 0) { + } else if (preg_match('/^edge.*$/', $line) != 0 + && empty($this->relations) === true + ) { // Edge. // This is really not needed, because is already defined // in $this->relations. Only for debug purposes. @@ -1492,12 +1554,21 @@ class NetworkMap } $relations[] = [ - 'from' => $fields[2], - 'to' => $fields[1], + 'id_parent' => $target_node['id_node'], + 'parent_type' => NODE_GENERIC, + 'id_parent_source_data' => $mod_rel['module_b'], + 'id_child' => $node['id_node'], + 'child_type' => NODE_GENERIC, + 'id_child_source_data' => $mod_rel['module_a'], ]; } } + // Use current relationship definitions (if exists). + if (empty($this->relations) === false) { + $relations = $this->relations; + } + return [ 'nodes' => $nodes, 'relations' => $relations, @@ -1513,6 +1584,8 @@ class NetworkMap */ public function calculateCoords() { + global $config; + switch (PHP_OS) { case 'WIN32': case 'WINNT': @@ -1686,22 +1759,6 @@ class NetworkMap * Calculate references. */ - // Set the position of modules. - foreach ($nodes as $key => $node) { - if ($node['type'] == 'module') { - // Search the agent of this module for to get the - // position. - foreach ($nodes as $key2 => $node2) { - if ($node2['id_agent'] != 0 && $node2['type'] == 'agent') { - if ($node2['id_agent'] == $node['id_agent']) { - $nodes[$key]['coords'][0] = ($nodes[$key2]['coords'][0] + $node['height'] / 2); - $nodes[$key]['coords'][1] = ($nodes[$key2]['coords'][1] + $node['width'] / 2); - } - } - } - } - } - $index = 0; $node_center = []; @@ -1716,7 +1773,7 @@ class NetworkMap $source = $this->getNodeData($id); $node_tmp['id_agent'] = $source['id_agente']; - $node_tmp['id_module'] = $source['id_module']; + $node_tmp['id_module'] = $source['id_agente_modulo']; $node_tmp['type'] = $source['node_type']; $node_tmp['x'] = $coords['x']; $node_tmp['y'] = $coords['y']; @@ -1764,6 +1821,8 @@ class NetworkMap $node_tmp['id_agent'] = $source['id_agente']; $node_tmp['id_module'] = $source['id_agente_modulo']; $node_tmp['source_data'] = 0; + $node_center['x'] = ($coords['x'] - MAP_X_CORRECTION); + $node_center['y'] = ($coords['y'] - MAP_Y_CORRECTION); break; case NODE_GENERIC: @@ -1771,6 +1830,7 @@ class NetworkMap $node_tmp['text'] = $source['label']; $node_tmp['id_agent'] = $source['id_agente']; $node_tmp['id_module'] = $source['id_agente_modulo']; + $node_tmp['source_data'] = $source['id_source']; break; } @@ -1791,34 +1851,43 @@ class NetworkMap $index++; } - // Prepare graph edges. + // Prepare graph edges and clean double references. $graph['relations'] = []; - - // Edges from and to references id_nodes. Retrieve source data - // before link them. + $parents = []; foreach ($relations as $rel) { - // Parent. - $from_source = $this->getNodeData($rel['from']); - // Child. - $to_source = $this->getNodeData($rel['to']); + $tmp = [ + 'id_map' => $this->idMap, + 'id_parent' => $rel['id_parent'], + 'parent_type' => $rel['parent_type'], + 'id_parent_source_data' => $rel['id_parent_source_data'], + 'id_child' => $rel['id_child'], + 'child_type' => $rel['child_type'], + 'id_child_source_data' => $rel['id_child_source_data'], + 'id_parent_agent' => $rel['id_parent_agent'], + 'id_child_agent' => $rel['id_child_agent'], + ]; + if ($rel['id_child_agent'] == 103 || $rel['id_parent_agent'] == 103) { + hd($tmp); + } - $edge = []; - $edge['id_map'] = $this->idMap; - $edge['id_parent'] = $rel['from']; - $edge['id_child'] = $rel['to']; - $edge['parent_type'] = $from_source['node_type']; - $edge['child_type'] = $to_source['node_type']; - $edge['id_child_source_data'] = $this->auxGetIdByType( - $to_source - ); - $edge['id_parent_source_data'] = $this->auxGetIdByType( - $from_source - ); + // Avoid child - parent - parent -child relation duplicated. + $found = 0; + foreach ($parents[$tmp['id_parent_source_data']] as $k) { + if ($k == $tmp['id_child_source_data']) { + $found = 1; + } + } - $graph['relations'][] = $edge; + if ($found == 0) { + $parents[$tmp['id_child_source_data']][] = $tmp['id_parent_source_data']; + $graph['relations'][] = $tmp; + } } + hd($parents); + if ($this->idMap > 0 && (!isset($this->map['__simulated']))) { + // TODO: REMOVE '!'. if (!enterprise_installed()) { $nodes_and_relations = enterprise_hook( 'save_generate_nodes', @@ -2646,7 +2715,9 @@ class NetworkMap $user_readonly = !$networkmap_write && !$networkmap_manage; - if (isset($this->idMap)) { + if (isset($this->idMap) + && isset($this->map['__simulated']) === false + ) { $output .= $this->loadMapSkel(); $output .= $this->loadMapData(); $output .= $this->loadController(); @@ -2656,6 +2727,7 @@ class NetworkMap $output .= $this->loadMapSkel(); $output .= $this->loadMapData(); $output .= $this->loadController(); + $output .= $this->loadAdvancedInterface(); } if ($return === false) { From 1b99c5db49d64d824f5badc90a86cccfeab8ef4e Mon Sep 17 00:00:00 2001 From: fbsanchez Date: Tue, 19 Mar 2019 20:43:08 +0100 Subject: [PATCH 4/7] WIP netclass editor Former-commit-id: 2c7dffb63e4f9c587c3cdcbf8f87f134a82c1299 --- .../include/class/NetworkMap.class.php | 944 +++++++++++++----- pandora_console/include/constants.php | 8 + .../include/functions_networkmap.php | 12 +- 3 files changed, 714 insertions(+), 250 deletions(-) diff --git a/pandora_console/include/class/NetworkMap.class.php b/pandora_console/include/class/NetworkMap.class.php index 6282b9c980..2407fa8b75 100644 --- a/pandora_console/include/class/NetworkMap.class.php +++ b/pandora_console/include/class/NetworkMap.class.php @@ -233,7 +233,7 @@ class NetworkMap $this->mapOptions['text_filter'] = ''; $this->mapOptions['dont_show_subgroups'] = false; $this->mapOptions['strict_user'] = false; - $this->mapOptions['size_canvas'] = null; + $this->mapOptions['size_canvas'] = 0; $this->mapOptions['old_mode'] = false; $this->mapOptions['map_filter'] = [ 'dont_show_subgroups' => 0, @@ -281,7 +281,9 @@ class NetworkMap // Map options, check default values above. // This is only used while generating new maps using // (generateDotGraph). - if (is_array($options['map_options'])) { + if (isset($options['map_options']) + && is_array($options['map_options']) + ) { foreach ($options['map_options'] as $k => $v) { $this->mapOptions[$k] = $v; } @@ -807,8 +809,34 @@ class NetworkMap /** - * Generates or loads nodes&relations array from data load - * and stores it in $this->graph. + * Generates or loads nodes&relations array from DB. + * Load, calculates statuses and leave the structure in $this->graph. + * + * * Structure generated: + * Nodes: + * id_map. + * id. + * id_agent. + * id_module. + * type. + * x. + * y. + * width. + * height. + * text. + * source_data. + * style (json). + * + * Relations: + * id_map. + * id_parent. + * parent_type. + * id_parent_source_data. + * id_child. + * child_type. + * id_child_source_data. + * id_parent_agent. + * id_child_agent. * * @return void */ @@ -823,16 +851,30 @@ class NetworkMap return; } - $nodes_and_relations = []; - $nodes_and_relations['nodes'] = []; - $index_nodes = 0; - foreach ($nodes as $node) { + $graph = []; + $graph['nodes'] = []; + $node_mapping = []; + $i = 0; + foreach ($nodes as $k => $node) { if (!$node['deleted']) { - $nodes_and_relations['nodes'][$index_nodes]['id_map'] = $node['id_map']; - $nodes_and_relations['nodes'][$index_nodes]['x'] = $node['x']; - $nodes_and_relations['nodes'][$index_nodes]['y'] = $node['y']; - $nodes_and_relations['nodes'][$index_nodes]['source_data'] = $node['source_data']; - $nodes_and_relations['nodes'][$index_nodes]['type'] = $node['type']; + $tmp_node = []; + $tmp_node['id_map'] = $node['id_map']; + $tmp_node['id'] = $i; + $tmp_node['id_db'] = $node['id']; + $tmp_node['source_data'] = $node['source_data']; + $tmp_node['type'] = $node['type']; + + if ($tmp_node['type'] == NODE_AGENT) { + $tmp_node['id_agent'] = $tmp_node['source_data']; + } else if ($tmp_node['type'] == NODE_MODULE) { + $tmp_node['id_module'] = $tmp_node['source_data']; + $tmp_node['id_agent'] = modules_get_agentmodule_agent( + $tmp_node['id_module'] + ); + } else { + $tmp_node['id_agent'] = 0; + $tmp_node['id_module'] = 0; + } $style_node = json_decode($node['style'], true); $style = []; @@ -842,35 +884,67 @@ class NetworkMap $style['height'] = $style_node['height']; $style['label'] = $style_node['label']; $style['id_networkmap'] = $style_node['networkmap']; - $nodes_and_relations['nodes'][$index_nodes]['style'] = json_encode($style); + $tmp_node['style'] = json_encode($style); - if ($node['type'] == 1) { - $nodes_and_relations['nodes'][$index_nodes]['id_agent'] = $style_node['id_agent']; + $tmp_node['x'] = $node['x']; + $tmp_node['y'] = $node['y']; + $tmp_node['z'] = $node['z']; + $tmp_node['width'] = $style['width']; + $tmp_node['height'] = $style['height']; + $tmp_node['text'] = $style['label']; + + if ($tmp_node['type'] == 1) { + $tmp_node['id_agent'] = $style_node['id_agent']; } - $nodes_and_relations['nodes'][$index_nodes]['id_in_db'] = $node['id']; - - $index_nodes++; + $node_mapping[$node['source_data'].'_'.$tmp_node['type']] = $i; + $this->nodeMapping[$i] = $i; + $graph['nodes'][$i++] = $tmp_node; } } - $nodes_and_relations['relations'] = []; - $index_relations = 0; + $graph['relations'] = []; + $i = 0; if (is_array($relations)) { - foreach ($relations as $relation) { - $nodes_and_relations['relations'][$index_relations]['id_map'] = $relation['id_map']; - $nodes_and_relations['relations'][$index_relations]['id_parent'] = $relation['id_parent']; - $nodes_and_relations['relations'][$index_relations]['id_child'] = $relation['id_child']; - $nodes_and_relations['relations'][$index_relations]['parent_type'] = $relation['parent_type']; - $nodes_and_relations['relations'][$index_relations]['child_type'] = $relation['child_type']; - $nodes_and_relations['relations'][$index_relations]['id_parent_source_data'] = $relation['id_parent_source_data']; - $nodes_and_relations['relations'][$index_relations]['id_child_source_data'] = $relation['id_child_source_data']; + foreach ($relations as $rel) { + $edge = []; + $edge['id_map'] = $rel['id_map']; + $edge['id_db'] = $rel['id']; + $edge['id'] = $rel[$i]; - $index_relations++; + if ($rel['parent_type'] == NODE_AGENT) { + $edge['id_parent_agent'] = $rel['id_parent_source_data']; + } else if ($rel['parent_type'] == NODE_MODULE) { + $edge['id_parent_agent'] = modules_get_agentmodule_agent( + $rel['id_parent_source_data'] + ); + } + + if ($rel['child_type'] == NODE_AGENT) { + $edge['id_child_agent'] = $rel['id_child_source_data']; + } else if ($rel['child_type'] == NODE_MODULE) { + $edge['id_child_agent'] = modules_get_agentmodule_agent( + $rel['id_child_source_data'] + ); + } + + // Search parent. + $kp = $edge['id_parent_agent'].'_'.NODE_AGENT; + $edge['id_parent'] = $node_mapping[$kp]; + $edge['parent_type'] = $rel['parent_type']; + $edge['id_parent_source_data'] = $rel['id_parent_source_data']; + + // Search child. + $kc = $edge['id_child_agent'].'_'.NODE_AGENT; + $edge['id_child'] = $node_mapping[$kc]; + $edge['child_type'] = $rel['child_type']; + $edge['id_child_source_data'] = $rel['id_child_source_data']; + + $graph['relations'][$i++] = $edge; } } - $this->graph = $nodes_and_relations; + $this->graph = $graph; } @@ -888,6 +962,7 @@ class NetworkMap $map_filter = $this->mapOptions['map_filter']; $nooverlap = $this->mapOptions['nooverlap']; + $zoom = $this->mapOptions['zoom']; if (isset($config['networkmap_max_width'])) { $size_x = ($config['networkmap_max_width'] / 100); @@ -1037,6 +1112,357 @@ class NetworkMap } + /** + * Avoid multiple connections between two nodes if any of them does not + * add more information. Prioritize. + * + * For instance, if we have module - module relationship and agent - agent + * discard agent - agent relationship (module - module apports more + * information). + * + * @return void + */ + public function cleanGraphRelations() + { + global $config; + + $relations = $this->graph['relations']; + + $segregation_links = []; + $index = 0; + $index2 = 0; + $index3 = 0; + $index4 = 0; + foreach ($relations as $rel) { + if (($rel['parent_type'] == NODE_AGENT) && ($rel['child_type'] == NODE_AGENT)) { + $segregation_links['aa'][$index] = $rel; + $index++; + } else if (($rel['parent_type'] == NODE_MODULE) && ($rel['child_type'] == NODE_MODULE)) { + $segregation_links['mm'][$index2] = $rel; + $index2++; + } else if (($rel['parent_type'] == NODE_GENERIC) && ($rel['child_type'] == NODE_GENERIC)) { + $segregation_links['ff'][$index4] = $rel; + $index4++; + } else { + $segregation_links['am'][$index3] = $rel; + $index3++; + } + } + + $final_links = []; + + // ---------------------------------------------------------------- + // --------------------- Clean duplicate links -------------------- + // ---------------------------------------------------------------- + $duplicated = false; + $index_to_del = 0; + $index = 0; + if (isset($segregation_links['aa']) === true + && is_array($segregation_links['aa']) === true + ) { + foreach ($segregation_links['aa'] as $link) { + foreach ($segregation_links['aa'] as $link2) { + if ($link['id_parent'] == $link2['id_child'] + && $link['id_child'] == $link2['id_parent'] + ) { + /* + XXX: Verify + * enterprise_hook( + * 'delete_link', + * [$segregation_links['aa'][$index_to_del]] + * ); + */ + unset($segregation_links['aa'][$index_to_del]); + } + + $index_to_del++; + } + + $final_links['aa'][$index] = $link; + $index++; + + $duplicated = false; + $index_to_del = 0; + } + } + + $duplicated = false; + $index_to_del = 0; + $index2 = 0; + if (isset($segregation_links['mm']) === true + && is_array($segregation_links['mm']) === true + ) { + foreach ($segregation_links['mm'] as $link) { + foreach ($segregation_links['mm'] as $link2) { + if ($link['id_parent'] == $link2['id_child'] + && $link['id_child'] == $link2['id_parent'] + ) { + /* + XXX: Verify + * enterprise_hook( + * 'delete_link', + * [$segregation_links['mm'][$index_to_del]] + * ); + */ + + unset($segregation_links['mm'][$index_to_del]); + } + + $index_to_del++; + } + + $final_links['mm'][$index2] = $link; + $index2++; + + $duplicated = false; + $index_to_del = 0; + } + } + + $duplicated = false; + $index_to_del = 0; + $index3 = 0; + + if (isset($segregation_links['ff']) === true + && is_array($segregation_links['ff']) === true + ) { + foreach ($segregation_links['ff'] as $link) { + foreach ($segregation_links['ff'] as $link2) { + if ($link['id_parent'] == $link2['id_child'] + && $link['id_child'] == $link2['id_parent'] + ) { + /* + XXX: Verify + * enterprise_hook( + * 'delete_link', + * [$segregation_links['ff'][$index_to_del]] + * ); + */ + unset($segregation_links['ff'][$index_to_del]); + } + + $index_to_del++; + } + + $final_links['ff'][$index3] = $link; + $index3++; + + $duplicated = false; + $index_to_del = 0; + } + } + + $final_links['am'] = $segregation_links['am']; + + /* + ---------------------------------------------------------------- + ----------------- AA, AM and MM links management --------------- + ------------------ Priority: ----------------------------------- + -------------------- 1 -> MM (module - module) ----------------- + -------------------- 2 -> AM (agent - module) ------------------ + -------------------- 3 -> AA (agent - agent) ------------------- + ---------------------------------------------------------------- + */ + + $final_links2 = []; + $index = 0; + $l3_link = []; + $agent1 = 0; + $agent2 = 0; + + if (isset($final_links['mm']) === true + && is_array($final_links['mm']) === true + ) { + foreach ($final_links['mm'] as $rel_mm) { + $module_parent = $rel_mm['id_parent_source_data']; + $module_children = $rel_mm['id_child_source_data']; + $agent1 = (int) agents_get_agent_id_by_module_id( + $module_parent + ); + $agent2 = (int) agents_get_agent_id_by_module_id( + $module_children + ); + foreach ($final_links['aa'] as $key => $rel_aa) { + $l3_link = $rel_aa; + $id_p_source_data = (int) $rel_aa['id_parent_source_data']; + $id_c_source_data = (int) $rel_aa['id_child_source_data']; + if ((($id_p_source_data == $agent1) + && ($id_c_source_data == $agent2)) + || (($id_p_source_data == $agent2) + && ($id_c_source_data == $agent1)) + ) { + enterprise_hook( + 'delete_link', + [$final_links['aa'][$key]] + ); + + unset($final_links['aa'][$key]); + } + } + } + } + + $final_links2['aa'] = $final_links['aa']; + $final_links2['mm'] = $final_links['mm']; + $final_links2['am'] = $final_links['am']; + $final_links2['ff'] = $final_links['ff']; + + $same_m = []; + $index = 0; + if (isset($final_links2['am']) === true + && is_array($final_links2['am']) === true + ) { + foreach ($final_links2['am'] as $rel_am) { + foreach ($final_links2['am'] as $rel_am2) { + if (($rel_am['id_child_source_data'] == $rel_am2['id_child_source_data']) + && ($rel_am['id_parent_source_data'] != $rel_am2['id_parent_source_data']) + ) { + $same_m[$index]['rel'] = $rel_am2; + $same_m[$index]['agent_parent'] = $rel_am['id_parent_source_data']; + $index++; + } + } + } + } + + $final_links3 = []; + $index = 0; + $l3_link = []; + $have_l3 = false; + if (isset($final_links2['aa']) === true + && is_array($final_links2['aa']) === true + ) { + foreach ($final_links2['aa'] as $key => $rel_aa) { + $l3_link = $rel_aa; + foreach ($same_m as $rel_am) { + if ((($rel_aa['id_parent_source_data'] == $rel_am['parent']['id_parent_source_data']) + && ($rel_aa['id_child_source_data'] == $rel_am['rel']['id_parent_source_data'])) + || (($rel_aa['id_child_source_data'] == $rel_am['parent']['id_parent_source_data']) + && ($rel_aa['id_parent_source_data'] == $rel_am['rel']['id_parent_source_data'])) + ) { + enterprise_hook( + 'delete_link', + [$final_links2['aa'][$key]] + ); + + unset($final_links2['aa'][$key]); + } + } + } + } + + $final_links3['aa'] = $final_links2['aa']; + $final_links3['mm'] = $segregation_links['mm']; + $final_links3['am'] = $segregation_links['am']; + $final_links3['ff'] = $final_links2['ff']; + + $cleaned_links = []; + if (isset($final_links3['aa']) === true + && is_array($final_links3['aa']) === true + ) { + foreach ($final_links3['aa'] as $link) { + $cleaned_links[] = $link; + } + } + + if (isset($final_links3['am']) === true + && is_array($final_links3['am']) === true + ) { + foreach ($final_links3['am'] as $link) { + $cleaned_links[] = $link; + } + } + + if (isset($final_links3['mm']) === true + && is_array($final_links3['mm']) === true + ) { + foreach ($final_links3['mm'] as $link) { + $cleaned_links[] = $link; + } + } + + if (isset($final_links3['ff']) === true + && is_array($final_links3['ff']) === true + ) { + foreach ($final_links3['ff'] as $link) { + $cleaned_links[] = $link; + } + } + + $this->graph['relations'] = $cleaned_links; + } + + + /** + * Internal method to allow developer to compare status from + * different origins by checking a value. + * + * Greater value implies more critical. + * + * @param integer $status Status. + * + * @return integer Criticity value. + */ + private static function getStatusNumeric($status) + { + if (isset($status) === false) { + return NO_CRIT; + } + + switch ($status) { + case AGENT_MODULE_STATUS_NORMAL: + case AGENT_STATUS_NORMAL: + return CRIT_1; + + case AGENT_MODULE_STATUS_NOT_INIT: + case AGENT_STATUS_NOT_INIT: + return CRIT_0; + + case AGENT_MODULE_STATUS_CRITICAL_BAD: + case AGENT_STATUS_CRITICAL: + return CRIT_4; + + case AGENT_MODULE_STATUS_WARNING: + case AGENT_STATUS_WARNING: + return CRIT_3; + + case AGENT_MODULE_STATUS_CRITICAL_ALERT: + case AGENT_MODULE_STATUS_WARNING_ALERT: + case AGENT_STATUS_ALERT_FIRED: + return CRIT_5; + + case AGENT_MODULE_STATUS_UNKNOWN: + case AGENT_STATUS_UNKNOWN: + return CRIT_2; + + default: + // Ignored. + break; + } + + return NO_CRIT; + } + + + /** + * Returns worst status from two received. + * Agent and module statuses should be identical, unless little differences. + * + * @param integer $status_a Status A. + * @param integer $status_b Status B. + * + * @return integer Status A or status B, the worstest one. + */ + public static function getWorstStatus($status_a, $status_b) + { + // Case agent statuses. + $a = self::getStatusNumeric($status_a); + $b = self::getStatusNumeric($status_b); + + return ($a > $b) ? $status_a : $status_b; + } + + /** * Returns target color to be used based on the status received. * @@ -1047,7 +1473,7 @@ class NetworkMap public static function getColorByStatus($status) { if (isset($status) === false) { - return COL_IGNORED; + return COL_UNKNOWN; } switch ($status) { @@ -1089,9 +1515,9 @@ class NetworkMap /** * Translates a standard node into a JS node with following attributes: * - * @param array $node Input array (standard node structure). + * @param array $nodes Input array (standard nodes structure). * id_map. - * id_in_db. + * id_db. * type. * source_data. * x. @@ -1131,130 +1557,145 @@ class NetworkMap * map_id. * networkmap_id. */ - public function nodeToJS($node) + public function nodesToJS($nodes) { global $config; - $item = []; - $item['id'] = $node['id']; + $return = []; + foreach ($nodes as $node) { + $item = []; + $item['id'] = $node['id']; - // Id titem. - if (enterprise_installed() - && $this->map['__simulated'] === false - ) { - $item['id_db'] = $node['id_db']; - } - - $source_data = $this->getNodeData($node['id']); - - if (enterprise_installed() && $simulated === false) { - enterprise_include_once('include/functions_networkmap.php'); - $item['id_db'] = $node['id_in_db']; - } else { - $item['id_db'] = (int) $node['id']; - } - - $item['type'] = $node['type']; - $item['fixed'] = true; - $item['x'] = (int) $node['x']; - $item['y'] = (int) $node['y']; - $item['z'] = (int) $node['z']; - - // X,Y aliases for D3. - $item['px'] = $item['x']; - $item['py'] = $item['y']; - - // Status represents the status of the node (critical, warning...). - // State represents state of node in map (in holding_area or not). - $item['state'] = $node['state']; - $item['deleted'] = $node['deleted']; - - // Node color. - $item['color'] = $this->getColorByStatus($source_data['status']); - switch ($node['type']) { - case NODE_AGENT: - $item['id_agent'] = $node['source_data']; - break; - - case NODE_MODULE: - $item['id_module'] = $node['source_data']; - break; - - case NODE_PANDORA: - $item['color'] = COL_IGNORED; - $node['style']['image'] = ui_get_logo_to_center_networkmap(); - break; - - case NODE_GENERIC: - default: - $item['color'] = $node['color']; - break; - } - - // Calculate values. - // 40 => DEFAULT NODE RADIUS. - // 30 => alignment factor. - $holding_area_max_y = ($this->mapOptions['height'] + 30 + $this->mapOptions['map_filter']['node_radius'] * 2 - $this->mapOptions['map_filter']['holding_area'][1] + 10 * $this->mapOptions['map_filter']['node_radius']); - - // Update position if node must be stored in holding_area. - if ($item['state'] == 'holding_area') { - $holding_area_x = ($this->mapOptions['width'] + 30 + $this->mapOptions['map_filter']['node_radius'] * 2 - $this->mapOptions['map_filter']['holding_area'][0] + ($count_item_holding_area % 11) * $this->mapOptions['map_filter']['node_radius']); - $holding_area_y = ($this->mapOptions['height'] + 30 + $this->mapOptions['map_filter']['node_radius'] * 2 - $this->mapOptions['map_filter']['holding_area'][1] + (int) (($count_item_holding_area / 11)) * $this->mapOptions['map_filter']['node_radius']); - - // Keep holding area nodes in holding area. - if ($holding_area_max_y <= $holding_area_y) { - $holding_area_y = $holding_area_max_y; + if ($node['deleted']) { + // Skip deleted nodes. + continue; } - $item['x'] = $holding_area_x; - $item['y'] = $holding_area_y; + // Id titem. + if ($this->map['__simulated'] === false) { + $item['id_db'] = $node['id_db']; + } else { + $item['id_db'] = (int) $node['id']; + } - // Increment for the next node in holding area. - $count_item_holding_area++; + // Get source data. + $source_data = $this->getNodeData($node['id']); + + if (is_array($node['style']) === false) { + $node['style'] = json_decode($node['style'], true); + } + + $item['type'] = $node['type']; + $item['fixed'] = true; + $item['x'] = (int) $node['x']; + $item['y'] = (int) $node['y']; + $item['z'] = (int) $node['z']; + + // X,Y aliases for D3. + $item['px'] = $item['x']; + $item['py'] = $item['y']; + + // Status represents the status of the node (critical, warning...). + // State represents state of node in map (in holding_area or not). + $item['state'] = $node['state']; + $item['deleted'] = $node['deleted']; + + // Node color. + $item['color'] = self::getColorByStatus($source_data['status']); + switch ($node['type']) { + case NODE_AGENT: + $item['id_agent'] = $node['source_data']; + break; + + case NODE_MODULE: + $item['id_module'] = $node['source_data']; + break; + + case NODE_PANDORA: + $item['color'] = COL_IGNORED; + $node['style']['image'] = ui_get_logo_to_center_networkmap(); + break; + + case NODE_GENERIC: + default: + $item['color'] = $node['color']; + break; + } + + // Calculate values. + // 40 => DEFAULT NODE RADIUS. + // 30 => alignment factor. + $holding_area_max_y = ($this->mapOptions['height'] + 30 + $this->mapOptions['map_filter']['node_radius'] * 2 - $this->mapOptions['map_filter']['holding_area'][1] + 10 * $this->mapOptions['map_filter']['node_radius']); + + // Update position if node must be stored in holding_area. + if ($item['state'] == 'holding_area') { + $holding_area_x = ($this->mapOptions['width'] + 30 + $this->mapOptions['map_filter']['node_radius'] * 2 - $this->mapOptions['map_filter']['holding_area'][0] + ($count_item_holding_area % 11) * $this->mapOptions['map_filter']['node_radius']); + $holding_area_y = ($this->mapOptions['height'] + 30 + $this->mapOptions['map_filter']['node_radius'] * 2 - $this->mapOptions['map_filter']['holding_area'][1] + (int) (($count_item_holding_area / 11)) * $this->mapOptions['map_filter']['node_radius']); + + // Keep holding area nodes in holding area. + if ($holding_area_max_y <= $holding_area_y) { + $holding_area_y = $holding_area_max_y; + } + + $item['x'] = $holding_area_x; + $item['y'] = $holding_area_y; + + // Increment for the next node in holding area. + $count_item_holding_area++; + } + + // Node image. + $item['image_url'] = ''; + $item['image_width'] = 0; + $item['image_height'] = 0; + if (empty($node['style']['image']) === false) { + $item['image_url'] = ui_get_full_url( + $node['style']['image'] + ); + $image_size = getimagesize( + $config['homedir'].'/'.$node['style']['image'] + ); + $item['image_width'] = (int) $image_size[0]; + $item['image_height'] = (int) $image_size[1]; + } + + $item['raw_text'] = $node['style']['label']; + $item['text'] = io_safe_output($node['style']['label']); + $item['shape'] = $node['style']['shape']; + $item['map_id'] = $node['id_map']; + if (!isset($node['style']['id_networkmap']) + || $node['style']['id_networkmap'] == '' + || $node['style']['id_networkmap'] == 0 + ) { + $item['networkmap_id'] = 0; + } else { + $item['networkmap_id'] = $node['style']['id_networkmap']; + } + + $return[] = $item; } - // Node image. - $item['image_url'] = ''; - $item['image_width'] = 0; - $item['image_height'] = 0; - if (empty($node['style']['image']) === false) { - $item['image_url'] = ui_get_full_url( - $node['style']['image'] - ); - $image_size = getimagesize( - $config['homedir'].'/'.$node['style']['image'] - ); - $item['image_width'] = (int) $image_size[0]; - $item['image_height'] = (int) $image_size[1]; - } - - $item['raw_text'] = $node['style']['label']; - $item['text'] = io_safe_output($node['style']['label']); - $item['shape'] = $node['style']['shape']; - $item['map_id'] = $node['id_map']; - if (!isset($node['style']['id_networkmap']) - || $node['style']['id_networkmap'] == '' - || $node['style']['id_networkmap'] == 0 - ) { - $item['networkmap_id'] = 0; - } else { - $item['networkmap_id'] = $node['style']['id_networkmap']; - } - - return $item; + return $return; } /** * Transforms an edge relationship into a JS array to be dumped. + * Sets fields like status, link color and updates some internal identifiers + * used by JS frontend. * - * @param array $edge Edge information. + * @param array $edges Edges information in array of following items. * * * Input structure: + * id_map. + * id_parent. * parent_type. - * child_type. * id_parent_source_data. + * id_child. + * child_type. * id_child_source_data. + * id_parent_agent. + * id_child_agent. * * @return array Edge translated to JS object. * @@ -1276,11 +1717,96 @@ class NetworkMap * text_start. * text_end. */ - public function edgeToJS($edge) + public function edgeToJS($edges) { - // TODO: migrate networkmap_links_to_js_links here. - hd($edge); - return []; + $return = []; + // JS edge pseudo identificator. + $i = 0; + foreach ($edges as $rel) { + $item = []; + + // Simulated index. + $item['id_db'] = $i; + $item['deleted'] = 0; + + // Else load. + if (isset($this->map['__simulated']) === false) { + $item['id_db'] = $rel['id_db']; + $item['deleted'] = $rel['deleted']; + $item['target_id_db'] = $this->nodes[$rel['id_parent']]['id']; + $item['source_id_db'] = $this->nodes[$rel['id_child']]['id']; + } + + // Set relationship as 'agent' by default. + // Generic and Pandora nodes simulates agent relationships. + $item['arrow_start'] = 'agent'; + $item['arrow_end'] = 'agent'; + $item['source'] = $rel['id_parent']; + $item['target'] = $rel['id_child']; + $item['id_agent_start'] = $rel['id_child_agent']; + $item['id_agent_end'] = $rel['id_parent_agent']; + + if ($rel['parent_type'] === NODE_MODULE) { + $item['arrow_start'] = 'module'; + $item['id_module_start'] = $rel['id_parent_source_data']; + $item['status_start'] = modules_get_agentmodule_status( + $item['id_module_start'] + ); + + // Extract interface name to be placed on edge. + $text = modules_get_agentmodule_name( + (int) $item['id_module_start'] + ); + if (preg_match( + '/(.+)_ifOperStatus$/', + (string) $text, + $matches + ) + ) { + if ($matches[1]) { + $item['text_start'] = io_safe_output($matches[1]); + } + } + } + + if ($rel['child_type'] === NODE_MODULE) { + $item['arrow_end'] = 'module'; + $item['id_module_end'] = $rel['id_child_source_data']; + $item['status_end'] = modules_get_agentmodule_status( + $item['id_module_end'] + ); + + // Extract interface name to be placed on edge. + $text = modules_get_agentmodule_name( + (int) $item['id_module_end'] + ); + if (preg_match( + '/(.+)_ifOperStatus$/', + (string) $text, + $matches + ) + ) { + if ($matches[1]) { + $item['text_end'] = io_safe_output($matches[1]); + } + } + } + + // Use worst case to set link color. + $item['link_color'] = self::getColorByStatus( + self::getWorstStatus( + $item['status_start'], + $item['status_end'] + ) + ); + + // Set direct values. + $item['id'] = $i++; + + $return[] = $item; + } + + return $return; } @@ -1343,7 +1869,6 @@ class NetworkMap */ public function generateDotGraph($nodes=false) { - echo 'regenerar la net es: '.$this->network.'
'; if (!isset($this->dotGraph)) { // Generate dot file. $this->nodes = []; @@ -1457,8 +1982,8 @@ class NetworkMap foreach ($this->nodes as $k => $item) { $target = $this->calculateRelations($k); - // Adopt orphans. - if (empty($target)) { + // Adopt all orphan nodes but pandora one. + if (empty($target) && $item['id_node'] != 0) { $rel = []; $rel['id_parent'] = 0; $rel['id_child'] = $item['id_node']; @@ -1530,8 +2055,8 @@ class NetworkMap // Graph definition. $fields = explode(' ', $line); - $this->map['width'] = ($fields[2] * 100); - $this->map['height'] = ($fields[3] * 100); + $this->map['width'] = 0; + $this->map['height'] = 0; } else if (preg_match('/^node.*$/', $line) != 0) { // Node. $fields = explode(' ', $line); @@ -1866,16 +2391,18 @@ class NetworkMap 'id_parent_agent' => $rel['id_parent_agent'], 'id_child_agent' => $rel['id_child_agent'], ]; - if ($rel['id_child_agent'] == 103 || $rel['id_parent_agent'] == 103) { - hd($tmp); - } - // Avoid child - parent - parent -child relation duplicated. + // Avoid [child - parent] : [parent - child] relation duplicates. $found = 0; - foreach ($parents[$tmp['id_parent_source_data']] as $k) { - if ($k == $tmp['id_child_source_data']) { - $found = 1; + if (is_array($parents[$tmp['id_parent_source_data']])) { + foreach ($parents[$tmp['id_parent_source_data']] as $k) { + if ($k == $tmp['id_child_source_data']) { + $found = 1; + break; + } } + } else { + $parents[$tmp['id_parent_source_data']] = []; } if ($found == 0) { @@ -1884,16 +2411,14 @@ class NetworkMap } } - hd($parents); - + // Save data. if ($this->idMap > 0 && (!isset($this->map['__simulated']))) { - // TODO: REMOVE '!'. - if (!enterprise_installed()) { - $nodes_and_relations = enterprise_hook( + if (enterprise_installed()) { + $graph = enterprise_hook( 'save_generate_nodes', [ $this->idMap, - $nodes_and_relations, + $graph, ] ); } @@ -1919,7 +2444,6 @@ class NetworkMap } $this->graph = $graph; - } @@ -1952,9 +2476,8 @@ class NetworkMap ]; } - $this->graph['relations'] = clean_duplicate_links( - $this->graph['relations'] - ); + // Prioritize relations between same nodes. + $this->cleanGraphRelations(); // Print some params to handle it in js. html_print_input_hidden('product_name', get_product_name()); @@ -1999,95 +2522,28 @@ class NetworkMap $output .= 'var node_radius = '.$networkmap['filter']['node_radius'].";\n"; $output .= 'var networkmap_holding_area_dimensions = '.json_encode($networkmap['filter']['holding_area']).";\n"; $output .= "var networkmap = {'nodes': [], 'links': []};\n"; - $nodes = $this->graph['nodes']; - if (empty($nodes)) { + // Init. + $count_item_holding_area = 0; + $count = 0; + + // Translate nodes to JS Nodes. + $nodes = $this->graph['nodes']; + if (is_array($nodes) === false) { $nodes = []; } - $count_item_holding_area = 0; - $count = 0; - $nodes_graph = []; - - foreach ($nodes as $key => $node) { - $style = json_decode($node['style'], true); - $node['style'] = json_decode($node['style'], true); - - // Only agents can be show. - if (isset($node['type'])) { - if ($node['type'] == 1) { - continue; - } - } else { - $node['type'] = ''; - } - - $item = $this->nodeToJS( - $node - ); - - /* - * $count, - * $count_item_holding_area, - * $simulate - */ - - if ($item['deleted']) { - continue; - } - - $output .= 'networkmap.nodes.push('.json_encode($item).");\n"; - $nodes_graph[$item['id']] = $item; - } + $nodes_js = $this->nodesToJS($nodes); + $output .= 'networkmap.nodes = ('.json_encode($nodes_js).");\n"; + // Translate edges to js links. $relations = $this->graph['relations']; - - if ($relations === false) { + if (is_array($relations) === false) { $relations = []; } - // Clean the relations and transform the module relations into - // interfaces. - networkmap_clean_relations_for_js($relations); - - $links_js = networkmap_links_to_js_links( - $relations, - $nodes_graph, - $simulate - ); - - $array_aux = []; - foreach ($links_js as $link_js) { - if ($link_js['deleted']) { - unset($links_js[$link_js['id']]); - } - - if ($link_js['target'] == -1) { - unset($links_js[$link_js['id']]); - } - - if ($link_js['source'] == -1) { - unset($links_js[$link_js['id']]); - } - - if ($link_js['target'] == $link_js['source']) { - unset($links_js[$link_js['id']]); - } - - if ($link_js['arrow_start'] == 'module' && $link_js['arrow_end'] == 'module') { - $output .= 'networkmap.links.push('.json_encode($link_js).");\n"; - $array_aux[$link_js['id_agent_start']] = 1; - unset($links_js[$link_js['id']]); - } - } - - foreach ($links_js as $link_js) { - if (($link_js['id_agent_end'] === 0) && $array_aux[$link_js['id_agent_start']] === 1) { - continue; - } else { - $output .= 'networkmap.links.push('.json_encode($link_js).");\n"; - } - } + $links_js = $this->edgeToJS($relations); + $output .= 'networkmap.links = ('.json_encode($links_js).");\n"; $output .= ' //////////////////////////////////////////////////////////////////// diff --git a/pandora_console/include/constants.php b/pandora_console/include/constants.php index f54ca3d550..12f4d8f1fa 100644 --- a/pandora_console/include/constants.php +++ b/pandora_console/include/constants.php @@ -195,6 +195,14 @@ define('AGENT_STATUS_UNKNOWN', 3); define('AGENT_STATUS_ALERT_FIRED', 4); define('AGENT_STATUS_WARNING', 2); +// Pseudo criticity analysis. +define('NO_CRIT', -1); +define('CRIT_0', 0); +define('CRIT_1', 1); +define('CRIT_2', 2); +define('CRIT_3', 3); +define('CRIT_4', 4); +define('CRIT_5', 5); // Visual maps contants. // The items kind. diff --git a/pandora_console/include/functions_networkmap.php b/pandora_console/include/functions_networkmap.php index 39d4ee95ab..82a1e957b5 100644 --- a/pandora_console/include/functions_networkmap.php +++ b/pandora_console/include/functions_networkmap.php @@ -1889,8 +1889,8 @@ function networkmap_links_to_js_links( } } } - } else if (($relation['parent_type'] == NODE_GENERIC) - && ($relation['child_type'] == NODE_GENERIC) + } else if (($relation['parent_type'] == NODE_PANDORA) + && ($relation['child_type'] == NODE_PANDORA) ) { foreach ($nodes_graph as $key2 => $node) { if ($relation['id_parent'] == $node['id_db']) { @@ -1903,10 +1903,10 @@ function networkmap_links_to_js_links( $agent2 = $node['id_db']; } } - } else if (($relation['parent_type'] == NODE_GENERIC) - || ($relation['child_type'] == NODE_GENERIC) + } else if (($relation['parent_type'] == NODE_PANDORA) + || ($relation['child_type'] == NODE_PANDORA) ) { - if ($relation['parent_type'] == NODE_GENERIC) { + if ($relation['parent_type'] == NODE_PANDORA) { foreach ($nodes_graph as $key2 => $node) { if ($relation['id_parent'] == $node['id_db']) { $agent = $node['id_db']; @@ -1914,7 +1914,7 @@ function networkmap_links_to_js_links( $agent2 = $node['id_db']; } } - } else if ($relation['child_type'] == NODE_GENERIC) { + } else if ($relation['child_type'] == NODE_PANDORA) { foreach ($nodes_graph as $key2 => $node) { if ($relation['id_child'] == $node['id_db']) { $agent2 = $node['id_db']; From 9aecc6b85bb3b5f0dbbfe0852be3fbd86e0c1d55 Mon Sep 17 00:00:00 2001 From: fbsanchez Date: Wed, 20 Mar 2019 13:10:20 +0100 Subject: [PATCH 5/7] NetworkClass RC1 Former-commit-id: 8b77646d618d157f0804b11cb7193a7f25bda97b --- .../include/class/NetworkMap.class.php | 172 +++++++----------- 1 file changed, 65 insertions(+), 107 deletions(-) diff --git a/pandora_console/include/class/NetworkMap.class.php b/pandora_console/include/class/NetworkMap.class.php index 2407fa8b75..46b6c5f09a 100644 --- a/pandora_console/include/class/NetworkMap.class.php +++ b/pandora_console/include/class/NetworkMap.class.php @@ -520,6 +520,19 @@ class NetworkMap } + /** + * Set nodes. + * + * @param array $nodes Nodes definition. + * + * @return void + */ + public function setNodes($nodes) + { + $this->nodes = $nodes; + } + + /** * Return nodes of current map. * @@ -546,6 +559,19 @@ class NetworkMap } + /** + * Set relations. + * + * @param array $relations Relations definition. + * + * @return void + */ + public function setRelations($relations) + { + $this->relations = $relations; + } + + /** * Return relations of current map. * @@ -601,7 +627,7 @@ class NetworkMap } else { // Group map. $nodes = agents_get_agents( - $filter, + ['id_grupo' => $this->idGroup], ['*'], 'AR', [ @@ -851,97 +877,15 @@ class NetworkMap return; } - $graph = []; - $graph['nodes'] = []; - $node_mapping = []; - $i = 0; - foreach ($nodes as $k => $node) { - if (!$node['deleted']) { - $tmp_node = []; - $tmp_node['id_map'] = $node['id_map']; - $tmp_node['id'] = $i; - $tmp_node['id_db'] = $node['id']; - $tmp_node['source_data'] = $node['source_data']; - $tmp_node['type'] = $node['type']; + $graph = enterprise_hook( + 'networkmap_load_map', + [$this] + ); - if ($tmp_node['type'] == NODE_AGENT) { - $tmp_node['id_agent'] = $tmp_node['source_data']; - } else if ($tmp_node['type'] == NODE_MODULE) { - $tmp_node['id_module'] = $tmp_node['source_data']; - $tmp_node['id_agent'] = modules_get_agentmodule_agent( - $tmp_node['id_module'] - ); - } else { - $tmp_node['id_agent'] = 0; - $tmp_node['id_module'] = 0; - } - - $style_node = json_decode($node['style'], true); - $style = []; - $style['shape'] = $style_node['shape']; - $style['image'] = $style_node['image']; - $style['width'] = $style_node['width']; - $style['height'] = $style_node['height']; - $style['label'] = $style_node['label']; - $style['id_networkmap'] = $style_node['networkmap']; - $tmp_node['style'] = json_encode($style); - - $tmp_node['x'] = $node['x']; - $tmp_node['y'] = $node['y']; - $tmp_node['z'] = $node['z']; - $tmp_node['width'] = $style['width']; - $tmp_node['height'] = $style['height']; - $tmp_node['text'] = $style['label']; - - if ($tmp_node['type'] == 1) { - $tmp_node['id_agent'] = $style_node['id_agent']; - } - - $node_mapping[$node['source_data'].'_'.$tmp_node['type']] = $i; - $this->nodeMapping[$i] = $i; - $graph['nodes'][$i++] = $tmp_node; - } - } - - $graph['relations'] = []; - $i = 0; - if (is_array($relations)) { - foreach ($relations as $rel) { - $edge = []; - $edge['id_map'] = $rel['id_map']; - $edge['id_db'] = $rel['id']; - $edge['id'] = $rel[$i]; - - if ($rel['parent_type'] == NODE_AGENT) { - $edge['id_parent_agent'] = $rel['id_parent_source_data']; - } else if ($rel['parent_type'] == NODE_MODULE) { - $edge['id_parent_agent'] = modules_get_agentmodule_agent( - $rel['id_parent_source_data'] - ); - } - - if ($rel['child_type'] == NODE_AGENT) { - $edge['id_child_agent'] = $rel['id_child_source_data']; - } else if ($rel['child_type'] == NODE_MODULE) { - $edge['id_child_agent'] = modules_get_agentmodule_agent( - $rel['id_child_source_data'] - ); - } - - // Search parent. - $kp = $edge['id_parent_agent'].'_'.NODE_AGENT; - $edge['id_parent'] = $node_mapping[$kp]; - $edge['parent_type'] = $rel['parent_type']; - $edge['id_parent_source_data'] = $rel['id_parent_source_data']; - - // Search child. - $kc = $edge['id_child_agent'].'_'.NODE_AGENT; - $edge['id_child'] = $node_mapping[$kc]; - $edge['child_type'] = $rel['child_type']; - $edge['id_child_source_data'] = $rel['id_child_source_data']; - - $graph['relations'][$i++] = $edge; - } + if ($graph === ENTERPRISE_NOT_HOOK) { + // Method not available, regenerate. + $this->generateNetworkMap(); + return; } $this->graph = $graph; @@ -1572,7 +1516,7 @@ class NetworkMap } // Id titem. - if ($this->map['__simulated'] === false) { + if (isset($this->map['__simulated']) === false) { $item['id_db'] = $node['id_db']; } else { $item['id_db'] = (int) $node['id']; @@ -1733,8 +1677,19 @@ class NetworkMap if (isset($this->map['__simulated']) === false) { $item['id_db'] = $rel['id_db']; $item['deleted'] = $rel['deleted']; - $item['target_id_db'] = $this->nodes[$rel['id_parent']]['id']; - $item['source_id_db'] = $this->nodes[$rel['id_child']]['id']; + $item['target_id_db'] = $this->getNodeData( + $rel['id_parent'], + 'id_db' + ); + $item['source_id_db'] = $this->getNodeData( + $rel['id_child'], + 'id_db' + ); + } + + if ($item['deleted']) { + // Relation is deleted. Avoid. + continue; } // Set relationship as 'agent' by default. @@ -1746,7 +1701,7 @@ class NetworkMap $item['id_agent_start'] = $rel['id_child_agent']; $item['id_agent_end'] = $rel['id_parent_agent']; - if ($rel['parent_type'] === NODE_MODULE) { + if ($rel['parent_type'] == NODE_MODULE) { $item['arrow_start'] = 'module'; $item['id_module_start'] = $rel['id_parent_source_data']; $item['status_start'] = modules_get_agentmodule_status( @@ -1769,7 +1724,7 @@ class NetworkMap } } - if ($rel['child_type'] === NODE_MODULE) { + if ($rel['child_type'] == NODE_MODULE) { $item['arrow_end'] = 'module'; $item['id_module_end'] = $rel['id_child_source_data']; $item['status_end'] = modules_get_agentmodule_status( @@ -1989,7 +1944,8 @@ class NetworkMap $rel['id_child'] = $item['id_node']; $rel['parent_type'] = NODE_PANDORA; $rel['child_type'] = $item['node_type']; - $rel['child_source_data'] = $item['id_source_data']; + $rel['id_child_source_data'] = $item['id_source_data']; + $orphans[] = $rel; } else { // Flattern edges. @@ -2392,17 +2348,19 @@ class NetworkMap 'id_child_agent' => $rel['id_child_agent'], ]; - // Avoid [child - parent] : [parent - child] relation duplicates. $found = 0; - if (is_array($parents[$tmp['id_parent_source_data']])) { - foreach ($parents[$tmp['id_parent_source_data']] as $k) { - if ($k == $tmp['id_child_source_data']) { - $found = 1; - break; + if (isset($tmp['id_parent_source_data'])) { + // Avoid [child - parent] : [parent - child] relation duplicates. + if (is_array($parents[$tmp['id_parent_source_data']])) { + foreach ($parents[$tmp['id_parent_source_data']] as $k) { + if ($k === $tmp['id_child_source_data']) { + $found = 1; + break; + } } + } else { + $parents[$tmp['id_parent_source_data']] = []; } - } else { - $parents[$tmp['id_parent_source_data']] = []; } if ($found == 0) { @@ -2412,7 +2370,7 @@ class NetworkMap } // Save data. - if ($this->idMap > 0 && (!isset($this->map['__simulated']))) { + if ($this->idMap > 0 && (isset($this->map['__simulated']) === false)) { if (enterprise_installed()) { $graph = enterprise_hook( 'save_generate_nodes', @@ -2457,7 +2415,7 @@ class NetworkMap $networkmap = $this->map; $simulate = false; - if (!isset($networkmap['__simulated'])) { + if (isset($networkmap['__simulated']) === false) { $networkmap['filter'] = json_decode( $networkmap['filter'], true From 9b68e7d04e28351f7a57f4c6e859fcb2d04678b3 Mon Sep 17 00:00:00 2001 From: fbsanchez Date: Wed, 20 Mar 2019 14:15:33 +0100 Subject: [PATCH 6/7] Switch extended events to standard events in host creation (recon) Former-commit-id: 74ee131b3b4083fd50cc37e1a7854ad159afa13a --- pandora_server/lib/PandoraFMS/DiscoveryServer.pm | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/pandora_server/lib/PandoraFMS/DiscoveryServer.pm b/pandora_server/lib/PandoraFMS/DiscoveryServer.pm index d250c36988..6c4b3a5ad4 100644 --- a/pandora_server/lib/PandoraFMS/DiscoveryServer.pm +++ b/pandora_server/lib/PandoraFMS/DiscoveryServer.pm @@ -473,8 +473,7 @@ sub PandoraFMS::Recon::Base::create_agent($$) { $self->{'pa_config'}, $self->{'pa_config'}->{'servername'}, $host_name, $device, $self->{'group_id'}, 0, $id_os, '', 300, $self->{'dbh'}, undef, $location->{'longitude'}, - $location->{'latitude'}, undef, undef, undef, undef, - undef, undef, $self->{'main_event_id'} + $location->{'latitude'} ); return undef unless defined ($agent_id) and ($agent_id > 0); From 05c03d2d46a9fae1cfe2262d38484b5da674ddad Mon Sep 17 00:00:00 2001 From: fbsanchez Date: Wed, 20 Mar 2019 17:22:06 +0100 Subject: [PATCH 7/7] NetworkMap class Former-commit-id: 1c5d4f55853928052482f42aca2900d8964929ad --- pandora_console/godmode/servers/discovery.php | 2 + .../include/class/NetworkMap.class.php | 350 ++++++------------ 2 files changed, 107 insertions(+), 245 deletions(-) diff --git a/pandora_console/godmode/servers/discovery.php b/pandora_console/godmode/servers/discovery.php index d9e19abdff..46ee2f8979 100755 --- a/pandora_console/godmode/servers/discovery.php +++ b/pandora_console/godmode/servers/discovery.php @@ -15,6 +15,8 @@ if (! check_acl($config['id_user'], 0, 'AW')) { ui_require_css_file('discovery'); +ui_print_page_header(__('Discovery'), '', false, '', true); + /** * Mask class names. diff --git a/pandora_console/include/class/NetworkMap.class.php b/pandora_console/include/class/NetworkMap.class.php index 46b6c5f09a..37d2ec2cb0 100644 --- a/pandora_console/include/class/NetworkMap.class.php +++ b/pandora_console/include/class/NetworkMap.class.php @@ -1072,268 +1072,127 @@ class NetworkMap $relations = $this->graph['relations']; - $segregation_links = []; - $index = 0; - $index2 = 0; - $index3 = 0; - $index4 = 0; - foreach ($relations as $rel) { - if (($rel['parent_type'] == NODE_AGENT) && ($rel['child_type'] == NODE_AGENT)) { - $segregation_links['aa'][$index] = $rel; - $index++; - } else if (($rel['parent_type'] == NODE_MODULE) && ($rel['child_type'] == NODE_MODULE)) { - $segregation_links['mm'][$index2] = $rel; - $index2++; - } else if (($rel['parent_type'] == NODE_GENERIC) && ($rel['child_type'] == NODE_GENERIC)) { - $segregation_links['ff'][$index4] = $rel; - $index4++; - } else { - $segregation_links['am'][$index3] = $rel; - $index3++; - } - } - - $final_links = []; - - // ---------------------------------------------------------------- - // --------------------- Clean duplicate links -------------------- - // ---------------------------------------------------------------- - $duplicated = false; - $index_to_del = 0; - $index = 0; - if (isset($segregation_links['aa']) === true - && is_array($segregation_links['aa']) === true - ) { - foreach ($segregation_links['aa'] as $link) { - foreach ($segregation_links['aa'] as $link2) { - if ($link['id_parent'] == $link2['id_child'] - && $link['id_child'] == $link2['id_parent'] - ) { - /* - XXX: Verify - * enterprise_hook( - * 'delete_link', - * [$segregation_links['aa'][$index_to_del]] - * ); - */ - unset($segregation_links['aa'][$index_to_del]); - } - - $index_to_del++; - } - - $final_links['aa'][$index] = $link; - $index++; - - $duplicated = false; - $index_to_del = 0; - } - } - - $duplicated = false; - $index_to_del = 0; - $index2 = 0; - if (isset($segregation_links['mm']) === true - && is_array($segregation_links['mm']) === true - ) { - foreach ($segregation_links['mm'] as $link) { - foreach ($segregation_links['mm'] as $link2) { - if ($link['id_parent'] == $link2['id_child'] - && $link['id_child'] == $link2['id_parent'] - ) { - /* - XXX: Verify - * enterprise_hook( - * 'delete_link', - * [$segregation_links['mm'][$index_to_del]] - * ); - */ - - unset($segregation_links['mm'][$index_to_del]); - } - - $index_to_del++; - } - - $final_links['mm'][$index2] = $link; - $index2++; - - $duplicated = false; - $index_to_del = 0; - } - } - - $duplicated = false; - $index_to_del = 0; - $index3 = 0; - - if (isset($segregation_links['ff']) === true - && is_array($segregation_links['ff']) === true - ) { - foreach ($segregation_links['ff'] as $link) { - foreach ($segregation_links['ff'] as $link2) { - if ($link['id_parent'] == $link2['id_child'] - && $link['id_child'] == $link2['id_parent'] - ) { - /* - XXX: Verify - * enterprise_hook( - * 'delete_link', - * [$segregation_links['ff'][$index_to_del]] - * ); - */ - unset($segregation_links['ff'][$index_to_del]); - } - - $index_to_del++; - } - - $final_links['ff'][$index3] = $link; - $index3++; - - $duplicated = false; - $index_to_del = 0; - } - } - - $final_links['am'] = $segregation_links['am']; + $cleaned = []; + $rel_map = []; /* - ---------------------------------------------------------------- - ----------------- AA, AM and MM links management --------------- - ------------------ Priority: ----------------------------------- - -------------------- 1 -> MM (module - module) ----------------- - -------------------- 2 -> AM (agent - module) ------------------ - -------------------- 3 -> AA (agent - agent) ------------------- - ---------------------------------------------------------------- - */ + * Relation map: + * id_child.'_'.id_parent => [ + * 'priority' (0,1) + * 'relation_index' + * ] + */ - $final_links2 = []; - $index = 0; - $l3_link = []; - $agent1 = 0; - $agent2 = 0; + foreach ($relations as $index => $rel) { + /* + * AA, AM and MM links management + * Priority: + * 1 -> MM (module - module) + * 1 -> AM (agent - module) + * 0 -> AA (agent - agent) + */ - if (isset($final_links['mm']) === true - && is_array($final_links['mm']) === true - ) { - foreach ($final_links['mm'] as $rel_mm) { - $module_parent = $rel_mm['id_parent_source_data']; - $module_children = $rel_mm['id_child_source_data']; - $agent1 = (int) agents_get_agent_id_by_module_id( - $module_parent - ); - $agent2 = (int) agents_get_agent_id_by_module_id( - $module_children - ); - foreach ($final_links['aa'] as $key => $rel_aa) { - $l3_link = $rel_aa; - $id_p_source_data = (int) $rel_aa['id_parent_source_data']; - $id_c_source_data = (int) $rel_aa['id_child_source_data']; - if ((($id_p_source_data == $agent1) - && ($id_c_source_data == $agent2)) - || (($id_p_source_data == $agent2) - && ($id_c_source_data == $agent1)) - ) { - enterprise_hook( - 'delete_link', - [$final_links['aa'][$key]] - ); + $id_parent = $rel['id_parent']; + $id_child = $rel['id_child']; + $rel_type = $rel['child_type'].'_'.$rel['parent_type']; - unset($final_links['aa'][$key]); + $valid = 0; + $key = -1; + if ($rel['parent_type'] == NODE_MODULE + && $rel['child_type'] == NODE_MODULE + ) { + // Module information available. + $priority = 1; + $valid = 1; + + if (is_array($rel_map[$id_child.'_'.$id_parent])) { + // Already defined. + $key = $id_child.'_'.$id_parent; + $data = $rel_map[$id_child.'_'.$id_parent]; + if ($priority > $data['priority']) { + unset($rel[$data['index']]); + } else { + $valid = 0; } } - } - } - $final_links2['aa'] = $final_links['aa']; - $final_links2['mm'] = $final_links['mm']; - $final_links2['am'] = $final_links['am']; - $final_links2['ff'] = $final_links['ff']; - - $same_m = []; - $index = 0; - if (isset($final_links2['am']) === true - && is_array($final_links2['am']) === true - ) { - foreach ($final_links2['am'] as $rel_am) { - foreach ($final_links2['am'] as $rel_am2) { - if (($rel_am['id_child_source_data'] == $rel_am2['id_child_source_data']) - && ($rel_am['id_parent_source_data'] != $rel_am2['id_parent_source_data']) - ) { - $same_m[$index]['rel'] = $rel_am2; - $same_m[$index]['agent_parent'] = $rel_am['id_parent_source_data']; - $index++; + if (is_array($rel_map[$id_parent.'_'.$id_child])) { + // Already defined. + $key = $id_parent.'_'.$id_child; + $data = $rel_map[$id_parent.'_'.$id_child]; + if ($priority > $data['priority']) { + unset($rel[$data['index']]); + } else { + $valid = 0; } } - } - } - $final_links3 = []; - $index = 0; - $l3_link = []; - $have_l3 = false; - if (isset($final_links2['aa']) === true - && is_array($final_links2['aa']) === true - ) { - foreach ($final_links2['aa'] as $key => $rel_aa) { - $l3_link = $rel_aa; - foreach ($same_m as $rel_am) { - if ((($rel_aa['id_parent_source_data'] == $rel_am['parent']['id_parent_source_data']) - && ($rel_aa['id_child_source_data'] == $rel_am['rel']['id_parent_source_data'])) - || (($rel_aa['id_child_source_data'] == $rel_am['parent']['id_parent_source_data']) - && ($rel_aa['id_parent_source_data'] == $rel_am['rel']['id_parent_source_data'])) - ) { - enterprise_hook( - 'delete_link', - [$final_links2['aa'][$key]] - ); + if ($valid == 1) { + $rel_map[$id_parent.'_'.$id_child] = [ + 'index' => $index, + 'priority' => $priority, + ]; + } + } else if ($rel['parent_type'] == NODE_AGENT + && $rel['child_type'] == NODE_AGENT + ) { + // Module information not available. + $priority = 0; + $valid = 1; - unset($final_links2['aa'][$key]); + if (is_array($rel_map[$id_child.'_'.$id_parent])) { + // Already defined. + $key = $id_child.'_'.$id_parent; + $data = $rel_map[$id_child.'_'.$id_parent]; + if ($priority > $data['priority']) { + unset($rel[$data['index']]); + } else { + $valid = 0; } } + + if (is_array($rel_map[$id_parent.'_'.$id_child])) { + // Already defined. + $key = $id_parent.'_'.$id_child; + $data = $rel_map[$id_parent.'_'.$id_child]; + if ($priority > $data['priority']) { + unset($rel[$data['index']]); + } else { + $valid = 0; + } + } + + if ($valid == 1) { + $rel_map[$id_parent.'_'.$id_child] = [ + 'index' => $index, + 'priority' => $priority, + ]; + } + } else if ($rel['parent_type'] == NODE_MODULE + && $rel['child_type'] == NODE_AGENT + ) { + // Module information not available. + $priority = 1; + + $valid = 1; + } else if ($rel['parent_type'] == NODE_AGENT + && $rel['child_type'] == NODE_MODULE + ) { + // Module information not available. + $priority = 1; + + $valid = 1; + } else { + // Pandora & generic links are always accepted. + $valid = 1; + } + + if ($valid === 1) { + $cleaned[] = $rel; } } - $final_links3['aa'] = $final_links2['aa']; - $final_links3['mm'] = $segregation_links['mm']; - $final_links3['am'] = $segregation_links['am']; - $final_links3['ff'] = $final_links2['ff']; - - $cleaned_links = []; - if (isset($final_links3['aa']) === true - && is_array($final_links3['aa']) === true - ) { - foreach ($final_links3['aa'] as $link) { - $cleaned_links[] = $link; - } - } - - if (isset($final_links3['am']) === true - && is_array($final_links3['am']) === true - ) { - foreach ($final_links3['am'] as $link) { - $cleaned_links[] = $link; - } - } - - if (isset($final_links3['mm']) === true - && is_array($final_links3['mm']) === true - ) { - foreach ($final_links3['mm'] as $link) { - $cleaned_links[] = $link; - } - } - - if (isset($final_links3['ff']) === true - && is_array($final_links3['ff']) === true - ) { - foreach ($final_links3['ff'] as $link) { - $cleaned_links[] = $link; - } - } - - $this->graph['relations'] = $cleaned_links; + $this->graph['relations'] = $cleaned; } @@ -3040,6 +2899,7 @@ class NetworkMap $minimap_display = ''; if ($this->mapOptions['pure']) { $minimap_display = 'none'; + $minimap_display = ''; } $networkmap = $this->map;