835 lines
23 KiB
PHP
835 lines
23 KiB
PHP
<?php
|
|
/**
|
|
* Cluster view main class.
|
|
*
|
|
* @category Class
|
|
* @package Pandora FMS
|
|
* @subpackage Community
|
|
* @version 1.0.0
|
|
* @license See below
|
|
*
|
|
* ______ ___ _______ _______ ________
|
|
* | __ \.-----.--.--.--| |.-----.----.-----. | ___| | | __|
|
|
* | __/| _ | | _ || _ | _| _ | | ___| |__ |
|
|
* |___| |___._|__|__|_____||_____|__| |___._| |___| |__|_|__|_______|
|
|
*
|
|
* ============================================================================
|
|
* Copyright (c) 2005-2023 Pandora FMS
|
|
* Please see https://pandorafms.com/community/ 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);
|
|
if (empty($id) === true) {
|
|
$id_agente = get_parameter('id_agente', null);
|
|
try {
|
|
$cluster = Cluster::loadFromAgentId($id_agente, true);
|
|
} catch (\Exception $e) {
|
|
$err = ui_print_error_message(
|
|
__('Cluster not found: '.$e->getMessage()),
|
|
'',
|
|
true
|
|
);
|
|
}
|
|
} else {
|
|
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;
|
|
}
|
|
|
|
$allGroups = agents_get_all_groups_agent(
|
|
$cluster->agent()->id_agente(),
|
|
$cluster->agent()->id_grupo()
|
|
);
|
|
|
|
$flag = (int) get_parameter('flag', 0);
|
|
if ($flag === 1 && check_acl_one_of_groups($config['id_user'], $allGroups, 'AW') === true) {
|
|
$id_agent_module = get_parameter('id_agente_modulo');
|
|
db_process_sql_update(
|
|
'tagente_modulo',
|
|
['flag' => 1],
|
|
['id_agente_modulo' => $id_agent_module]
|
|
);
|
|
}
|
|
|
|
$flag_agent = (int) get_parameter('flag_agent', 0);
|
|
if ($flag_agent === 1 && check_acl_one_of_groups($config['id_user'], $allGroups, 'AW') === true) {
|
|
db_process_sql_update(
|
|
'tagente_modulo',
|
|
['flag' => 1],
|
|
['id_agente' => $cluster->agent()->id_agente()]
|
|
);
|
|
}
|
|
|
|
$counters = $cluster->getCounters();
|
|
$counters_chart = [];
|
|
$counters_bullet = [];
|
|
foreach ($counters as $key => $value) {
|
|
if ($key === 'not_init') {
|
|
$counters_chart['Not init'] = $value;
|
|
} else {
|
|
$counters_chart[ucfirst($key)] = $value;
|
|
}
|
|
|
|
$counters_bullet[$key.'_count'] = $value;
|
|
}
|
|
|
|
unset($counters_chart['Total']);
|
|
|
|
$module_involved_ids = $cluster->getIdsModulesInvolved();
|
|
|
|
View::render(
|
|
'cluster/view',
|
|
[
|
|
'message' => $msg,
|
|
'error' => $err,
|
|
'config' => $config,
|
|
'model' => $this,
|
|
'cluster' => $cluster,
|
|
'critical' => $critical,
|
|
'allGroups' => $allGroups,
|
|
'counters_chart' => $counters_chart,
|
|
'counters_bullet' => $counters_bullet,
|
|
'module_involved_ids' => array_combine($module_involved_ids, $module_involved_ids),
|
|
]
|
|
);
|
|
}
|
|
|
|
|
|
/**
|
|
* 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()
|
|
{
|
|
return $this->getAll('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 {
|
|
// Html div part for csv export.
|
|
$tmp->group = ui_print_group_icon(
|
|
$tmp->group,
|
|
true
|
|
).html_print_div(
|
|
[
|
|
'content' => groups_get_name($tmp->group),
|
|
'class' => 'invisible',
|
|
],
|
|
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.
|
|
// Html div part for csv export.
|
|
$tmp->known_status = ui_print_module_status(
|
|
$tmp->known_status,
|
|
true
|
|
).html_print_div(
|
|
[
|
|
'content' => $tmp->known_status,
|
|
'class' => 'invisible',
|
|
],
|
|
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/details.svg',
|
|
true,
|
|
[
|
|
'title' => __('View'),
|
|
'class' => 'main_menu_icon 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/edit.svg',
|
|
true,
|
|
[
|
|
'title' => __('Edit'),
|
|
'class' => 'main_menu_icon 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/delete.svg',
|
|
true,
|
|
[
|
|
'title' => __('Delete'),
|
|
'class' => 'main_menu_icon 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' => [
|
|
'icon' => 'back',
|
|
'mode' => 'secondary',
|
|
],
|
|
'return' => true,
|
|
],
|
|
],
|
|
];
|
|
|
|
return $form;
|
|
}
|
|
|
|
|
|
}
|