Merge branch 'ent-8535-Monitorizacion-de-clusters-Pasar-de-Enterprise-a-Open' into 'develop'

implement cluster in open project

See merge request artica/pandorafms!5204
This commit is contained in:
Daniel Rodriguez 2022-12-21 09:05:01 +00:00
commit cc2da2d4a4
17 changed files with 4231 additions and 75 deletions

View File

@ -1666,6 +1666,14 @@ godmode/um_client/vendor/sebastian/object-enumerator
godmode/um_client/vendor/sebastian
godmode/um_client/vendor
update_manager_client/resources/styles/pandora.css
enterprise/views/cluster/edit.php
enterprise/views/cluster/list.php
enterprise/views/cluster/view.php
enterprise/include/lib/Cluster.php
enterprise/include/lib/ClusterModule.php
enterprise/include/lib/ClusterViewer/ClusterManager.php
enterprise/include/lib/ClusterViewer/ClusterWizard.php
enterprise/operation/cluster/cluster.php
enterprise/meta/general/upload_head_image.php
general/first_task/transactional_list.php
enterprise/include/ajax/transactional.ajax.php

View File

@ -68,7 +68,7 @@ ui_print_info_message(['no_close' => true, 'message' => __('There are no cluster
if (check_acl($config['id_user'], 0, 'AW')) {
?>
<form action='index.php?sec=estado&sec2=enterprise/operation/cluster/cluster&op=new' method="post">
<form action='index.php?sec=estado&sec2=operation/cluster/cluster&op=new' method="post">
<input type="submit" class="button_task ui_toggle" value="<?php echo __('Create Cluster'); ?>" />
</form>
@ -76,4 +76,4 @@ ui_print_info_message(['no_close' => true, 'message' => __('There are no cluster
}
?>
</div>
</div>
</div>

View File

@ -732,17 +732,15 @@ if ($agents !== false) {
}
if ($agent['id_os'] == CLUSTER_OS_ID) {
if (enterprise_installed()) {
$cluster = PandoraFMS\Enterprise\Cluster::loadFromAgentId(
$agent['id_agente']
);
$url = 'index.php?sec=reporting&sec2='.ENTERPRISE_DIR;
$url .= '/operation/cluster/cluster';
$url = ui_get_full_url(
$url.'&op=update&id='.$cluster->id()
);
echo '<a href="'.$url.'">'.ui_print_truncate_text($agent['alias'], 'agent_medium').'</a>';
}
$cluster = PandoraFMS\Cluster::loadFromAgentId(
$agent['id_agente']
);
$url = 'index.php?sec=reporting&sec2=';
$url .= 'operation/cluster/cluster';
$url = ui_get_full_url(
$url.'&op=update&id='.$cluster->id()
);
echo '<a href="'.$url.'">'.ui_print_truncate_text($agent['alias'], 'agent_medium').'</a>';
} else {
echo '<a alt ='.$agent['nombre']." href='index.php?sec=gagente&
sec2=godmode/agentes/configurar_agente&tab=$main_tab&
@ -792,18 +790,16 @@ if ($agents !== false) {
echo '</span><div class="left actions clear_left" style=" visibility: hidden">';
if ($check_aw) {
if ($agent['id_os'] == CLUSTER_OS_ID) {
if (enterprise_installed()) {
$cluster = PandoraFMS\Enterprise\Cluster::loadFromAgentId(
$agent['id_agente']
);
$url = 'index.php?sec=reporting&sec2='.ENTERPRISE_DIR;
$url .= '/operation/cluster/cluster';
$url = ui_get_full_url(
$url.'&op=update&id='.$cluster->id()
);
echo '<a href="'.$url.'">'.__('Edit').'</a>';
echo ' | ';
}
$cluster = PandoraFMS\Cluster::loadFromAgentId(
$agent['id_agente']
);
$url = 'index.php?sec=reporting&sec2=';
$url .= 'operation/cluster/cluster';
$url = ui_get_full_url(
$url.'&op=update&id='.$cluster->id()
);
echo '<a href="'.$url.'">'.__('Edit').'</a>';
echo ' | ';
} else {
echo '<a href="index.php?sec=gagente&
sec2=godmode/agentes/configurar_agente&tab=main&
@ -825,17 +821,15 @@ if ($agents !== false) {
echo ' | ';
if ($agent['id_os'] == CLUSTER_OS_ID) {
if (enterprise_installed()) {
$cluster = PandoraFMS\Enterprise\Cluster::loadFromAgentId(
$agent['id_agente']
);
$url = 'index.php?sec=reporting&sec2='.ENTERPRISE_DIR;
$url .= '/operation/cluster/cluster';
$url = ui_get_full_url(
$url.'&op=view&id='.$cluster->id()
);
echo '<a href="'.$url.'">'.__('View').'</a>';
}
$cluster = PandoraFMS\Cluster::loadFromAgentId(
$agent['id_agente']
);
$url = 'index.php?sec=reporting&sec2=';
$url .= 'operation/cluster/cluster';
$url = ui_get_full_url(
$url.'&op=view&id='.$cluster->id()
);
echo '<a href="'.$url.'">'.__('View').'</a>';
} else {
echo '<a href="index.php?sec=estado
&sec2=operation/agentes/ver_agente

View File

@ -62,7 +62,7 @@ enterprise_include_once('include/functions_cron.php');
// Clases.
use PandoraFMS\Agent;
use PandoraFMS\Module;
use PandoraFMS\Enterprise\Cluster;
use PandoraFMS\Cluster;
use PandoraFMS\Enterprise\Metaconsole\Node;
use PandoraFMS\Event;
use PandoraFMS\SpecialDay;

View File

@ -690,18 +690,16 @@ function treeview_printTable($id_agente, $server_data=[], $no_head=false)
$go_to_agent = '<div style="text-align: right">';
if ($agent['id_os'] == CLUSTER_OS_ID) {
if (enterprise_installed()) {
$cluster = PandoraFMS\Enterprise\Cluster::loadFromAgentId(
$agent['id_agente']
);
$url = 'index.php?sec=reporting&sec2='.ENTERPRISE_DIR;
$url .= '/operation/cluster/cluster';
$url = ui_get_full_url(
$url.'&op=update&id='.$cluster->id()
);
$go_to_agent .= '<a target="_blank" href="'.$url.'">';
$go_to_agent .= html_print_submit_button(__('Edit cluster'), 'upd_button', false, 'class="sub config"', true);
}
$cluster = PandoraFMS\Cluster::loadFromAgentId(
$agent['id_agente']
);
$url = 'index.php?sec=reporting&sec2=';
$url .= 'operation/cluster/cluster';
$url = ui_get_full_url(
$url.'&op=update&id='.$cluster->id()
);
$go_to_agent .= '<a target="_blank" href="'.$url.'">';
$go_to_agent .= html_print_submit_button(__('Edit cluster'), 'upd_button', false, 'class="sub config"', true);
} else {
$go_to_agent .= '<a target=_blank href="'.$console_url.'index.php?sec=gagente&sec2=godmode/agentes/configurar_agente&id_agente='.$id_agente.$ent.'">';
$go_to_agent .= html_print_submit_button(__('Go to agent edition'), 'upd_button', false, 'class="sub config"', true);

View File

@ -0,0 +1,906 @@
<?php
/**
* Cluster entity class.
*
* @category Class
* @package Pandora FMS
* @subpackage Community
* @version 1.0.0
* @license See below
*
* ______ ___ _______ _______ ________
* | __ \.-----.--.--.--| |.-----.----.-----. | ___| | | __|
* | __/| _ | | _ || _ | _| _ | | ___| |__ |
* |___| |___._|__|__|_____||_____|__| |___._| |___| |__|_|__|_______|
*
* ============================================================================
* Copyright (c) 2005-2021 Artica Soluciones Tecnologicas
* Please see http://pandorafms.org for full contribution list
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation for version 2.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* ============================================================================
*/
// Begin.
namespace PandoraFMS;
use PandoraFMS\Entity;
use PandoraFMS\Agent;
use PandoraFMS\Module;
use PandoraFMS\Group;
/**
* PandoraFMS Cluster entity.
*/
class Cluster extends Entity
{
/**
* References cluster status Module.
*
* @var PandoraFMS\Module
*/
private $clusterStatus;
/**
* Array of PandoraFMS\Agents members of this cluster.
*
* @var array
*/
private $members = [];
/**
* AA modules.
*
* @var array
*/
private $aaModules = [];
/**
* AP modules.
*
* @var array
*/
private $apModules = [];
/**
* Removed items.
*
* @var array
*/
private $removedItems = [];
/**
* Loads a cluster definition from target agent (rel 1-1).
*
* @param integer $id_agent Agent id.
* @param boolean $load_members Load members or not.
*
* @return PandoraFMS\Cluster Object.
*/
public static function loadFromAgentId(
int $id_agent,
?bool $load_members=true
) {
if (is_numeric($id_agent) === true
&& $id_agent > 0
) {
$cluster_id = db_get_value(
'id',
'tcluster',
'id_agent',
$id_agent
);
return new self($cluster_id, $load_members);
}
return null;
}
/**
* Builds a PandoraFMS\ClusterViewer\Cluster object from a cluster id.
*
* @param integer $id_cluster Cluster Id.
* @param boolean $load_members Load members or not.
*
* @throws \Exception On error.
*/
public function __construct(?int $id_cluster=null, ?bool $load_members=true)
{
if (is_numeric($id_cluster) === true
&& $id_cluster > 0
) {
try {
parent::__construct('tcluster', ['id' => $id_cluster]);
} catch (\Exception $e) {
throw new \Exception('Cluster id not found.');
}
if ($load_members === true) {
// Retrieve members.
$data = \db_get_all_rows_filter(
'tcluster_agent',
['id_cluster' => $id_cluster]
);
if (is_array($data) === true) {
foreach ($data as $row) {
$this->addMember($row['id_agent']);
}
}
}
// Retrieve items.
$data = \db_get_all_rows_filter(
'tcluster_item',
['id_cluster' => $id_cluster]
);
if (is_array($data) === true) {
foreach ($data as $row) {
if ($row['item_type'] === 'AA') {
$this->aaModules[$row['name']] = new ClusterModule(
$row['id']
);
} else if ($row['item_type'] === 'AP') {
$this->apModules[$row['name']] = new ClusterModule(
$row['id']
);
}
}
}
} else {
parent::__construct('tcluster');
}
// Customize certain fields.
try {
$this->fields['group'] = new Group($this->group());
} catch (\Exception $e) {
$this->fields['group'] = new Group();
}
if ($this->id_agent() !== null) {
try {
$this->fields['agent'] = new Agent($this->id_agent(), true);
} catch (\Exception $e) {
$this->fields['agent'] = new Agent();
}
} else {
$this->fields['agent'] = new Agent();
}
if ($this->id_agent() !== null) {
$this->clusterStatus = Module::search(
[
'nombre' => io_safe_input('Cluster status'),
'id_agente' => $this->id_agent(),
],
1
);
}
}
/**
* Return an array of PandoraFMS\Agents as members of current cluster.
*
* @return array Of agents.
*/
public function getMembers()
{
if (is_array($this->members) === true) {
return $this->members;
}
return [];
}
/**
* Cleans members from cluster object.
*
* @return void
*/
public function cleanMembers()
{
unset($this->members);
}
/**
* Register a new agent in the cluster.
*
* @param integer $id_agent New id_agent to be added.
*
* @return mixed
* @throws \Exception On error.
*/
public function addMember(int $id_agent)
{
if (isset($this->members[$id_agent]) === true) {
// Already joining.
return;
}
try {
$agent = new Agent($id_agent);
} catch (\Exception $e) {
return;
}
if ($agent->id_agente() === null) {
throw new \Exception('Invalid agent id.');
}
$this->members[$agent->id_agente()] = $agent;
return $agent;
}
/**
* Remove an agent from the cluster.
*
* @param integer $id_agent New id_agent to be removed.
*
* @return void
* @throws \Exception On error.
*/
public function removeMember(int $id_agent)
{
if (isset($this->members[$id_agent]) === false) {
return;
}
unset($this->members[$id_agent]);
$rs = \db_process_sql_delete(
'tcluster_agent',
[
'id_cluster' => $this->fields['id'],
'id_agent' => $id_agent,
]
);
return true;
}
/**
* Return AA modules associated to current cluster.
*
* @param integer $type AA or AP (use constants)
* MODULE_PREDICTION_CLUSTER_AA
* MODULE_PREDICTION_CLUSTER_AP.
*
* @return array Of items.
*/
public function getItems(?int $type=null)
{
$items = [];
if ($type === MODULE_PREDICTION_CLUSTER_AA) {
if (is_array($this->aaModules) === true) {
return $this->aaModules;
}
}
if ($type === MODULE_PREDICTION_CLUSTER_AP) {
if (is_array($this->apModules) === true) {
return $this->apModules;
}
}
if (is_array($this->apModules) === true
) {
$items = array_merge($items, $this->apModules);
}
if (is_array($this->aaModules) === true
) {
$items = array_merge($items, $this->aaModules);
}
return $items;
}
/**
* Retrieve AA modules.
*
* @return array Of ClusterItem definition.
*/
public function getAAModules()
{
return $this->getItems(MODULE_PREDICTION_CLUSTER_AA);
}
/**
* Retrieve AP modules.
*
* @return array Of ClusterItem definition.
*/
public function getAPModules()
{
return $this->getItems(MODULE_PREDICTION_CLUSTER_AP);
}
/**
* Retrieves module definition from current members matching name.
*
* @param string $name Target name to retrieve.
*
* @return array Module fields.
* @throws \Exception On error.
*/
public function getModuleSkel(string $name)
{
foreach ($this->members as $member) {
$module = $member->searchModules(
['nombre' => $name]
);
if ($module !== null && empty($module) === false) {
if (count($module) > 1) {
$msg = __METHOD__.' error: Multiple occurrences of "';
$msg .= $name.'", please remove duplicates from agent "';
$msg .= $member->alias().'".';
throw new \Exception(
$msg
);
}
// Method searchModules returns multiple occurrences.
$module = $module[0];
$module = $module->toArray();
break;
}
}
// Remove specific fields.
unset($module['id_agente_modulo']);
unset($module['id_agente']);
return $module;
}
/**
* Add an item to the cluster.
*
* @param string $name Target name.
* @param integer $type Item type.
* @param array $definition Module definition.
*
* @return ClusterModule Created module.
* @throws \Exception On error.
*/
public function addItem(string $name, int $type, array $definition)
{
$item = new ClusterModule();
$item->name($name);
$item->id_cluster($this->id());
// Skel values.
$module_skel = $this->getModuleSkel($name);
// Customize definition.
$definition = array_merge($module_skel, $definition);
// Store in cluster agent.
$definition['id_agente'] = $this->id_agent();
if ($type === MODULE_PREDICTION_CLUSTER_AA) {
$item->item_type('AA');
} else if ($type === MODULE_PREDICTION_CLUSTER_AP) {
$item->item_type('AP');
} else {
throw new \Exception(__METHOD__.' error: Invalid item type');
}
// Set module definition.
$item->setModule($definition);
// Default values.
$item->critical_limit(0);
$item->warning_limit(0);
$item->is_critical(0);
return $item;
}
/**
* Add AA module to the cluster.
*
* @param string $name Target name.
*
* @return void
*/
public function addAAModule(string $name)
{
if (empty($this->aaModules[$name]) === true) {
$main_id = $this->clusterStatus->id_agente_modulo();
// Register module in agent.
// id_modulo = 0,
// tcp_port = 1,
// prediction_moddule = 6.
// Set thresholds while updating.
$this->aaModules[$name] = $this->addItem(
$name,
MODULE_PREDICTION_CLUSTER_AA,
[
'nombre' => $name,
'id_modulo' => 0,
'prediction_module' => 6,
'tcp_port' => 1,
'id_tipo_modulo' => 1,
'custom_integer_1' => $this->id(),
'parent_module_id' => $main_id,
]
);
\db_pandora_audit(
AUDIT_LOG_AGENT_MANAGEMENT,
'Module '.io_safe_output(
$name
).' added to cluster'.io_safe_output(
$this->fields['name']
).' as Active-Active module'
);
}
}
/**
* Add AP module to the cluster.
*
* @param string $name Target name.
*
* @return void
*/
public function addAPModule(string $name)
{
if (empty($this->apModules[$name]) === true) {
$main_id = $this->clusterStatus->id_agente_modulo();
$type = db_get_value(
'id_tipo_modulo',
'tagente_modulo',
'nombre',
$name
);
if (empty($type) === true) {
$type = 1;
}
// Register module in agent.
// id_modulo = 5,
// tcp_port = 1,
// prediction_moddule = 7.
// Set thresholds while updating.
$this->apModules[$name] = $this->addItem(
$name,
MODULE_PREDICTION_CLUSTER_AP,
[
'nombre' => $name,
'id_modulo' => 5,
'prediction_module' => 7,
'tcp_port' => 1,
'id_tipo_modulo' => $type,
'custom_integer_1' => $this->id(),
'parent_module_id' => $main_id,
]
);
\db_pandora_audit(
AUDIT_LOG_AGENT_MANAGEMENT,
'Module '.io_safe_output(
$name
).' added to cluster'.io_safe_output(
$this->fields['name']
).' as Active-Passive module'
);
}
}
/**
* Removes AA module from the cluster.
*
* @param string $name Target name.
*
* @return void
*/
public function removeAAModule(string $name)
{
if (empty($this->aaModules[$name]) === false) {
// Mark item for db elimination.
$this->removedItems[] = [
'id' => $this->aaModules[$name]->id(),
'item_type' => $this->aaModules[$name]->item_type(),
];
$this->aaModules[$name]->delete();
unset($this->aaModules[$name]);
}
}
/**
* Removes AP module from the cluster.
*
* @param string $name Target name.
*
* @return void
*/
public function removeAPModule(string $name)
{
if (empty($this->apModules[$name]) === false) {
// Mark item for db elimination.
$this->removedItems[] = [
'id' => $this->apModules[$name]->id(),
'item_type' => $this->apModules[$name]->item_type(),
];
$this->apModules[$name]->delete();
unset($this->apModules[$name]);
}
}
/**
* Return found cluster definitions.
*
* @param array $filter Conditions.
*
* @return mixed Array or false.
*/
public static function search(array $filter)
{
return \db_get_all_rows_filter(
'tcluster',
$filter
);
}
/**
* Operates with group.
*
* @param integer|null $id_group Target group to update. Retrieve group obj
* if null.
*
* @return mixed Void if set, PandoraFMS\Group if argument is null.
*/
public function group(?int $id_group=null)
{
if (is_numeric($id_group) === true && $id_group > 0) {
$this->fields['group'] = new Group($id_group);
} else {
return $this->fields['group'];
}
}
/**
* Returns AA modules as nodes for a map if any, if not, retrieves members.
*
* @return array Of PandoraFMS\Networkmap nodes.
*/
public function getNodes()
{
// Parse agents.
$nodes = [];
$node_count = 0;
$parent = $node_count;
$id_node = $node_count++;
$status = \agents_get_status_from_counts($this->agent()->toArray());
$image = 'images/networkmap/'.os_get_icon($this->agent()->id_os());
if (empty($this->aaModules) === true) {
// No AA modules, use members.
$parent = $this->agent()->id_agente();
// Add node.
foreach ($this->members as $agent) {
$node = [];
foreach ($agent->toArray() as $k => $v) {
$node[$k] = $v;
}
$node['id_agente'] = $agent->id_agente();
$node['id_parent'] = $parent;
$node['id_node'] = $node_count;
$node['image'] = 'images/networkmap/'.os_get_icon(
$agent->id_os()
);
$node['status'] = \agents_get_status_from_counts(
$agent->toArray()
);
$nodes[$node_count++] = $node;
}
} else {
foreach ($this->aaModules as $cl_item) {
$cl_module = $cl_item->getModule();
if ($cl_module === null) {
continue;
}
foreach ($this->members as $agent) {
$module = $agent->searchModules(
['nombre' => $cl_module->nombre()]
);
if (empty($module) === true) {
// AA Module not found in member.
continue;
}
// Transform multi array to get first occurrence.
// Warning. Here must only be 1 result.
$module = array_shift($module);
$node = [];
$node['type'] = NODE_GENERIC;
$node['label'] = $agent->alias().' &raquo; ';
$node['label'] .= $module->nombre();
$node['id_agente'] = $module->id_agente();
$node['id_agente_modulo'] = $module->id_agente_modulo();
$node['id_parent'] = $parent;
$node['id_node'] = $node_count;
$node['image'] = 'images/networkmap/'.os_get_icon(
$agent->id_os()
);
$node['status'] = $module->getStatus()->last_known_status();
$nodes[$node_count++] = $node;
}
}
}
$nodes[$parent] = $this->agent()->toArray();
$nodes[$parent] = ($nodes[$parent] + [
'id_parent' => $parent,
'id_node' => $id_node,
'status' => $status,
'id_agente' => $this->agent()->id_agente(),
'image' => $image,
]);
return $nodes;
}
/**
* Saves current group definition to database.
*
* @return mixed Affected rows of false in case of error.
* @throws \Exception On error.
*/
public function save()
{
$values = $this->fields;
unset($values['agent']);
$values['group'] = $this->group()->id_grupo();
if (isset($values['id']) === true && $values['id'] > 0) {
// Update.
$rs = \db_process_sql_update(
'tcluster',
$values,
['id' => $this->fields['id']]
);
if ($rs === false) {
global $config;
throw new \Exception(
__METHOD__.' error: '.$config['dbconnection']->error
);
}
\db_pandora_audit(
AUDIT_LOG_AGENT_MANAGEMENT,
'Cluster '.io_safe_output($this->fields['name']).' modified'
);
} else {
// New.
$rs = \db_process_sql_insert(
'tcluster',
$values
);
if ($rs === false) {
global $config;
throw new \Exception(
__METHOD__.' error: '.$config['dbconnection']->error
);
}
$this->fields['id'] = $rs;
\db_pandora_audit(
AUDIT_LOG_AGENT_MANAGEMENT,
'Cluster '.io_safe_output($this->fields['name']).' created'
);
}
$this->saveMembers();
$this->saveItems();
return true;
}
/**
* Updates entries in tcluster_agent.
*
* @return void
* @throws \Exception On error.
*/
public function saveMembers()
{
$err = __METHOD__.' error: ';
$values = [];
foreach ($this->members as $agent) {
$values[$agent->id_agente()] = [
'id_cluster' => $this->fields['id'],
'id_agent' => $agent->id_agente(),
];
}
if (empty($values) === true) {
return;
}
// Clean previous relationships.
$rs = \db_process_sql_delete(
'tcluster_agent',
[ 'id_cluster' => $this->fields['id'] ]
);
foreach ($values as $set) {
// Add current relationships.
$rs = \db_process_sql_insert(
'tcluster_agent',
$set
);
if ($rs === false) {
global $config;
throw new \Exception(
$err.$config['dbconnection']->error
);
}
}
}
/**
* Undocumented function
*
* @return void
*/
public function saveItems()
{
$items = $this->getItems();
foreach ($this->removedItems as $item) {
\db_process_sql_delete(
'tcluster_item',
$item
);
}
// Save cluster modules.
foreach ($items as $item) {
$item->save();
}
}
/**
* Force cluster status module to be executed.
*
* @param boolean $get_informed Throw exception if clusterStatus is null.
*
* @return void
* @throws \Exception On error.
*/
public function force(?bool $get_informed=true)
{
if ($this->clusterStatus === null) {
if ($get_informed === true) {
throw new \Exception(
__METHOD__.' error: Cluster status module does not exist'
);
}
} else {
$this->clusterStatus->flag(1);
$this->clusterStatus->save();
}
}
/**
* Delete cluster from db.
*
* @return void
* @throws \Exception On error.
*/
public function delete()
{
global $config;
if ($this->agent() !== null) {
// Delete agent and modules.
$this->agent()->delete();
}
// Remove entries from db.
// Table tcluster_agent.
$rs = \db_process_sql_delete(
'tcluster_agent',
['id_cluster' => $this->fields['id']]
);
if ($rs === false) {
throw new \Exception(
__METHOD__.' error: '.$config['dbconnection']->error
);
}
// Table tcluster_item.
$rs = \db_process_sql_delete(
'tcluster_item',
['id_cluster' => $this->fields['id']]
);
if ($rs === false) {
throw new \Exception(
__METHOD__.' error: '.$config['dbconnection']->error
);
}
// Table tcluster.
$rs = \db_process_sql_delete(
'tcluster',
['id' => $this->fields['id']]
);
if ($rs === false) {
throw new \Exception(
__METHOD__.' error: '.$config['dbconnection']->error
);
}
\db_pandora_audit(
AUDIT_LOG_AGENT_MANAGEMENT,
'Cluster '.io_safe_output($this->fields['name']).' deleted'
);
unset($this->aaModules);
unset($this->apModules);
unset($this->fields);
}
}

View File

@ -0,0 +1,268 @@
<?php
// phpcs:disable Squiz.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
/**
* Cluster module entity class.
*
* @category Class
* @package Pandora FMS
* @subpackage Community
* @version 1.0.0
* @license See below
*
* ______ ___ _______ _______ ________
* | __ \.-----.--.--.--| |.-----.----.-----. | ___| | | __|
* | __/| _ | | _ || _ | _| _ | | ___| |__ |
* |___| |___._|__|__|_____||_____|__| |___._| |___| |__|_|__|_______|
*
* ============================================================================
* Copyright (c) 2005-2021 Artica Soluciones Tecnologicas
* Please see http://pandorafms.org for full contribution list
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation for version 2.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* ============================================================================
*/
// Begin.
namespace PandoraFMS;
use PandoraFMS\Entity;
use PandoraFMS\Module;
/**
* Represents AA and AP modules entity from a cluster.
*/
class ClusterModule extends Entity
{
/**
* Associated module.
*
* @var PandoraFMS\Module
*/
private $module;
/**
* Builds a PandoraFMS\ClusterViewer\ClusterModule object from a id.
*
* @param integer $id ClusterModule Id.
*
* @throws \Exception On error.
*/
public function __construct(?int $id=null)
{
if (is_numeric($id) === true && $id > 0) {
try {
parent::__construct('tcluster_item', ['id' => $id]);
} catch (\Exception $e) {
throw new \Exception('ClusterModule id not found.');
}
// Get module.
$this->module = Module::search(
[
'nombre' => $this->name(),
'custom_integer_1' => $this->id_cluster(),
],
1
);
} else {
parent::__construct('tcluster_item');
$this->module = new Module();
}
}
/**
* Returns current object as array.
*
* @return array Of fields.
*/
public function toArray()
{
return $this->fields;
}
/**
* Associates a module to this clusterModule.
*
* @param array $params Module parameters.
*
* @return void
*/
public function setModule(array $params)
{
$this->module = new Module();
foreach ($params as $k => $v) {
$this->module->{$k}($v);
}
}
/**
* Associates a module to this clusterModule.
*
* @param PandoraFMS\Module $module Module definition.
*
* @return void
*/
public function setModuleObject(Module $module)
{
$this->module = $module;
}
/**
* Returns current module.
*
* @return PandoraFMS\Module Object.
*/
public function getModule()
{
return $this->module;
}
/**
* Saves or retrieves value of warning_limit.
*
* @param float|null $value Warning value.
*
* @return mixed Value or empty.
*/
public function warning_limit(?float $value=null)
{
if ($value !== null) {
$this->fields['warning_limit'] = $value;
if ($this->module !== null) {
$this->module->min_warning($value);
}
} else {
return $this->fields['warning_limit'];
}
}
/**
* Saves or retrieves value of critical_limit.
*
* @param float|null $value Critical value.
*
* @return mixed Value or empty.
*/
public function critical_limit(?float $value=null)
{
if ($value !== null) {
$this->fields['critical_limit'] = $value;
if ($this->module !== null) {
$this->module->min_critical($value);
}
} else {
return $this->fields['critical_limit'];
}
}
/**
* Save ClusterModule.
*
* @return boolean True if success, false if error.
* @throws \Exception On db error.
*/
public function save()
{
$values = $this->fields;
if ($this->module === null) {
return false;
}
if (method_exists($this->module, 'save') === false) {
throw new \Exception(
__METHOD__.' error: Cluster module "'.$this->name().'" invalid.'
);
}
if (isset($values['id']) === true && $values['id'] > 0) {
// Update.
$rs = \db_process_sql_update(
'tcluster_item',
$values,
['id' => $this->fields['id']]
);
if ($rs === false) {
global $config;
throw new \Exception(
__METHOD__.' error: '.$config['dbconnection']->error
);
}
if ($this->module === null) {
throw new \Exception(
__METHOD__.' error: Cluster module "'.$this->name().'" is not defined'
);
}
// Update reference.
$this->module->custom_integer_2($this->fields['id']);
// Update module.
$this->module->save();
return true;
} else {
// New.
$rs = \db_process_sql_insert(
'tcluster_item',
$values
);
if ($rs === false) {
global $config;
throw new \Exception(
__METHOD__.' error: '.$config['dbconnection']->error
);
}
$this->fields['id'] = $rs;
// Update reference.
$this->module->custom_integer_2($this->fields['id']);
// Update module.
$this->module->save();
return true;
}
return false;
}
/**
* Erases this object and its module.
*
* @return void
*/
public function delete()
{
if (method_exists($this->module, 'delete') === true) {
$this->module->delete();
}
unset($this->fields);
unset($this->module);
}
}

View File

@ -0,0 +1,767 @@
<?php
/**
* Cluster view main class.
*
* @category Class
* @package Pandora FMS
* @subpackage Community
* @version 1.0.0
* @license See below
*
* ______ ___ _______ _______ ________
* | __ \.-----.--.--.--| |.-----.----.-----. | ___| | | __|
* | __/| _ | | _ || _ | _| _ | | ___| |__ |
* |___| |___._|__|__|_____||_____|__| |___._| |___| |__|_|__|_______|
*
* ============================================================================
* Copyright (c) 2005-2021 Artica Soluciones Tecnologicas
* Please see http://pandorafms.org for full contribution list
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation for version 2.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* ============================================================================
*/
// Begin.
namespace PandoraFMS\ClusterViewer;
use PandoraFMS\View;
use PandoraFMS\Group;
use PandoraFMS\Cluster;
/**
* Class to handle Cluster view operations.
*/
class ClusterManager
{
/**
* Url of controller.
*
* @var string
*/
public $ajaxController;
/**
* Url (main).
*
* @var string
*/
public $url;
/**
* Number of clusters defined.
*
* @var integer
*/
private static $count;
/**
* Constructor
*
* @param string $ajax_page Path to ajax controller.
* @param string $url Url.
*/
public function __construct(
string $ajax_page='operation/cluster/cluster',
string $url='index.php?sec=estado&sec2=operation/cluster/cluster'
) {
global $config;
check_login();
if (! check_acl($config['id_user'], 0, 'AR')
&& ! check_acl($config['id_user'], 0, 'AW')
) {
db_pandora_audit(
AUDIT_LOG_ACL_VIOLATION,
'Trying to access cluster viewer'
);
if (is_ajax()) {
echo json_encode(['error' => 'noaccess']);
} else {
include 'general/noaccess.php';
}
exit;
}
$this->ajaxController = $ajax_page;
$this->url = $url;
}
/**
* Main program starts here.
*
* @return void
*/
public function run()
{
$operation = get_parameter('op', '');
switch ($operation) {
case 'new':
case 'update':
$this->showClusterEditor($operation);
break;
case 'view':
$this->showCluster();
break;
case 'delete':
$this->deleteCluster();
break;
case 'force':
$this->forceCluster();
break;
default:
$n_clusters = $this->getCount();
if ($n_clusters > 0) {
$this->showList();
} else {
$this->showWelcome();
}
break;
}
}
/**
* Prints error message
*
* @param string $msg Message.
*
* @return void
*/
public function error(string $msg)
{
if (is_ajax()) {
echo json_encode(
['error' => $msg]
);
} else {
ui_print_error_message($msg);
}
}
/**
* Loads view 'first tasks' for cluster view.
* Old style.
*
* @return void
*/
public function showWelcome()
{
global $config;
include_once $config['homedir'].'/general/first_task/cluster_builder.php';
}
/**
* Prepares available clusters definition for current users and loads view.
*
* @param string|null $msg Message (if any).
*
* @return void
*/
public function showList(?string $msg='')
{
global $config;
// Extract data.
$n_clusters = $this->getCount();
if ($n_clusters > 0) {
$clusters = $this->getAll();
} else {
$clusters = [];
}
View::render(
'cluster/list',
[
'message' => $msg,
'config' => $config,
'model' => $this,
'n_clusters' => $n_clusters,
'clusters' => $clusters,
]
);
}
/**
* Show cluster information.
*
* @param string|null $msg Message (if any).
*
* @return void
*/
public function showCluster(?string $msg=null)
{
global $config;
$err = '';
$id = get_parameter('id', null);
try {
$cluster = new Cluster($id);
} catch (\Exception $e) {
$err = ui_print_error_message(
__('Cluster not found: '.$e->getMessage()),
'',
true
);
}
if ($cluster->agent()->id_agente() === null) {
// Failed.
$err = ui_print_error_message(
__('Cluster agent not found: '),
'',
true
);
$critical = true;
}
View::render(
'cluster/view',
[
'message' => $msg,
'error' => $err,
'config' => $config,
'model' => $this,
'cluster' => $cluster,
'critical' => $critical,
]
);
}
/**
* Removes a cluster from db.
*
* @return void
*/
public function deleteCluster()
{
$msg = '';
$id = get_parameter('id', null);
try {
$cluster = new Cluster($id);
$cluster->delete();
unset($cluster);
} catch (\Exception $e) {
$msg = ui_print_error_message(
__('Error while deleting, reason: %s', $e->getMessage()),
'',
true
);
}
if (empty($msg) === true) {
$msg = ui_print_success_message(
__('Cluster successfully deleted.'),
'',
true
);
}
$this->showList($msg);
}
/**
* Force cluster execution.
*
* @return void
*/
public function forceCluster()
{
$msg = '';
$id = get_parameter('id', null);
try {
$cluster = new Cluster($id);
$cluster->force();
unset($cluster);
} catch (\Exception $e) {
$msg = ui_print_error_message(
__('Error while forcing, reason: %s', $e->getMessage()),
'',
true
);
}
if (empty($msg) === true) {
$msg = ui_print_success_message(
__('Cluster successfully forced.'),
'',
true
);
}
$this->showCluster($msg);
}
/**
* Shows editor for target cluster (or new one).
*
* @param string $operation Current operation.
*
* @return void
*/
public function showClusterEditor(string $operation)
{
global $config;
if (!check_acl($config['id_user'], 0, 'AW')) {
db_pandora_audit(
AUDIT_LOG_ACL_VIOLATION,
'Trying to create clusters'
);
include 'general/noaccess.php';
} else {
$wizard = new ClusterWizard(
$this->url,
$operation
);
$wizard->run();
}
}
/**
* Returns number of clusters registered.
*
* @return integer
*/
public function getCount()
{
if (isset($this->count) !== true) {
$this->count = $this->getAll('count');
}
return $this->count;
}
/**
* Return all cluster definitons matching given filters.
*
* @param mixed $fields Fields array or 'count' keyword to retrieve
* count, null or default to use default ones.
* @param array $filter Filters to be applied.
* @param integer $offset Offset (pagination).
* @param integer $limit Limit (pagination).
* @param string $order Sort order.
* @param string $sort_field Sort field.
*
* @return array With all results or false if error.
* @throws \Exception On error.
*/
public static function getAll(
$fields=null,
array $filter=[],
?int $offset=null,
?int $limit=null,
?string $order=null,
?string $sort_field=null
) {
$sql_filters = [];
$order_by = '';
$pagination = '';
global $config;
if (is_array($filter) === false) {
throw new \Exception('[ClusterManager::getAll] Filter must be an array.');
}
if (empty($filter['id_group']) === false
&& (int) $filter['id_group'] !== 0
) {
$sql_filters[] = sprintf(
' AND tc.`group` = %d',
$filter['id_group']
);
}
if (empty($filter['free_search']) === false) {
$topic = io_safe_input($filter['free_search']);
$sql_filters[] = sprintf(
' AND (lower(tc.name) like lower("%%%s%%")
OR lower(tc.description) like lower("%%%s%%") ) ',
$topic,
$topic
);
}
$count = false;
if (is_array($fields) === false && $fields === 'count') {
$fields = ['tc.*'];
$count = true;
} else if (is_array($fields) === false) {
// Default values.
$fields = [
'tc.*',
'(SELECT COUNT(*) FROM `tcluster_agent` WHERE `id_cluster` = tc.`id`) as `nodes`',
'tas.known_status',
];
}
if (isset($order) === true) {
$dir = 'asc';
if ($order === 'desc') {
$dir = 'desc';
};
if ($sort_field === 'type') {
$sort_field = 'cluster_type';
}
if (in_array(
$sort_field,
[
'name',
'description',
'group',
'cluster_type',
'nodes',
'known_status',
]
) === true
) {
$order_by = sprintf(
'ORDER BY `%s` %s',
$sort_field,
$dir
);
}
}
if (isset($limit) === true && $limit > 0
&& isset($offset) === true && $offset >= 0
) {
$pagination = sprintf(
' LIMIT %d OFFSET %d ',
$limit,
$offset
);
}
$sql = sprintf(
'SELECT %s
FROM tcluster tc
LEFT JOIN tagente ta
ON tc.id_agent = ta.id_agente
LEFT JOIN tagente_modulo tam
ON tam.id_agente = tc.id_agent
AND tam.nombre = "%s"
LEFT JOIN tagente_estado tas
ON tam.id_agente_modulo=tas.id_agente_modulo
WHERE 1=1
%s
%s
%s',
join(',', $fields),
io_safe_input('Cluster status'),
join(' ', $sql_filters),
$order_by,
$pagination
);
if ($count === true) {
$sql = sprintf('SELECT count(*) as n FROM ( %s ) tt', $sql);
// Counter.. All data.
return db_get_value_sql($sql);
}
return db_get_all_rows_sql($sql);
}
/**
* Return data for datatables painting.
*
* @return void
* @throws \Exception On Error.
*/
public function draw()
{
global $config;
// Datatables offset, limit and order.
$filter = get_parameter('filter', []);
$start = get_parameter('start', 0);
$length = get_parameter('length', $config['block_size']);
$order = get_datatable_order(true);
try {
ob_start();
$fields = [
'tc.*',
'(SELECT COUNT(*) FROM `tcluster_agent` WHERE `id_cluster` = tc.`id`) as `nodes`',
'tas.known_status',
];
// Retrieve data.
$data = self::getAll(
// Fields.
$fields,
// Filter.
$filter,
// Offset.
$start,
// Limit.
$length,
// Order.
$order['direction'],
// Sort field.
$order['field']
);
// Retrieve counter.
$count = self::getAll(
'count',
$filter
);
if ($data) {
$data = array_reduce(
$data,
function ($carry, $item) {
global $config;
// Transforms array of arrays $data into an array
// of objects, making a post-process of certain fields.
$tmp = (object) $item;
$manage = check_acl(
$config['id_user'],
$tmp->group,
'AW',
true
);
$tmp->name = '<b><a href="'.ui_get_full_url(
$this->url.'&op=view&id='.$tmp->id
).'">'.$tmp->name.'</a></b>';
if (empty($tmp->group) === true) {
$tmp->group = __('Not set');
} else {
$tmp->group = ui_print_group_icon(
$tmp->group,
true
);
}
// Type.
if ($tmp->cluster_type === 'AA') {
$tmp->type = __('Active-Active');
} else if ($tmp->cluster_type === 'AP') {
$tmp->type = __('Active-Passive');
} else {
$tmp->type = __('Unknown');
}
// Status.
$tmp->known_status = ui_print_module_status(
$tmp->known_status,
true
);
// Options. View.
$tmp->options = '<a href="';
$tmp->options .= ui_get_full_url(
$this->url.'&op=view&id='.$tmp->id
);
$tmp->options .= '">';
$tmp->options .= html_print_image(
'images/operation.png',
true,
[
'title' => __('View'),
'class' => 'invert_filter',
]
);
$tmp->options .= '</a>';
if ($manage) {
// Options. Edit.
$tmp->options .= '<a href="';
$tmp->options .= ui_get_full_url(
$this->url.'&op=update&id='.$tmp->id
);
$tmp->options .= '">';
$tmp->options .= html_print_image(
'images/config.png',
true,
[
'title' => __('Edit'),
'class' => 'invert_filter',
]
);
$tmp->options .= '</a>';
// Options. Delete.
$tmp->options .= '<a href="';
$tmp->options .= ui_get_full_url(
$this->url.'&op=delete&id='.$tmp->id
);
$tmp->options .= '">';
$tmp->options .= html_print_image(
'images/cross.png',
true,
[
'title' => __('Delete'),
'class' => 'invert_filter',
]
);
$tmp->options .= '</a>';
}
$carry[] = $tmp;
return $carry;
}
);
}
// Datatables format: RecordsTotal && recordsfiltered.
echo json_encode(
[
'data' => $data,
'recordsTotal' => $count,
'recordsFiltered' => $count,
]
);
// Capture output.
$response = ob_get_clean();
} catch (\Exception $e) {
echo json_encode(['error' => $e->getMessage()]);
exit;
}
// If not valid, show error with issue.
json_decode($response);
if (json_last_error() == JSON_ERROR_NONE) {
// If valid dump.
echo $response;
} else {
echo json_encode(
['error' => $response]
);
}
}
/**
* Provides data for wizard. Ajax method.
*
* @return void
*/
public function getAgentsFromGroup()
{
$side = get_parameter('side', null);
$id = get_parameter('id', null);
$group_id = get_parameter('group_id', 0);
$group_recursion = (bool) get_parameter('group_recursion', 0);
$groups = [];
if ($group_recursion === true) {
$groups = groups_get_children_ids($group_id, true);
} else {
$groups = $group_id;
}
if ($side === 'left') {
// Available agents.
$agents = agents_get_agents(
[ 'id_grupo' => $groups ],
[
'id_agente',
'alias',
]
);
$agents = array_reduce(
$agents,
function ($carry, $item) {
$carry[$item['id_agente']] = io_safe_output($item['alias']);
return $carry;
}
);
} else if ($side === 'right') {
// Selected agents.
$cluster = new Cluster($id);
$agents = $cluster->getMembers();
$agents = array_reduce(
$agents,
function ($carry, $item) use ($groups) {
if (in_array($item->id_grupo(), $groups) === true) {
$carry[$item->id_agente()] = io_safe_output(
$item->alias()
);
}
return $carry;
}
);
}
if (empty($agents) === true) {
echo '[]';
} else {
// Dump response.
echo json_encode($agents);
}
}
/**
* Returns a goBack form structure.
*
* @return array Form structure.
*/
public function getGoBackForm()
{
$form['form']['action'] = $this->url;
$form['form']['method'] = 'POST';
$form['form']['id'] = 'go-back-form';
$form['inputs'] = [
[
'arguments' => [
'name' => 'submit',
'label' => __('Go back'),
'type' => 'submit',
'attributes' => 'class="sub cancel"',
'return' => true,
],
],
];
return $form;
}
}

File diff suppressed because it is too large Load Diff

View File

@ -846,17 +846,15 @@ foreach ($agents as $agent) {
$data[0] .= '<div class="agentleft_'.$agent['id_agente'].'" style="visibility: hidden; clear: left;">';
if ($agent['id_os'] == CLUSTER_OS_ID) {
if (enterprise_installed()) {
$cluster = PandoraFMS\Enterprise\Cluster::loadFromAgentId(
$agent['id_agente']
);
$url = 'index.php?sec=reporting&sec2='.ENTERPRISE_DIR;
$url .= '/operation/cluster/cluster';
$url = ui_get_full_url(
$url.'&op=view&id='.$cluster->id()
);
$data[0] .= '<a href="'.$url.'">'.__('View').'</a>';
}
$cluster = PandoraFMS\Cluster::loadFromAgentId(
$agent['id_agente']
);
$url = 'index.php?sec=reporting&sec2=';
$url .= 'operation/cluster/cluster';
$url = ui_get_full_url(
$url.'&op=view&id='.$cluster->id()
);
$data[0] .= '<a href="'.$url.'">'.__('View').'</a>';
} else {
$data[0] .= '<a href="index.php?sec=estado&sec2=operation/agentes/ver_agente&id_agente='.$agent['id_agente'].'">'.__('View').'</a>';
}
@ -865,17 +863,15 @@ foreach ($agents as $agent) {
$data[0] .= ' | ';
if ($agent['id_os'] == CLUSTER_OS_ID) {
if (enterprise_installed()) {
$cluster = PandoraFMS\Enterprise\Cluster::loadFromAgentId(
$agent['id_agente']
);
$url = 'index.php?sec=reporting&sec2='.ENTERPRISE_DIR;
$url .= '/operation/cluster/cluster';
$url = ui_get_full_url(
$url.'&op=update&id='.$cluster->id()
);
$data[0] .= '<a href="'.$url.'">'.__('Edit').'</a>';
}
$cluster = PandoraFMS\Cluster::loadFromAgentId(
$agent['id_agente']
);
$url = 'index.php?sec=reporting&sec2=';
$url .= 'operation/cluster/cluster';
$url = ui_get_full_url(
$url.'&op=update&id='.$cluster->id()
);
$data[0] .= '<a href="'.$url.'">'.__('Edit').'</a>';
} else {
$data[0] .= '<a href="index.php?sec=gagente&amp;sec2=godmode/agentes/configurar_agente&amp;id_agente='.$agent['id_agente'].'">'.__('Edit').'</a>';
}

View File

@ -0,0 +1,70 @@
<?php
/**
* Cluster view entrypoint.
*
* @category View
* @package Pandora FMS
* @subpackage Community
* @version 1.0.0
* @license See below
*
* ______ ___ _______ _______ ________
* | __ \.-----.--.--.--| |.-----.----.-----. | ___| | | __|
* | __/| _ | | _ || _ | _| _ | | ___| |__ |
* |___| |___._|__|__|_____||_____|__| |___._| |___| |__|_|__|_______|
*
* ============================================================================
* Copyright (c) 2005-2021 Artica Soluciones Tecnologicas
* Please see http://pandorafms.org for full contribution list
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation for version 2.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* ============================================================================
*/
// Begin.
global $config;
require 'vendor/autoload.php';
use PandoraFMS\ClusterViewer\ClusterManager;
$ajaxPage = 'operation/cluster/cluster';
// Control call flow.
try {
// User access and validation is being processed on class constructor.
$obj = new ClusterManager($ajaxPage);
} catch (Exception $e) {
if (is_ajax()) {
echo json_encode(['error' => '[ClusterManager]'.$e->getMessage() ]);
exit;
} else {
echo '[ClusterManager]'.$e->getMessage();
}
// Stop this execution, but continue 'globally'.
return;
}
// AJAX controller.
if (is_ajax()) {
$method = get_parameter('method');
if (method_exists($obj, $method) === true) {
$obj->{$method}();
} else {
$obj->error('Method not found. [ClusterManager::'.$method.']');
}
// Stop any execution.
exit;
} else {
// Run.
$obj->run();
}

View File

@ -147,7 +147,12 @@ if ($access_console_node === true) {
$sub['snmpconsole']['subtype'] = 'nolink';
}
enterprise_hook('cluster_menu');
if (check_acl($config['id_user'], 0, 'AR')) {
$sub['operation/cluster/cluster']['text'] = __('Cluster View');
$sub['operation/cluster/cluster']['id'] = 'cluster';
$sub['operation/cluster/cluster']['refr'] = 0;
}
enterprise_hook('aws_menu');
enterprise_hook('SAP_view');

View File

@ -0,0 +1,129 @@
<?php
/**
* Cluster View: Edit
*
* @category View
* @package Pandora FMS
* @subpackage Cluster View
* @version 1.0.0
* @license See below
*
* ______ ___ _______ _______ ________
* | __ \.-----.--.--.--| |.-----.----.-----. | ___| | | __|
* | __/| _ | | _ || _ | _| _ | | ___| |__ |
* |___| |___._|__|__|_____||_____|__| |___._| |___| |__|_|__|_______|
*
* ============================================================================
* Copyright (c) 2005-2021 Artica Soluciones Tecnologicas
* Please see http://pandorafms.org for full contribution list
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation for version 2.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* ============================================================================
*/
// Begin.
// Prepare header and breadcrums.
$i = 0;
$bc = [];
$extra = '&op='.$wizard->operation;
if ($wizard->id !== null) {
$extra .= '&id='.$wizard->id;
}
$bc[] = [
'link' => $wizard->parentUrl,
'label' => __('Cluster list'),
'selected' => false,
];
$labels = $wizard->getLabels();
foreach ($labels as $key => $label) {
$bc[] = [
'link' => $wizard->url.(($key >= 0) ? $extra.'&page='.$key : ''),
'label' => __($label),
'selected' => ($wizard->page == $key),
];
}
$wizard->prepareBreadcrum($bc);
$header_str = __(ucfirst($wizard->getOperation())).' ';
$header_str .= (($cluster->name() !== null) ? $cluster->name() : __('cluster '));
$header_str .= ' &raquo; '.__($labels[$wizard->page]);
// Header.
$buttons = [];
$main_page = '<a href="'.$wizard->parentUrl.'">';
$main_page .= html_print_image(
'images/list.png',
true,
[
'title' => __('Cluster list'),
'class' => 'invert_filter',
]
);
$main_page .= '</a>';
$buttons = [
[
'active' => false,
'text' => $main_page,
],
];
if ($cluster !== null) {
if ($cluster->id() !== null) {
$view = '<a href="'.$wizard->parentUrl.'&op=view&id='.$cluster->id().'">';
$view .= html_print_image(
'images/operation.png',
true,
[
'title' => __('View this cluster'),
'class' => 'invert_filter',
]
);
$view .= '</a>';
$buttons[] = [
'active' => false,
'text' => $view,
];
}
}
ui_print_page_header(
$header_str,
'',
false,
'cluster_view',
true,
// Buttons.
$buttons,
false,
'',
GENERIC_SIZE_TEXT,
'',
$wizard->printHeader(true)
);
// Check if any error ocurred.
if (empty($wizard->errMessages) === false) {
foreach ($wizard->errMessages as $msg) {
ui_print_error_message(__($msg));
}
}
if (empty($form) === false) {
// Print form (prepared in ClusterWizard).
HTML::printForm($form, false, ($wizard->page < 6));
}
// Print always go back button.
HTML::printForm($wizard->getGoBackForm(), false);

View File

@ -0,0 +1,132 @@
<?php
/**
* Cluster View: List
*
* @category View
* @package Pandora FMS
* @subpackage Cluster View
* @version 1.0.0
* @license See below
*
* ______ ___ _______ _______ ________
* | __ \.-----.--.--.--| |.-----.----.-----. | ___| | | __|
* | __/| _ | | _ || _ | _| _ | | ___| |__ |
* |___| |___._|__|__|_____||_____|__| |___._| |___| |__|_|__|_______|
*
* ============================================================================
* Copyright (c) 2005-2021 Artica Soluciones Tecnologicas
* Please see http://pandorafms.org for full contribution list
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation for version 2.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* ============================================================================
*/
// Header.
\ui_print_page_header(
__('Monitoring').' &raquo; '.__('Clusters'),
'images/chart.png',
false,
'',
false
);
if (empty($message) === false) {
echo $message;
}
// Datatables list.
try {
$columns = [
'name',
'description',
'group',
'type',
'nodes',
'known_status',
[
'text' => 'options',
'class' => 'action_buttons',
],
];
$column_names = [
__('Name'),
__('Description'),
__('Group'),
__('Type'),
__('Nodes'),
__('Status'),
__('Options'),
];
$tableId = 'clusters';
// Load datatables user interface.
ui_print_datatable(
[
'id' => $tableId,
'class' => 'info_table',
'style' => 'width: 100%',
'columns' => $columns,
'column_names' => $column_names,
'ajax_url' => $model->ajaxController,
'ajax_data' => ['method' => 'draw'],
'no_sortable_columns' => [-1],
'order' => [
'field' => 'known_status',
'direction' => 'asc',
],
'search_button_class' => 'sub filter float-right',
'form' => [
'inputs' => [
[
'label' => __('Filter group'),
'name' => 'id_group',
'returnAllGroup' => true,
'privilege' => 'AR',
'type' => 'select_groups',
'return' => true,
'size' => '250px',
],
[
'label' => __('Free search'),
'type' => 'text',
'class' => 'mw250px',
'id' => 'free_search',
'name' => 'free_search',
],
],
],
]
);
} catch (Exception $e) {
echo $e->getMessage();
}
if (check_acl($config['id_user'], 0, 'AW')) {
HTML::printForm(
[
'form' => [
'method' => 'POST',
'action' => ui_get_full_url($model->url.'&op=new'),
],
'inputs' => [
[
'class' => 'w100p',
'arguments' => [
'name' => 'submit',
'label' => __('New cluster'),
'type' => 'submit',
'attributes' => 'class="sub next"',
'return' => true,
],
],
],
]
);
}

View File

@ -0,0 +1,453 @@
<?php
/**
* Cluster View: View
*
* @category View
* @package Pandora FMS
* @subpackage Cluster View
* @version 1.0.0
* @license See below
*
* ______ ___ _______ _______ ________
* | __ \.-----.--.--.--| |.-----.----.-----. | ___| | | __|
* | __/| _ | | _ || _ | _| _ | | ___| |__ |
* |___| |___._|__|__|_____||_____|__| |___._| |___| |__|_|__|_______|
*
* ============================================================================
* Copyright (c) 2005-2021 Artica Soluciones Tecnologicas
* Please see http://pandorafms.org for full contribution list
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation for version 2.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* ============================================================================
*/
ui_require_css_file('discovery');
ui_require_css_file('agent_view');
ui_require_css_file('cluster_view');
$html = new HTML();
// Begin.
// Prepare header and breadcrums.
$i = 0;
$bc = [];
$bc[] = [
'link' => $model->url,
'label' => __('Cluster list'),
'selected' => false,
];
$bc[] = [
'link' => $model->url.'&op=view&id='.$cluster->id(),
'label' => __('Cluster details'),
'selected' => true,
];
$html->prepareBreadcrum($bc);
// Header.
$main_page = '<a href="'.$model->url.'">';
$main_page .= html_print_image(
'images/list.png',
true,
[
'title' => __('Cluster list'),
'class' => 'invert_filter',
]
);
$main_page .= '</a>';
$edit = '<a href="'.$model->url.'&op=update&id='.$cluster->id().'">';
$edit .= html_print_image(
'images/setup.png',
true,
[
'title' => __('Edit this cluster'),
'class' => 'invert_filter',
]
);
$edit .= '</a>';
ui_print_page_header(
__('Cluster details').' &raquo; '.$cluster->name(),
'',
false,
// Help link.
'cluster_view',
true,
// Buttons.
[
[
'active' => false,
'text' => $main_page,
],[
'active' => false,
'text' => $edit,
],
],
false,
'',
GENERIC_SIZE_TEXT,
'',
$html->printHeader(true)
);
if (empty($error) === false) {
echo $error;
}
if (empty($message) === false) {
echo $message;
}
if ($critical === true) {
// Print always go back button.
HTML::printForm($model->getGoBackForm(), false);
return;
}
/*
*
* All this block has been retrieved from 'estado_generalagente.php' as
* described in issue #5755.
*
*/
/*
*
*
* CLUSTER AGENT DETAILS.
*
*/
// Prepare information for view.
$alive_animation = agents_get_status_animation(
agents_get_interval_status($cluster->agent()->toArray(), false)
);
$agent_name = ui_print_agent_name(
$cluster->agent()->id_agente(),
true,
500,
'font-size: medium;font-weight:bold',
true,
'',
'',
false,
false
);
$in_planned_downtime = db_get_sql(
'SELECT executed FROM tplanned_downtime
INNER JOIN tplanned_downtime_agents
ON tplanned_downtime.id = tplanned_downtime_agents.id_downtime
WHERE tplanned_downtime_agents.id_agent = '.$cluster->agent()->id_agente().' AND tplanned_downtime.executed = 1'
);
if ($cluster->agent()->disabled()) {
if ($in_planned_downtime) {
$agent_name = '<em>'.$agent_name.ui_print_help_tip(__('Disabled'), true);
} else {
$agent_name = '<em>'.$agent_name.'</em>'.ui_print_help_tip(__('Disabled'), true);
}
} else if ($cluster->agent()->quiet()) {
if ($in_planned_downtime) {
$agent_name = "<em'>".$agent_name.'&nbsp;'.html_print_image('images/dot_blue.png', true, ['border' => '0', 'title' => __('Quiet'), 'alt' => '']);
} else {
$agent_name = "<em'>".$agent_name.'&nbsp;'.html_print_image('images/dot_blue.png', true, ['border' => '0', 'title' => __('Quiet'), 'alt' => '']).'</em>';
}
} else {
$agent_name = $agent_name;
}
if ($in_planned_downtime && !$cluster->agent()->disabled() && !$cluster->agent()->quiet()) {
$agent_name .= '<em>&nbsp;'.ui_print_help_tip(
__('Agent in scheduled downtime'),
true,
'images/minireloj-16.png'
).'</em>';
} else if (($in_planned_downtime && !$cluster->agent()->disabled())
|| ($in_planned_downtime && !$cluster->agent()->quiet())
) {
$agent_name .= '&nbsp;'.ui_print_help_tip(
__('Agent in scheduled downtime'),
true,
'images/minireloj-16.png'
).'</em>';
}
$table_agent_header = '<div class="agent_details_agent_alias">';
$table_agent_header .= $agent_name;
$table_agent_header .= '</div>';
$table_agent_header .= '<div class="agent_details_agent_name">';
if (!$config['show_group_name']) {
$table_agent_header .= ui_print_group_icon(
$cluster->agent()->id_grupo(),
true,
'groups_small',
'padding-right: 6px;'
);
}
$table_agent_header .= '</div>';
$status_img = agents_detail_view_status_img(
$cluster->agent()->critical_count(),
$cluster->agent()->warning_count(),
$cluster->agent()->unknown_count(),
$cluster->agent()->total_count(),
$cluster->agent()->notinit_count()
);
$table_agent_header .= '<div class="icono_right">'.$status_img.'</div>';
$table_agent_header .= '&nbsp;&nbsp;';
$table_agent_header .= '<a href="'.$model->url.'&op=force&id='.$cluster->id();
$table_agent_header .= '">'.html_print_image(
'images/target.png',
true,
[
'title' => __('Force cluster status calculation'),
'alt' => '',
'class' => 'invert_filter',
]
).'</a>';
// Fixed width non interactive charts.
$status_chart_width = 180;
$graph_width = 180;
$table_agent_graph = '<div id="status_pie" style="width: '.$status_chart_width.'px;">';
$table_agent_graph .= graph_agent_status(
$cluster->agent()->id_agente(),
$graph_width,
$graph_width,
true,
false,
false,
true
);
$table_agent_graph .= '</div>';
$table_agent_os = '<p>'.ui_print_os_icon(
$cluster->agent()->id_os(),
false,
true,
true,
false,
false,
false,
['title' => __('OS').': '.get_os_name($cluster->agent()->id_os())]
);
$table_agent_os .= (empty($cluster->agent()->os_version()) === true) ? get_os_name((int) $cluster->agent()->id_os()) : $cluster->agent()->os_version().'</p>';
$addresses = agents_get_addresses($cluster->agent()->id_agente());
$address = agents_get_address($cluster->agent()->id_agente());
foreach ($addresses as $k => $add) {
if ($add == $address) {
unset($addresses[$k]);
}
}
if (empty($address) === false) {
$table_agent_ip = '<p>'.html_print_image(
'images/world.png',
true,
[
'title' => __('IP address'),
'class' => 'invert_filter',
]
);
$table_agent_ip .= '<span class="align-top inline">';
$table_agent_ip .= empty($address) ? '<em>'.__('N/A').'</em>' : $address;
$table_agent_ip .= '</span></p>';
}
$table_agent_description = '<p>'.html_print_image(
'images/list.png',
true,
[
'title' => __('Description'),
'class' => 'invert_filter',
]
);
$table_agent_description .= '<span class="align-top inline">';
$table_agent_description .= empty(
$cluster->description()
) ? '<em>'.__('N/A').'</em>' : $cluster->description();
$table_agent_description .= '</span></p>';
$table_agent_count_modules = reporting_tiny_stats(
$cluster->agent()->toArray(),
true,
'agent',
// Useless.
':',
true
);
$table_agent_version = '<p>'.html_print_image(
'images/version.png',
true,
[
'title' => __('Agent Version'),
'class' => 'invert_filter',
]
);
$table_agent_version .= '<span class="align-top inline">';
$table_agent_version .= empty($cluster->agent()->agent_version()) ? '<i>'.__('Cluster agent').'</i>' : $cluster->agent()->agent_version();
$table_agent_version .= '</span></p>';
/*
*
* MAP
*
*/
$nodes = $cluster->getNodes();
$font_size = 20;
$width = '45%';
$height = '500';
$node_radius = 40;
// Generate map.
$map_manager = new NetworkMap(
[
'nodes' => $nodes,
'no_pandora_node' => 1,
'pure' => 1,
'map_options' => [
'generation_method' => LAYOUT_SPRING1,
'font_size' => $font_size,
'node_radius' => $node_radius,
'height' => $height,
'width' => '100%',
'tooltip' => true,
'size_image' => 50,
'z_dash' => 0.5,
'map_filter' => [
'node_sep' => 7,
'node_radius' => 50,
'x_offs' => 130,
'y_offs' => -70,
],
],
]
);
/*
*
* EVENTS 24h
*
*/
$table_events = '<div class="white_table_graph" id="table_events">';
$table_events .= '<div class="white_table_graph_header">';
$table_events .= html_print_image(
'images/arrow_down_green.png',
true
);
$table_events .= '<span>';
$table_events .= __('Events (Last 24h)');
$table_events .= '</span>';
$table_events .= '</div>';
$table_events .= '<div class="white_table_graph_content white-table-graph-content">';
$table_events .= graph_graphic_agentevents(
$cluster->agent()->id_agente(),
95,
70,
SECONDS_1DAY,
'',
true,
true,
500
);
$table_events .= '</div>';
$table_events .= '</div>';
?>
<div id="agent_details_first_row" class="w100p cluster-agent-data">
<div class="box-shadow agent_details_col agent_details_col_left">
<div class="agent_details_header">
<?php echo $table_agent_header; ?>
</div>
<div class="agent_details_content">
<div class="agent_details_graph">
<?php echo $table_agent_graph; ?>
<div class="agent_details_bullets">
<?php echo $table_agent_count_modules; ?>
</div>
</div>
<div class="agent_details_info">
<?php
echo $alive_animation;
echo $table_agent_os;
echo $table_agent_ip;
echo $table_agent_version;
echo $table_agent_description;
?>
</div>
</div>
</div>
<div class="box-shadow agent_details_col agent_details_col_right">
<div class="cluster-map">
<?php $map_manager->printMap(); ?>
</div>
</div>
</div>
<div class="w100p cluster-events-graph">
<?php echo $table_events; ?>
</div>
<div id='cluster-modules' class="w100p modules">
<?php
$id_agente = $cluster->agent()->id_agente();
require_once $config['homedir'].'/operation/agentes/estado_monitores.php';
?>
</div>
<?php
HTML::printForm(
[
'form' => [
'action' => $model->url.'&op=view&id='.$cluster->id(),
'method' => 'POST',
],
'inputs' => [
[
'arguments' => [
'name' => 'submit',
'label' => __('Reload'),
'type' => 'submit',
'attributes' => 'class="sub cancel"',
'return' => true,
],
],
],
],
false
);
echo '<br/>';
// Print always go back button.
HTML::printForm($model->getGoBackForm(), false);

View File

@ -280,6 +280,9 @@ our @EXPORT = qw(
notification_set_targets
notification_get_users
notification_get_groups
exec_cluster_aa_module
exec_cluster_ap_module
exec_cluster_status_module
);
# Some global variables
@ -7275,6 +7278,175 @@ sub pandora_snmptrapd_still_working ($$) {
}
}
################################################################################
# Execute a cluster status module.
################################################################################
sub exec_cluster_status_module ($$$$) {
my ($pa_config, $module, $server_id, $dbh) = @_;
# Execute cluster modules.
my @modules = get_db_rows($dbh,
'SELECT *
FROM tagente_modulo
WHERE tagente_modulo.id_agente = ?
AND tagente_modulo.disabled != 1
AND tagente_modulo.tcp_port = 1',
$module->{'id_agente'});
foreach my $agent_module (@modules) {
# Cluster active-active module.
if ($agent_module->{'prediction_module'} == 6) {
logger ($pa_config, "Executing cluster active-active critical module " . $agent_module->{'nombre'}, 10);
exec_cluster_aa_module($pa_config, $agent_module, $server_id, $dbh);
}
# Cluster active-passive module.
elsif ($agent_module->{'prediction_module'} == 7) {
logger ($pa_config, "Executing cluster active-passive critical module " . $agent_module->{'nombre'}, 10);
exec_cluster_ap_module($pa_config, $agent_module, $server_id, $dbh);
}
}
# Get the status of cluster modules.
my $data = -1;
@modules = get_db_rows($dbh,
'SELECT tagente_modulo.id_agente_modulo, tagente_estado.estado
FROM tagente_estado, tagente_modulo
WHERE tagente_estado.id_agente_modulo = tagente_modulo.id_agente_modulo
AND tagente_modulo.disabled != 1
AND tagente_modulo.tcp_port = 1
AND tagente_modulo.id_agente = ?',
$module->{'id_agente'});
foreach my $cluster_module (@modules) {
next if ($cluster_module->{'id_agente_modulo'} == $module->{'id_agente_modulo'}); # Skip the current module.
# Critical.
if ($cluster_module->{'estado'} == MODULE_NORMAL && $data < 0) {
$data = 0;
} elsif ($cluster_module->{'estado'} == MODULE_WARNING && $data < 1) {
$data = 1;
} elsif (($cluster_module->{'estado'} == MODULE_CRITICAL || $cluster_module->{'estado'} == MODULE_UNKNOWN) && $data < 2) {
$data = 2;
}
}
# No data.
if ($data < 0) {
pandora_update_module_on_error ($pa_config, $module, $dbh);
return;
}
# Get the current timestamp.
my $utimestamp = time ();
my $timestamp = strftime("%Y-%m-%d %H:%M:%S", localtime($utimestamp));
# Update the module.
pandora_process_module($pa_config, {'data' => $data}, '', $module, '', $timestamp, $utimestamp, $server_id, $dbh);
pandora_update_agent($pa_config, $timestamp, $module->{'id_agente'}, undef, undef, -1, $dbh);
}
################################################################################
# Execute a cluster active-active module.
################################################################################
sub exec_cluster_aa_module ($$$$) {
my ($pa_config, $module, $server_id, $dbh) = @_;
# Get the cluster item.
my $item = get_db_single_row($dbh, 'SELECT * FROM tcluster_item WHERE id=?', $module->{'custom_integer_2'});
if (!defined($item)) {
pandora_update_module_on_error ($pa_config, $module, $dbh);
return;
}
# Get cluster agents and compute the item status.
my ($not_normal, $total) = (0, 0);
my @agents = get_db_rows($dbh, 'SELECT id_agent FROM tcluster_agent WHERE id_cluster = ?', $module->{'custom_integer_1'});
foreach my $agent_id (@agents) {
my $item_status = get_db_value($dbh,
'SELECT estado
FROM tagente_estado, tagente_modulo
WHERE tagente_estado.id_agente_modulo = tagente_modulo.id_agente_modulo
AND tagente_modulo.id_agente = ?
AND tagente_modulo.nombre = ?',
$agent_id->{'id_agent'}, $item->{'name'});
# Count modules in a status other than normal.
if (!defined($item_status) || $item_status != MODULE_NORMAL) {
$not_normal += 1;
}
$total += 1;
}
# No data.
if ($total < 1) {
pandora_update_module_on_error ($pa_config, $module, $dbh);
return;
}
# Convert $not_normal to a percentage.
$not_normal = 100 * $not_normal / $total;
# Get the current timestamp.
my $utimestamp = time ();
my $timestamp = strftime ("%Y-%m-%d %H:%M:%S", localtime($utimestamp));
# Update module
pandora_process_module ($pa_config, {'data' => $not_normal}, '', $module, '', $timestamp, $utimestamp, $server_id, $dbh);
pandora_update_agent ($pa_config, $timestamp, $module->{'id_agente'}, undef, undef, -1, $dbh);
}
################################################################################
# Execute a cluster active-pasive module.
################################################################################
sub exec_cluster_ap_module ($$$$) {
my ($pa_config, $module, $server_id, $dbh) = @_;
# Get the cluster item.
my $item = get_db_single_row($dbh, 'SELECT * FROM tcluster_item WHERE id=?', $module->{'custom_integer_2'});
if (!defined($item)) {
pandora_update_module_on_error ($pa_config, $module, $dbh);
return;
}
# Get cluster agents and compute the item status.
my $data = undef;
my $utimestamp = 0;
my @agents = get_db_rows($dbh, 'SELECT id_agent FROM tcluster_agent WHERE id_cluster = ?', $module->{'custom_integer_1'});
foreach my $agent_id (@agents) {
my $status = get_db_single_row($dbh,
'SELECT datos, estado, utimestamp
FROM tagente_estado, tagente_modulo
WHERE tagente_estado.id_agente_modulo = tagente_modulo.id_agente_modulo
AND tagente_modulo.id_agente = ?
AND tagente_modulo.nombre = ?',
$agent_id->{'id_agent'}, $item->{'name'});
# Get the most recent data.
if (defined($status) && $status->{'estado'} != MODULE_UNKNOWN && $status->{'utimestamp'} > $utimestamp) {
$utimestamp = $status->{'utimestamp'};
$data = $status->{'datos'};
}
}
# No data.
if ($utimestamp == 0) {
pandora_update_module_on_error ($pa_config, $module, $dbh);
return;
}
# Get the current timestamp.
$utimestamp = time ();
my $timestamp = strftime ("%Y-%m-%d %H:%M:%S", localtime($utimestamp));
# Update the module.
pandora_process_module ($pa_config, {'data' => $data }, '', $module, '', $timestamp, $utimestamp, $server_id, $dbh);
# Update the agent.
pandora_update_agent ($pa_config, $timestamp, $module->{'id_agente'}, undef, undef, -1, $dbh);
}
# End of function declaration
# End of defined Code

View File

@ -207,21 +207,21 @@ sub exec_prediction_module ($$$$) {
# Cluster status module.
if ($agent_module->{'prediction_module'} == 5) {
logger ($pa_config, "Executing cluster status module " . $agent_module->{'nombre'}, 10);
enterprise_hook ('exec_cluster_status_module', [$pa_config, $agent_module, $server_id, $dbh]);
exec_cluster_status_module($pa_config, $agent_module, $server_id, $dbh);
return;
}
# Cluster active-active module.
if ($agent_module->{'prediction_module'} == 6) {
logger ($pa_config, "Executing cluster active-active module " . $agent_module->{'nombre'}, 10);
enterprise_hook ('exec_cluster_aa_module', [$pa_config, $agent_module, $server_id, $dbh]);
exec_cluster_aa_module($pa_config, $agent_module, $server_id, $dbh);
return;
}
# Cluster active-passive module.
if ($agent_module->{'prediction_module'} == 7) {
logger ($pa_config, "Executing cluster active-passive module " . $agent_module->{'nombre'}, 10);
enterprise_hook ('exec_cluster_ap_module', [$pa_config, $agent_module, $server_id, $dbh]);
exec_cluster_ap_module($pa_config, $agent_module, $server_id, $dbh);
return;
}