Merge remote-tracking branch 'origin/develop' into ent-9554-nuevas-graficas-de-tarta-que-reemplazar-a-las-actuales-3
This commit is contained in:
commit
47c503bdab
|
@ -1,5 +1,5 @@
|
|||
package: pandorafms-agent-unix
|
||||
Version: 7.0NG.767-221219
|
||||
Version: 7.0NG.767-221221
|
||||
Architecture: all
|
||||
Priority: optional
|
||||
Section: admin
|
||||
|
|
|
@ -14,7 +14,7 @@
|
|||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
|
||||
pandora_version="7.0NG.767-221219"
|
||||
pandora_version="7.0NG.767-221221"
|
||||
|
||||
echo "Test if you has the tools for to make the packages."
|
||||
whereis dpkg-deb | cut -d":" -f2 | grep dpkg-deb > /dev/null
|
||||
|
|
|
@ -1015,7 +1015,7 @@ my $Sem = undef;
|
|||
my $ThreadSem = undef;
|
||||
|
||||
use constant AGENT_VERSION => '7.0NG.767';
|
||||
use constant AGENT_BUILD => '221219';
|
||||
use constant AGENT_BUILD => '221221';
|
||||
|
||||
# Agent log default file size maximum and instances
|
||||
use constant DEFAULT_MAX_LOG_SIZE => 600000;
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
#
|
||||
%define name pandorafms_agent_unix
|
||||
%define version 7.0NG.767
|
||||
%define release 221219
|
||||
%define release 221221
|
||||
|
||||
Summary: Pandora FMS Linux agent, PERL version
|
||||
Name: %{name}
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
#
|
||||
%define name pandorafms_agent_unix
|
||||
%define version 7.0NG.767
|
||||
%define release 221219
|
||||
%define release 221221
|
||||
|
||||
Summary: Pandora FMS Linux agent, PERL version
|
||||
Name: %{name}
|
||||
|
|
|
@ -10,7 +10,7 @@
|
|||
# **********************************************************************
|
||||
|
||||
PI_VERSION="7.0NG.767"
|
||||
PI_BUILD="221219"
|
||||
PI_BUILD="221221"
|
||||
OS_NAME=`uname -s`
|
||||
|
||||
FORCE=0
|
||||
|
|
|
@ -186,7 +186,7 @@ UpgradeApplicationID
|
|||
{}
|
||||
|
||||
Version
|
||||
{221219}
|
||||
{221221}
|
||||
|
||||
ViewReadme
|
||||
{Yes}
|
||||
|
|
|
@ -30,7 +30,7 @@ using namespace Pandora;
|
|||
using namespace Pandora_Strutils;
|
||||
|
||||
#define PATH_SIZE _MAX_PATH+1
|
||||
#define PANDORA_VERSION ("7.0NG.767 Build 221219")
|
||||
#define PANDORA_VERSION ("7.0NG.767 Build 221221")
|
||||
|
||||
string pandora_path;
|
||||
string pandora_dir;
|
||||
|
|
|
@ -11,7 +11,7 @@ BEGIN
|
|||
VALUE "LegalCopyright", "Artica ST"
|
||||
VALUE "OriginalFilename", "PandoraAgent.exe"
|
||||
VALUE "ProductName", "Pandora FMS Windows Agent"
|
||||
VALUE "ProductVersion", "(7.0NG.767(Build 221219))"
|
||||
VALUE "ProductVersion", "(7.0NG.767(Build 221221))"
|
||||
VALUE "FileVersion", "1.0.0.0"
|
||||
END
|
||||
END
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
package: pandorafms-console
|
||||
Version: 7.0NG.767-221219
|
||||
Version: 7.0NG.767-221221
|
||||
Architecture: all
|
||||
Priority: optional
|
||||
Section: admin
|
||||
|
|
|
@ -14,7 +14,7 @@
|
|||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
|
||||
pandora_version="7.0NG.767-221219"
|
||||
pandora_version="7.0NG.767-221221"
|
||||
|
||||
package_pear=0
|
||||
package_pandora=1
|
||||
|
|
|
@ -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
|
||||
|
|
File diff suppressed because one or more lines are too long
|
@ -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>
|
||||
|
|
|
@ -59,6 +59,7 @@ ui_print_warning_message(
|
|||
]
|
||||
);
|
||||
|
||||
$table = new StdClass();
|
||||
$table->width = '100%';
|
||||
$table->class = 'databox filters';
|
||||
$table->data = [];
|
||||
|
|
|
@ -2118,6 +2118,9 @@ if ($delete_module) {
|
|||
exit;
|
||||
}
|
||||
|
||||
// Before delete the main module, check and delete the childrens from the original module.
|
||||
module_check_childrens_and_delete($id_borrar_modulo);
|
||||
|
||||
// Also call base function to delete modules.
|
||||
modules_delete_agent_module($id_borrar_modulo);
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -247,6 +247,8 @@ if ($module_action === 'delete') {
|
|||
$print_result_msg = true;
|
||||
$count_correct_delete_modules = 0;
|
||||
foreach ($id_agent_modules_delete as $id_agent_module_del) {
|
||||
// Before delete the main module, check and delete the childrens from the original module.
|
||||
module_check_childrens_and_delete($id_agent_module_del);
|
||||
$id_grupo = (int) agents_get_agent_group($id_agente);
|
||||
$all_groups = agents_get_all_groups_agent($id_agente, $id_grupo);
|
||||
|
||||
|
|
|
@ -231,7 +231,7 @@ if (strlen(trim($agentName)) > 0) {
|
|||
}
|
||||
|
||||
if ($actionID != -1 && $actionID != '') {
|
||||
$where .= ' AND talert_template_modules.id IN (SELECT id_alert_template_module FROM talert_template_module_actions WHERE id_alert_action = '.$actionID.') OR talert_template_modules.id IN (SELECT id FROM talert_template_modules ttm WHERE ttm.id_alert_template IN (SELECT tat.id FROM talert_templates tat WHERE tat.id_alert_action = '.$actionID.'))';
|
||||
$where .= ' AND (talert_template_modules.id IN (SELECT id_alert_template_module FROM talert_template_module_actions WHERE id_alert_action = '.$actionID.') OR talert_template_modules.id IN (SELECT id FROM talert_template_modules ttm WHERE ttm.id_alert_template IN (SELECT tat.id FROM talert_templates tat WHERE tat.id_alert_action = '.$actionID.')))';
|
||||
}
|
||||
|
||||
if ($status_alert === 'disabled') {
|
||||
|
|
|
@ -200,14 +200,14 @@ if ($access_console_node === true) {
|
|||
$sub['gmassive']['type'] = 'direct';
|
||||
$sub['gmassive']['subtype'] = 'nolink';
|
||||
$sub2 = [];
|
||||
$sub2['godmode/massive/massive_operations&tab=massive_agents']['text'] = __('Agents operations');
|
||||
$sub2['godmode/massive/massive_operations&tab=massive_modules']['text'] = __('Modules operations');
|
||||
$sub2['godmode/massive/massive_operations&tab=massive_plugins']['text'] = __('Plugins operations');
|
||||
$sub2['godmode/massive/massive_operations&tab=massive_agents']['text'] = __('Agents operations');
|
||||
$sub2['godmode/massive/massive_operations&tab=massive_modules']['text'] = __('Modules operations');
|
||||
$sub2['godmode/massive/massive_operations&tab=massive_plugins']['text'] = __('Plugins operations');
|
||||
if ((bool) check_acl($config['id_user'], 0, 'UM') === true) {
|
||||
$sub2['godmode/massive/massive_operations&tab=massive_users']['text'] = __('Users operations');
|
||||
$sub2['godmode/massive/massive_operations&tab=massive_users']['text'] = __('Users operations');
|
||||
}
|
||||
|
||||
$sub2['godmode/massive/massive_operations&tab=massive_alerts']['text'] = __('Alerts operations');
|
||||
$sub2['godmode/massive/massive_operations&tab=massive_alerts']['text'] = __('Alerts operations');
|
||||
enterprise_hook('massivepolicies_submenu');
|
||||
enterprise_hook('massivesnmp_submenu');
|
||||
enterprise_hook('massivesatellite_submenu');
|
||||
|
|
|
@ -2182,6 +2182,8 @@ switch ($action) {
|
|||
'historical_db_check'
|
||||
);
|
||||
$values['top_n_value'] = get_parameter('max_items');
|
||||
|
||||
$values['server_name'] = get_parameter('combo_server');
|
||||
} else if ($values['type'] == 'url') {
|
||||
$values['external_source'] = get_parameter('url');
|
||||
} else if ($values['type'] == 'event_report_group') {
|
||||
|
|
|
@ -77,6 +77,7 @@ if (is_ajax()) {
|
|||
true
|
||||
).' ';
|
||||
$table->data['autocreate_remote_users'] = $row;
|
||||
$table->data['csrf_token'] = html_print_csrf_hidden();
|
||||
|
||||
add_enterprise_auth_autocreate_profiles($table, $type_auth);
|
||||
}
|
||||
|
@ -475,6 +476,8 @@ if (!is_metaconsole()) {
|
|||
html_print_input_hidden('hash_save_config', md5('save'.$config['dbpass']));
|
||||
}
|
||||
|
||||
html_print_csrf_hidden();
|
||||
|
||||
html_print_table($table);
|
||||
echo '<div id="table_auth_result"></div>';
|
||||
echo '<div class="action-buttons" style="width: '.$table->width.'">';
|
||||
|
|
Binary file not shown.
After Width: | Height: | Size: 5.2 KiB |
|
@ -654,7 +654,7 @@ if ($get_agent_alerts_datatable === true) {
|
|||
}
|
||||
|
||||
$idGroup = $filter_alert['ag_group'];
|
||||
$tag_filter = $filter_alert['tag_filter'];
|
||||
$tag_filter = $filter_alert['tag'];
|
||||
$action_filter = $filter_alert['action'];
|
||||
|
||||
try {
|
||||
|
|
|
@ -26,6 +26,8 @@
|
|||
* ============================================================================
|
||||
*/
|
||||
|
||||
use PandoraFMS\Enterprise\Metaconsole\Node;
|
||||
|
||||
// Begin.
|
||||
if (check_login()) {
|
||||
global $config;
|
||||
|
@ -59,6 +61,11 @@ if (check_login()) {
|
|||
0
|
||||
);
|
||||
|
||||
$get_data_dataMatrix = (bool) get_parameter(
|
||||
'get_data_dataMatrix',
|
||||
0
|
||||
);
|
||||
|
||||
if ($get_agent_modules_json_by_name === true) {
|
||||
$agent_name = get_parameter('agent_name');
|
||||
|
||||
|
@ -1436,4 +1443,190 @@ if (check_login()) {
|
|||
|
||||
return;
|
||||
}
|
||||
|
||||
if ($get_data_dataMatrix === true) {
|
||||
global $config;
|
||||
|
||||
$table_id = get_parameter('table_id', '');
|
||||
$modules = json_decode(
|
||||
io_safe_output(
|
||||
get_parameter('modules', '')
|
||||
),
|
||||
true
|
||||
);
|
||||
$period = get_parameter('period', 0);
|
||||
$slice = get_parameter('slice', 0);
|
||||
|
||||
// Datatables offset, limit.
|
||||
$start = get_parameter('start', 0);
|
||||
$formatData = (bool) get_parameter('formatData', 0);
|
||||
$length = get_parameter(
|
||||
'length',
|
||||
$config['block_size']
|
||||
);
|
||||
|
||||
$order = get_datatable_order(true);
|
||||
|
||||
// Total time per page.
|
||||
$time_all_box = ($length * $slice);
|
||||
|
||||
// Total number of boxes.
|
||||
$total_box = ceil($period / $slice);
|
||||
|
||||
if ($start > 0) {
|
||||
$start = ($start / $length);
|
||||
}
|
||||
|
||||
// Uncompress.
|
||||
try {
|
||||
ob_start();
|
||||
$dateNow = get_system_time();
|
||||
$final = ($dateNow - $period);
|
||||
$date = ($dateNow - ($time_all_box * $start));
|
||||
|
||||
if (($date - $time_all_box) > $final) {
|
||||
$datelimit = ($date - $time_all_box);
|
||||
} else {
|
||||
$datelimit = $final;
|
||||
}
|
||||
|
||||
foreach ($modules as $key => $value) {
|
||||
if (is_metaconsole() === true) {
|
||||
try {
|
||||
$node = new Node((int) $value['id_node']);
|
||||
$node->connect();
|
||||
} catch (\Exception $e) {
|
||||
// Unexistent agent.
|
||||
$node->disconnect();
|
||||
}
|
||||
}
|
||||
|
||||
$value['thresholds'] = [
|
||||
'min_critical' => (empty($value['c_min']) === true) ? null : $value['c_min'],
|
||||
'max_critical' => (empty($value['c_max']) === true) ? null : $value['c_max'],
|
||||
'min_warning' => (empty($value['w_min']) === true) ? null : $value['w_min'],
|
||||
'max_warning' => (empty($value['w_max']) === true) ? null : $value['w_max'],
|
||||
];
|
||||
|
||||
$module_data = db_uncompress_module_data(
|
||||
$value['id'],
|
||||
$datelimit,
|
||||
$date,
|
||||
$slice,
|
||||
true
|
||||
);
|
||||
|
||||
$uncompressData[] = array_reduce(
|
||||
$module_data,
|
||||
function ($carry, $item) use ($value, $config, $formatData) {
|
||||
// Last value.
|
||||
$vdata = null;
|
||||
if (is_array($item['data']) === true) {
|
||||
foreach ($item['data'] as $v) {
|
||||
$vdata = $v['datos'];
|
||||
}
|
||||
}
|
||||
|
||||
$status = get_status_data_modules(
|
||||
$value['id'],
|
||||
$vdata,
|
||||
$value['thresholds']
|
||||
);
|
||||
|
||||
$resultData = '<span style="color:'.$status['color'].'">';
|
||||
if ($vdata !== null && $vdata !== '' && $vdata !== false) {
|
||||
if (isset($formatData) === true
|
||||
&& (bool) $formatData === true
|
||||
) {
|
||||
$resultData .= format_for_graph(
|
||||
$vdata,
|
||||
$config['graph_precision']
|
||||
);
|
||||
} else {
|
||||
$resultData .= sla_truncate(
|
||||
$vdata,
|
||||
$config['graph_precision']
|
||||
);
|
||||
}
|
||||
|
||||
$resultData .= ' '.$value['unit'];
|
||||
} else {
|
||||
$resultData .= '--';
|
||||
}
|
||||
|
||||
$resultData .= '</span>';
|
||||
$carry[] = [
|
||||
'utimestamp' => $item['utimestamp'],
|
||||
'Column-'.$value['id'] => $resultData,
|
||||
];
|
||||
|
||||
return $carry;
|
||||
},
|
||||
[]
|
||||
);
|
||||
|
||||
if (is_metaconsole() === true) {
|
||||
$node->disconnect();
|
||||
}
|
||||
}
|
||||
|
||||
if (empty($uncompressData) === false) {
|
||||
$data = array_reduce(
|
||||
$uncompressData,
|
||||
function ($carry, $item) {
|
||||
foreach ($item as $data_module) {
|
||||
foreach ($data_module as $key => $value) {
|
||||
if ($key === 'utimestamp') {
|
||||
$carry[$data_module['utimestamp']]['date'] = date('Y-m-d H:i', (int) $value);
|
||||
} else {
|
||||
$carry[$data_module['utimestamp']][$key] = $value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $carry;
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
if (empty($data) === false) {
|
||||
$data = array_reverse(array_values($data));
|
||||
} else {
|
||||
$data = [];
|
||||
}
|
||||
|
||||
// RecordsTotal && recordsfiltered resultados totales.
|
||||
echo json_encode(
|
||||
[
|
||||
'data' => $data,
|
||||
'recordsTotal' => $total_box,
|
||||
'recordsFiltered' => $total_box,
|
||||
]
|
||||
);
|
||||
|
||||
$response = ob_get_clean();
|
||||
|
||||
// Clean output buffer.
|
||||
while (ob_get_level() !== 0) {
|
||||
ob_end_clean();
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
echo json_encode(
|
||||
['error' => $e->getMessage()]
|
||||
);
|
||||
}
|
||||
|
||||
// If not valid it will throw an exception.
|
||||
json_decode($response);
|
||||
if (json_last_error() == JSON_ERROR_NONE) {
|
||||
// If valid dump.
|
||||
echo $response;
|
||||
} else {
|
||||
echo json_encode(
|
||||
['error' => $response]
|
||||
);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -20,7 +20,7 @@
|
|||
/**
|
||||
* Pandora build version and version
|
||||
*/
|
||||
$build_version = 'PC221219';
|
||||
$build_version = 'PC221221';
|
||||
$pandora_version = 'v7.0NG.767';
|
||||
|
||||
// Do not overwrite default timezone set if defined.
|
||||
|
|
|
@ -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;
|
||||
|
@ -10157,6 +10157,8 @@ function api_set_delete_module($id, $id2, $other, $trash1)
|
|||
}
|
||||
|
||||
if (!$simulate) {
|
||||
// Before delete the main module, check and delete the childrens from the original module.
|
||||
module_check_childrens_and_delete($idAgentModule);
|
||||
$return = modules_delete_agent_module($idAgentModule);
|
||||
} else {
|
||||
$return = true;
|
||||
|
@ -10182,6 +10184,8 @@ function api_set_delete_module($id, $id2, $other, $trash1)
|
|||
}
|
||||
|
||||
if (!$simulate) {
|
||||
// Before delete the main module, check and delete the childrens from the original module.
|
||||
module_check_childrens_and_delete($idAgentModule);
|
||||
$return = modules_delete_agent_module($idAgentModule);
|
||||
} else {
|
||||
$return = true;
|
||||
|
|
|
@ -523,6 +523,15 @@ function config_update_config()
|
|||
break;
|
||||
|
||||
case 'auth':
|
||||
$validatedCSRF = validate_csrf_code();
|
||||
|
||||
// CSRF Validation.
|
||||
if ($validatedCSRF === false) {
|
||||
include_once 'general/login_page.php';
|
||||
// Finish the execution.
|
||||
exit('</html>');
|
||||
}
|
||||
|
||||
// AUTHENTICATION SETUP.
|
||||
if (config_update_value('auth', get_parameter('auth'), true) === false) {
|
||||
$error_update[] = __('Authentication method');
|
||||
|
|
|
@ -757,7 +757,8 @@ function db_uncompress_module_data(
|
|||
$id_agente_modulo,
|
||||
$tstart=false,
|
||||
$tend=false,
|
||||
$slice_size=false
|
||||
$slice_size=false,
|
||||
$force_slice_not_data=false
|
||||
) {
|
||||
global $config;
|
||||
|
||||
|
@ -860,7 +861,10 @@ function db_uncompress_module_data(
|
|||
|
||||
$module_interval = modules_get_interval($id_agente_modulo);
|
||||
|
||||
if (($raw_data === false) && ( $first_utimestamp === false )) {
|
||||
if (($force_slice_not_data === false)
|
||||
&& ($raw_data === false)
|
||||
&& ( $first_utimestamp === false )
|
||||
) {
|
||||
// No data.
|
||||
return false;
|
||||
}
|
||||
|
@ -2313,7 +2317,13 @@ function db_get_lock(string $lockname, int $expiration_time=86400) :?int
|
|||
}
|
||||
|
||||
if ($lock_status === false) {
|
||||
return null;
|
||||
db_pandora_audit(
|
||||
AUDIT_LOG_SYSTEM,
|
||||
'Issue in Database Lock',
|
||||
'system'
|
||||
);
|
||||
|
||||
return (int) null;
|
||||
}
|
||||
|
||||
return (int) $lock_status;
|
||||
|
|
|
@ -3982,6 +3982,81 @@ function recursive_get_dt_from_modules_tree(&$f_modules, $modules, $deep)
|
|||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get the module data from a children
|
||||
*
|
||||
* @param integer $id_module Id module
|
||||
* @return array Children module data
|
||||
*/
|
||||
function get_children_module($id_module)
|
||||
{
|
||||
$children_module_data = db_get_all_rows_sql(
|
||||
'SELECT *
|
||||
FROM tagente_modulo
|
||||
WHERE parent_module_id = '.$id_module
|
||||
);
|
||||
|
||||
return $children_module_data;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Find and delete the childers modules from the $id_module
|
||||
*
|
||||
* @param mixed $id_module
|
||||
* @return void
|
||||
*/
|
||||
function module_check_childrens_and_delete($id_module)
|
||||
{
|
||||
$children_data = get_children_module($id_module);
|
||||
// Check if exist have a childer
|
||||
if ($children_data) {
|
||||
// If have more than 1 children
|
||||
if (is_array($children_data)) {
|
||||
foreach ($children_data as $children_module_data) {
|
||||
if ($children_module_data['parent_module_id']) {
|
||||
// Search children and delete this module
|
||||
// Before delete, lets check if exist (Just for cases it's already deleted)
|
||||
if (modules_check_agentmodule_exists($children_module_data['parent_module_id'])) {
|
||||
modules_delete_agent_module($children_module_data['parent_module_id']);
|
||||
}
|
||||
|
||||
module_check_childrens_and_delete($children_module_data['id_agente_modulo']);
|
||||
} else {
|
||||
// If haven't children just delete
|
||||
// Before delete, lets check if exist (Just for cases it's already deleted)
|
||||
if (modules_check_agentmodule_exists($children_module_data['id_agente_modulo'])) {
|
||||
modules_delete_agent_module($children_module_data['id_agente_modulo']);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// If just have 1 children
|
||||
if ($children_data['parent_module_id']) {
|
||||
// Before delete, lets check if exist (Just for cases it's already deleted)
|
||||
if (modules_check_agentmodule_exists($children_data['parent_module_id'])) {
|
||||
modules_delete_agent_module($children_data['parent_module_id']);
|
||||
}
|
||||
|
||||
module_check_childrens_and_delete($children_data['id_agente_modulo']);
|
||||
} else {
|
||||
// If haven't children just delete
|
||||
// Before delete, lets check if exist (Just for cases it's already deleted)
|
||||
if (modules_check_agentmodule_exists($children_data['id_agente_modulo'])) {
|
||||
modules_delete_agent_module($children_data['id_agente_modulo']);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Haven't childrens, so delete
|
||||
// Before delete, lets check if exist (Just for cases it's already deleted)
|
||||
if (modules_check_agentmodule_exists($id_module)) {
|
||||
modules_delete_agent_module($id_module);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @brief Get the button with the link to open realtime stats into a new window
|
||||
* Only to native (not satellite discovered) snmp modules.
|
||||
|
@ -4336,3 +4411,129 @@ function modules_get_regex(
|
|||
|
||||
return $result;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Status for data thresholds modules.
|
||||
*
|
||||
* @param integer $id_module Module ID.
|
||||
* @param mixed $data Data int, bool, null, etc.
|
||||
* @param array $thresholds Array thresholds.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
function get_status_data_modules(int $id_module, $data, $thresholds)
|
||||
{
|
||||
// Check not init.
|
||||
if ($data === false) {
|
||||
return ['color' => COL_NOTINIT];
|
||||
}
|
||||
|
||||
// Check boolean.
|
||||
$is_bolean = modules_is_boolean($id_module);
|
||||
if ($is_bolean === true) {
|
||||
if ($data > 0) {
|
||||
return ['color' => COL_CRITICAL];
|
||||
} else {
|
||||
return ['color' => COL_NORMAL];
|
||||
}
|
||||
}
|
||||
|
||||
$thresholds = calculateThreshold($thresholds);
|
||||
|
||||
foreach (getStatuses() as $status) {
|
||||
if ($thresholds[$status]['min'] === null
|
||||
&& $thresholds[$status]['max'] === null
|
||||
) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (($thresholds[$status]['min'] === null
|
||||
&& $thresholds[$status]['max'] >= $data)
|
||||
|| ($thresholds[$status]['max'] === null
|
||||
&& $thresholds[$status]['min'] <= $data)
|
||||
|| ($thresholds[$status]['min'] <= $data
|
||||
&& $thresholds[$status]['max'] >= $data)
|
||||
) {
|
||||
if ($status === 'critical') {
|
||||
return ['color' => COL_CRITICAL];
|
||||
} else if ($status === 'warning') {
|
||||
return ['color' => COL_WARNING];
|
||||
} else {
|
||||
return ['color' => COL_NORMAL];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return ['color' => COL_NORMAL];
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Calculate thresholds.
|
||||
*
|
||||
* @param array $thresholds_array
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
function calculateThreshold(array $thresholds_array)
|
||||
{
|
||||
$nMax = null;
|
||||
if ($thresholds_array['min_warning'] !== null) {
|
||||
$nMax = $thresholds_array['min_warning'];
|
||||
} else if ($thresholds_array['min_critical'] !== null) {
|
||||
$nMax = $thresholds_array['min_critical'];
|
||||
}
|
||||
|
||||
$wMin = null;
|
||||
if ($thresholds_array['min_warning'] !== null) {
|
||||
$wMin = $thresholds_array['min_warning'];
|
||||
}
|
||||
|
||||
$wMax = null;
|
||||
if ($thresholds_array['max_warning'] !== null) {
|
||||
$wMax = $thresholds_array['max_warning'];
|
||||
}
|
||||
|
||||
$cMin = null;
|
||||
if ($thresholds_array['min_critical'] !== null) {
|
||||
$cMin = $thresholds_array['min_critical'];
|
||||
}
|
||||
|
||||
$cMax = null;
|
||||
if ($thresholds_array['max_critical'] !== null) {
|
||||
$cMax = $thresholds_array['max_critical'];
|
||||
}
|
||||
|
||||
$thresholds = [
|
||||
'normal' => [
|
||||
'min' => null,
|
||||
'max' => $nMax,
|
||||
],
|
||||
'warning' => [
|
||||
'min' => $wMin,
|
||||
'max' => $wMax,
|
||||
],
|
||||
'critical' => [
|
||||
'min' => $cMin,
|
||||
'max' => $cMax,
|
||||
],
|
||||
];
|
||||
|
||||
return $thresholds;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get status.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
function getStatuses()
|
||||
{
|
||||
return [
|
||||
'critical',
|
||||
'warning',
|
||||
'normal',
|
||||
];
|
||||
}
|
||||
|
|
|
@ -3399,7 +3399,10 @@ function get_status_color_networkmap_fictional_point($id_networkmap, $parent='')
|
|||
|
||||
if ($agent['source_data'] == -2) {
|
||||
if (empty($parent) === true) {
|
||||
$option = json_decode($agent, true);
|
||||
if (is_array($agent) === false) {
|
||||
$option = json_decode($agent, true);
|
||||
}
|
||||
|
||||
if ($option['networkmap'] == 0) {
|
||||
$status = 0;
|
||||
} else {
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -1699,7 +1699,6 @@ function selected_node(d, selected_param, hold_other_selections) {
|
|||
);
|
||||
|
||||
d3.event.stopPropagation();
|
||||
d3.event.preventDefault();
|
||||
}
|
||||
|
||||
function clear_selection() {
|
||||
|
@ -4020,8 +4019,10 @@ function draw_elements_graph() {
|
|||
font_size +
|
||||
"px !important; text-align:center; text-anchor:middle; fill:#000000"
|
||||
)
|
||||
.text(function(d) {
|
||||
return ellipsize(get_node_name_ov(d), 30);
|
||||
.html(function(d) {
|
||||
d.text = ellipsize(d.text, 30);
|
||||
|
||||
return get_node_name_ov(d, true, font_size);
|
||||
})
|
||||
.classed("dragable_node fill_fff", true) //own dragable
|
||||
.on("click", selected_node)
|
||||
|
@ -4030,7 +4031,7 @@ function draw_elements_graph() {
|
|||
});
|
||||
|
||||
node_temp.append("title").text(function(d) {
|
||||
return get_node_name_ov(d);
|
||||
return get_node_name_ov(d, false);
|
||||
});
|
||||
|
||||
node.exit().remove();
|
||||
|
@ -4040,9 +4041,19 @@ function is_central_node(data) {
|
|||
return data.type == 0 && data.id_agent == 0;
|
||||
}
|
||||
|
||||
function get_node_name_ov(data) {
|
||||
function get_node_name_ov(data, generate_link, font_size) {
|
||||
font_size = font_size || 20;
|
||||
generate_link = generate_link || false;
|
||||
|
||||
var data_text = data.text;
|
||||
|
||||
if (generate_link === true && data.networkmap_id > 0) {
|
||||
data_text = `<a href="index.php?sec=network&sec2=operation/agentes/pandora_networkmap&tab=view&id_networkmap=
|
||||
${data.networkmap_id}" style="font-size: ${font_size}px;">${data.text}</a>`;
|
||||
}
|
||||
|
||||
// Node central name should be the product name
|
||||
return is_central_node(data) ? $("#hidden-product_name").val() : data.text;
|
||||
return is_central_node(data) ? $("#hidden-product_name").val() : data_text;
|
||||
}
|
||||
|
||||
function choose_group_for_show_agents() {
|
||||
|
|
|
@ -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().' » ';
|
||||
$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);
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -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);
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -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
|
@ -206,7 +206,7 @@ class Widget
|
|||
|
||||
$sql_search = '';
|
||||
if (empty($search) === false) {
|
||||
$sql_search = 'AND description LIKE "%'.$search.'%" ';
|
||||
$sql_search = 'AND description LIKE "%'.addslashes($search).'%" ';
|
||||
}
|
||||
|
||||
// User admin view all dashboards.
|
||||
|
@ -419,6 +419,7 @@ class Widget
|
|||
case 'GroupedMeterGraphs':
|
||||
case 'ColorModuleTabs':
|
||||
case 'BlockHistogram':
|
||||
case 'DataMatrix':
|
||||
$className .= '\\'.$name;
|
||||
break;
|
||||
|
||||
|
|
|
@ -0,0 +1,697 @@
|
|||
<?php
|
||||
/**
|
||||
* Widget data matrix Pandora FMS Console
|
||||
*
|
||||
* @category Console Class
|
||||
* @package Pandora FMS
|
||||
* @subpackage Widget
|
||||
* @version 1.0.0
|
||||
* @license See below
|
||||
*
|
||||
* ______ ___ _______ _______ ________
|
||||
* | __ \.-----.--.--.--| |.-----.----.-----. | ___| | | __|
|
||||
* | __/| _ | | _ || _ | _| _ | | ___| |__ |
|
||||
* |___| |___._|__|__|_____||_____|__| |___._| |___| |__|_|__|_______|
|
||||
*
|
||||
* ============================================================================
|
||||
* Copyright (c) 2005-2022 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.
|
||||
* ============================================================================
|
||||
*/
|
||||
|
||||
namespace PandoraFMS\Dashboard;
|
||||
|
||||
use PandoraFMS\Enterprise\Metaconsole\Node;
|
||||
|
||||
global $config;
|
||||
|
||||
/**
|
||||
* URL Widgets
|
||||
*/
|
||||
class DataMatrix extends Widget
|
||||
{
|
||||
|
||||
private const MAX_MODULES = 10;
|
||||
|
||||
/**
|
||||
* Name widget.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $name;
|
||||
|
||||
/**
|
||||
* Title widget.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $title;
|
||||
|
||||
/**
|
||||
* Page widget;
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $page;
|
||||
|
||||
/**
|
||||
* Class name widget.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $className;
|
||||
|
||||
/**
|
||||
* Values options for each widget.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $values;
|
||||
|
||||
/**
|
||||
* Configuration required.
|
||||
*
|
||||
* @var boolean
|
||||
*/
|
||||
protected $configurationRequired;
|
||||
|
||||
/**
|
||||
* Error load widget.
|
||||
*
|
||||
* @var boolean
|
||||
*/
|
||||
protected $loadError;
|
||||
|
||||
/**
|
||||
* Width.
|
||||
*
|
||||
* @var integer
|
||||
*/
|
||||
protected $width;
|
||||
|
||||
/**
|
||||
* Heigth.
|
||||
*
|
||||
* @var integer
|
||||
*/
|
||||
protected $height;
|
||||
|
||||
/**
|
||||
* Grid Width.
|
||||
*
|
||||
* @var integer
|
||||
*/
|
||||
protected $gridWidth;
|
||||
|
||||
/**
|
||||
* Cell ID.
|
||||
*
|
||||
* @var integer
|
||||
*/
|
||||
protected $cellId;
|
||||
|
||||
|
||||
/**
|
||||
* Construct.
|
||||
*
|
||||
* @param integer $cellId Cell ID.
|
||||
* @param integer $dashboardId Dashboard ID.
|
||||
* @param integer $widgetId Widget ID.
|
||||
* @param integer|null $width New width.
|
||||
* @param integer|null $height New height.
|
||||
* @param integer|null $gridWidth Grid width.
|
||||
*/
|
||||
public function __construct(
|
||||
int $cellId,
|
||||
int $dashboardId=0,
|
||||
int $widgetId=0,
|
||||
?int $width=0,
|
||||
?int $height=0,
|
||||
?int $gridWidth=0
|
||||
) {
|
||||
global $config;
|
||||
|
||||
// WARNING: Do not edit. This chunk must be in the constructor.
|
||||
parent::__construct(
|
||||
$cellId,
|
||||
$dashboardId,
|
||||
$widgetId
|
||||
);
|
||||
|
||||
// Width.
|
||||
$this->width = $width;
|
||||
|
||||
// Height.
|
||||
$this->height = $height;
|
||||
|
||||
// Grid Width.
|
||||
$this->gridWidth = $gridWidth;
|
||||
|
||||
// Cell Id.
|
||||
$this->cellId = $cellId;
|
||||
|
||||
// Options.
|
||||
$this->values = $this->decoders($this->getOptionsWidget());
|
||||
|
||||
// Positions.
|
||||
$this->position = $this->getPositionWidget();
|
||||
|
||||
// Page.
|
||||
$this->page = basename(__FILE__);
|
||||
|
||||
// ClassName.
|
||||
$class = new \ReflectionClass($this);
|
||||
$this->className = $class->getShortName();
|
||||
|
||||
// Title.
|
||||
$this->title = __('Color tabs modules');
|
||||
|
||||
// Name.
|
||||
if (empty($this->name) === true) {
|
||||
$this->name = 'single_graph';
|
||||
}
|
||||
|
||||
// This forces at least a first configuration.
|
||||
$this->configurationRequired = false;
|
||||
if (empty($this->values['moduleDataMatrix']) === true) {
|
||||
$this->configurationRequired = true;
|
||||
}
|
||||
|
||||
$this->overflow_scrollbars = false;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Decoders hack for retrocompability.
|
||||
*
|
||||
* @param array $decoder Values.
|
||||
*
|
||||
* @return array Returns the values with the correct key.
|
||||
*/
|
||||
public function decoders(array $decoder): array
|
||||
{
|
||||
$values = [];
|
||||
// Retrieve global - common inputs.
|
||||
$values = parent::decoders($decoder);
|
||||
|
||||
$values['agentsDataMatrix'] = [];
|
||||
if (isset($decoder['agentsDataMatrix']) === true) {
|
||||
if (isset($decoder['agentsDataMatrix'][0]) === true
|
||||
&& empty($decoder['agentsDataMatrix']) === false
|
||||
) {
|
||||
$values['agentsDataMatrix'] = explode(
|
||||
',',
|
||||
$decoder['agentsDataMatrix'][0]
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if (isset($decoder['selectionDataMatrix']) === true) {
|
||||
$values['selectionDataMatrix'] = $decoder['selectionDataMatrix'];
|
||||
}
|
||||
|
||||
$values['moduleDataMatrix'] = [];
|
||||
if (isset($decoder['moduleDataMatrix']) === true) {
|
||||
if (empty($decoder['moduleDataMatrix']) === false) {
|
||||
$values['moduleDataMatrix'] = $decoder['moduleDataMatrix'];
|
||||
}
|
||||
}
|
||||
|
||||
if (isset($decoder['formatData']) === true) {
|
||||
$values['formatData'] = $decoder['formatData'];
|
||||
}
|
||||
|
||||
$values['label'] = 'module';
|
||||
if (isset($decoder['label']) === true) {
|
||||
$values['label'] = $decoder['label'];
|
||||
}
|
||||
|
||||
if (isset($decoder['fontColor']) === true) {
|
||||
$values['fontColor'] = $decoder['fontColor'];
|
||||
}
|
||||
|
||||
if (isset($decoder['period']) === true) {
|
||||
$values['period'] = $decoder['period'];
|
||||
}
|
||||
|
||||
if (isset($decoder['slice']) === true) {
|
||||
$values['slice'] = $decoder['slice'];
|
||||
}
|
||||
|
||||
if (isset($decoder['limit']) === true) {
|
||||
$values['limit'] = $decoder['limit'];
|
||||
}
|
||||
|
||||
return $values;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Generates inputs for form (specific).
|
||||
*
|
||||
* @return array Of inputs.
|
||||
*
|
||||
* @throws Exception On error.
|
||||
*/
|
||||
public function getFormInputs(): array
|
||||
{
|
||||
global $config;
|
||||
|
||||
$values = $this->values;
|
||||
|
||||
// Retrieve global - common inputs.
|
||||
$inputs = parent::getFormInputs();
|
||||
|
||||
$blocks = [
|
||||
'row1',
|
||||
'row2',
|
||||
];
|
||||
|
||||
$inputs['blocks'] = $blocks;
|
||||
|
||||
foreach ($inputs as $kInput => $vInput) {
|
||||
$inputs['inputs']['row1'][] = $vInput;
|
||||
}
|
||||
|
||||
if (isset($values['formatData']) === false) {
|
||||
$values['formatData'] = 1;
|
||||
}
|
||||
|
||||
// Format Data.
|
||||
$inputs['inputs']['row1'][] = [
|
||||
'label' => __('Format Data'),
|
||||
'arguments' => [
|
||||
'name' => 'formatData',
|
||||
'id' => 'formatData',
|
||||
'type' => 'switch',
|
||||
'value' => $values['formatData'],
|
||||
],
|
||||
];
|
||||
|
||||
if (isset($values['period']) === false) {
|
||||
$values['period'] = SECONDS_1DAY;
|
||||
}
|
||||
|
||||
$inputs['inputs']['row1'][] = [
|
||||
'label' => __('Periodicity'),
|
||||
'arguments' => [
|
||||
'name' => 'period',
|
||||
'type' => 'interval',
|
||||
'value' => $values['period'],
|
||||
'nothing' => __('None'),
|
||||
'nothing_value' => 0,
|
||||
'style_icon' => 'flex-grow: 0',
|
||||
],
|
||||
];
|
||||
|
||||
if (isset($values['slice']) === false) {
|
||||
$values['slice'] = SECONDS_5MINUTES;
|
||||
}
|
||||
|
||||
$inputs['inputs']['row1'][] = [
|
||||
'label' => __('Interval'),
|
||||
'arguments' => [
|
||||
'name' => 'slice',
|
||||
'type' => 'interval',
|
||||
'value' => $values['slice'],
|
||||
'nothing' => __('None'),
|
||||
'nothing_value' => 0,
|
||||
'style_icon' => 'flex-grow: 0',
|
||||
],
|
||||
];
|
||||
|
||||
if (isset($values['limit']) === false) {
|
||||
$values['limit'] = $config['block_size'];
|
||||
}
|
||||
|
||||
// Limit Default block_size.
|
||||
$blockSizeD4 = \format_integer_round(($config['block_size'] / 4));
|
||||
$blockSizeD2 = \format_integer_round(($config['block_size'] / 2));
|
||||
$fields = [
|
||||
$config['block_size'] => $config['block_size'],
|
||||
$blockSizeD4 => $blockSizeD4,
|
||||
$blockSizeD2 => $blockSizeD2,
|
||||
($config['block_size'] * 2) => ($config['block_size'] * 2),
|
||||
($config['block_size'] * 3) => ($config['block_size'] * 3),
|
||||
];
|
||||
|
||||
$inputs['inputs']['row1'][] = [
|
||||
'label' => \__('Limit'),
|
||||
'arguments' => [
|
||||
'type' => 'select',
|
||||
'fields' => $fields,
|
||||
'class' => 'event-widget-input',
|
||||
'name' => 'limit',
|
||||
'selected' => $values['limit'],
|
||||
'return' => true,
|
||||
],
|
||||
];
|
||||
|
||||
// Type Label.
|
||||
$fields = [
|
||||
'module' => __('Module'),
|
||||
'agent' => __('Agent'),
|
||||
'agent_module' => __('Agent / module'),
|
||||
];
|
||||
|
||||
$inputs['inputs']['row2'][] = [
|
||||
'label' => __('Label'),
|
||||
'arguments' => [
|
||||
'type' => 'select',
|
||||
'fields' => $fields,
|
||||
'name' => 'label',
|
||||
'selected' => $values['label'],
|
||||
'return' => true,
|
||||
],
|
||||
];
|
||||
|
||||
$inputs['inputs']['row2'][] = [
|
||||
'arguments' => [
|
||||
'type' => 'select_multiple_modules_filtered_select2',
|
||||
'agent_values' => agents_get_agents_selected(0),
|
||||
'agent_name' => 'agentsDataMatrix[]',
|
||||
'agent_ids' => $values['agentsDataMatrix'],
|
||||
'selectionModules' => $values['selectionDataMatrix'],
|
||||
'selectionModulesNameId' => 'selectionDataMatrix',
|
||||
'modules_ids' => $values['moduleDataMatrix'],
|
||||
'modules_name' => 'moduleDataMatrix[]',
|
||||
'notStringModules' => true,
|
||||
],
|
||||
];
|
||||
|
||||
return $inputs;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get Post for widget.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getPost():array
|
||||
{
|
||||
// Retrieve global - common inputs.
|
||||
$values = parent::getPost();
|
||||
|
||||
$values['agentsDataMatrix'] = \get_parameter(
|
||||
'agentsDataMatrix',
|
||||
[]
|
||||
);
|
||||
$values['selectionDataMatrix'] = \get_parameter(
|
||||
'selectionDataMatrix',
|
||||
0
|
||||
);
|
||||
|
||||
$values['moduleDataMatrix'] = \get_parameter(
|
||||
'moduleDataMatrix'
|
||||
);
|
||||
|
||||
$agColor = [];
|
||||
if (isset($values['agentsDataMatrix'][0]) === true
|
||||
&& empty($values['agentsDataMatrix'][0]) === false
|
||||
) {
|
||||
$agColor = explode(',', $values['agentsDataMatrix'][0]);
|
||||
}
|
||||
|
||||
$agModule = [];
|
||||
if (isset($values['moduleDataMatrix'][0]) === true
|
||||
&& empty($values['moduleDataMatrix'][0]) === false
|
||||
) {
|
||||
$agModule = explode(',', $values['moduleDataMatrix'][0]);
|
||||
}
|
||||
|
||||
$values['moduleDataMatrix'] = \get_same_modules_all(
|
||||
$agColor,
|
||||
$agModule
|
||||
);
|
||||
|
||||
$values['formatData'] = \get_parameter_switch('formatData');
|
||||
|
||||
$values['fontColor'] = \get_parameter('fontColor', '#2c3e50');
|
||||
$values['label'] = \get_parameter('label', 'module');
|
||||
|
||||
$values['period'] = \get_parameter('period', 0);
|
||||
$values['slice'] = \get_parameter('slice', 0);
|
||||
$values['limit'] = \get_parameter('limit', 20);
|
||||
|
||||
return $values;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Draw widget.
|
||||
*
|
||||
* @return string;
|
||||
*/
|
||||
public function load()
|
||||
{
|
||||
$this->size = parent::getSize();
|
||||
|
||||
$output = '';
|
||||
if (count($this->values['moduleDataMatrix']) > self::MAX_MODULES) {
|
||||
$output .= '<div class="container-center">';
|
||||
$output .= \ui_print_info_message(
|
||||
__(
|
||||
'The maximum number of modules to display is %d, please reconfigure the widget.',
|
||||
self::MAX_MODULES
|
||||
),
|
||||
'',
|
||||
true
|
||||
);
|
||||
$output .= '</div>';
|
||||
return $output;
|
||||
}
|
||||
|
||||
if (is_metaconsole() === true) {
|
||||
$modules_nodes = array_reduce(
|
||||
$this->values['moduleDataMatrix'],
|
||||
function ($carry, $item) {
|
||||
$explode = explode('|', $item);
|
||||
$carry[$explode[0]][] = $explode[1];
|
||||
return $carry;
|
||||
},
|
||||
[]
|
||||
);
|
||||
|
||||
$modules = [];
|
||||
foreach ($modules_nodes as $n => $mod) {
|
||||
try {
|
||||
$node = new Node((int) $n);
|
||||
$node->connect();
|
||||
$node_mods = $this->getInfoModules($mod);
|
||||
if (empty($node_mods) === false) {
|
||||
foreach ($node_mods as $value) {
|
||||
$value['id_node'] = $n;
|
||||
$value['server_name'] = $node->toArray()['server_name'];
|
||||
$modules[] = $value;
|
||||
}
|
||||
}
|
||||
|
||||
$node->disconnect();
|
||||
} catch (\Exception $e) {
|
||||
// Unexistent agent.
|
||||
$node->disconnect();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$modules = $this->getInfoModules(
|
||||
$this->values['moduleDataMatrix']
|
||||
);
|
||||
}
|
||||
|
||||
if ($modules !== false && empty($modules) === false) {
|
||||
// Datatables list.
|
||||
try {
|
||||
$info_columns = $this->columns($modules);
|
||||
$columns = $info_columns['columns'];
|
||||
$column_names = $info_columns['column_names'];
|
||||
$columns_sort = $info_columns['columns_sort'];
|
||||
|
||||
$tableId = 'dataMatrix_'.$this->dashboardId.'_'.$this->cellId;
|
||||
// Load datatables user interface.
|
||||
ui_print_datatable(
|
||||
[
|
||||
'id' => $tableId,
|
||||
'class' => 'info_table',
|
||||
'style' => 'width: 100%',
|
||||
'columns' => $columns,
|
||||
'column_names' => $column_names,
|
||||
'ajax_url' => 'include/ajax/module',
|
||||
'ajax_data' => [
|
||||
'get_data_dataMatrix' => 1,
|
||||
'table_id' => $tableId,
|
||||
'period' => $this->values['period'],
|
||||
'slice' => $this->values['slice'],
|
||||
'formatData' => $this->values['formatData'],
|
||||
'modules' => json_encode($modules),
|
||||
],
|
||||
'default_pagination' => $this->values['limit'],
|
||||
'no_sortable_columns' => $columns_sort,
|
||||
'order' => [
|
||||
'field' => 'date',
|
||||
'direction' => 'desc',
|
||||
],
|
||||
'csv' => 0,
|
||||
]
|
||||
);
|
||||
} catch (\Exception $e) {
|
||||
echo $e->getMessage();
|
||||
}
|
||||
} else {
|
||||
$output = '';
|
||||
$output .= '<div class="container-center">';
|
||||
$output .= \ui_print_info_message(
|
||||
__('Not found modules'),
|
||||
'',
|
||||
true
|
||||
);
|
||||
$output .= '</div>';
|
||||
|
||||
return $output;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get info modules.
|
||||
*
|
||||
* @param array $modules Modules.
|
||||
*
|
||||
* @return array Data.
|
||||
*/
|
||||
private function getInfoModules(array $modules): array
|
||||
{
|
||||
$where = sprintf(
|
||||
'tagente_modulo.id_agente_modulo IN (%s)
|
||||
AND tagente_modulo.delete_pending = 0',
|
||||
implode(',', $modules)
|
||||
);
|
||||
|
||||
$sql = sprintf(
|
||||
'SELECT tagente_modulo.id_agente_modulo AS `id`,
|
||||
tagente_modulo.nombre AS `name`,
|
||||
tagente_modulo.unit AS `unit`,
|
||||
tagente_modulo.min_warning AS w_min,
|
||||
tagente_modulo.max_warning AS w_max,
|
||||
tagente_modulo.str_warning AS w_str,
|
||||
tagente_modulo.min_critical AS c_min,
|
||||
tagente_modulo.max_critical AS c_max,
|
||||
tagente_modulo.str_critical AS c_str,
|
||||
tagente_modulo.id_tipo_modulo AS type_module,
|
||||
tagente_estado.datos AS `data`,
|
||||
tagente_estado.timestamp AS `timestamp`,
|
||||
tagente_estado.estado AS `status`,
|
||||
tagente.alias
|
||||
FROM tagente_modulo
|
||||
LEFT JOIN tagente_estado
|
||||
ON tagente_modulo.id_agente_modulo = tagente_estado.id_agente_modulo
|
||||
LEFT JOIN tagente
|
||||
ON tagente_modulo.id_agente = tagente.id_agente
|
||||
WHERE %s',
|
||||
$where
|
||||
);
|
||||
|
||||
$modules = db_get_all_rows_sql($sql);
|
||||
|
||||
if ($modules === false) {
|
||||
$modules = [];
|
||||
}
|
||||
|
||||
return $modules;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get columns.
|
||||
*
|
||||
* @param array $modules Info modules.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
private function columns(array $modules)
|
||||
{
|
||||
$columns = [];
|
||||
$columns[] = 'date';
|
||||
$column_names = [];
|
||||
$column_names[] = __('Date');
|
||||
$columns_sort = [];
|
||||
$columns_sort[] = 0;
|
||||
foreach ($modules as $key => $module) {
|
||||
$columns[] = 'Column-'.$module['id'];
|
||||
// Module name.
|
||||
$name = '';
|
||||
switch ($this->values['label']) {
|
||||
case 'agent':
|
||||
$name = $module['alias'];
|
||||
break;
|
||||
|
||||
case 'agent_module':
|
||||
$name = $module['alias'].' / '.$module['name'];
|
||||
break;
|
||||
|
||||
default:
|
||||
case 'module':
|
||||
$name = $module['name'];
|
||||
break;
|
||||
}
|
||||
|
||||
$columns_sort[] = ($key + 1);
|
||||
$column_names[] = $name;
|
||||
}
|
||||
|
||||
$data = [
|
||||
'columns' => $columns,
|
||||
'column_names' => $column_names,
|
||||
'columns_sort' => $columns_sort,
|
||||
];
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get description.
|
||||
*
|
||||
* @return string.
|
||||
*/
|
||||
public static function getDescription()
|
||||
{
|
||||
return __('Data Matrix');
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get Name.
|
||||
*
|
||||
* @return string.
|
||||
*/
|
||||
public static function getName()
|
||||
{
|
||||
return 'DataMatrix';
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get size Modal Configuration.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getSizeModalConfiguration(): array
|
||||
{
|
||||
$size = [
|
||||
'width' => (is_metaconsole() === true) ? 1000 : 900,
|
||||
'height' => 480,
|
||||
];
|
||||
|
||||
return $size;
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -9156,3 +9156,16 @@ div#err_msg_centralised {
|
|||
.tag-editor .tag-editor-delete:hover i:before {
|
||||
color: #ccc !important;
|
||||
}
|
||||
|
||||
.snmp-td {
|
||||
padding: 0 !important;
|
||||
height: 0;
|
||||
}
|
||||
|
||||
.snmp-div {
|
||||
height: 100%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: start;
|
||||
padding-left: 10px;
|
||||
}
|
||||
|
|
|
@ -129,7 +129,7 @@
|
|||
<div style='height: 10px'>
|
||||
<?php
|
||||
$version = '7.0NG.767';
|
||||
$build = '221219';
|
||||
$build = '221221';
|
||||
$banner = "v$version Build $build";
|
||||
|
||||
error_reporting(0);
|
||||
|
|
|
@ -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&sec2=godmode/agentes/configurar_agente&id_agente='.$agent['id_agente'].'">'.__('Edit').'</a>';
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
}
|
|
@ -102,10 +102,16 @@ if (isset($fb64) === true) {
|
|||
);
|
||||
}
|
||||
|
||||
$id_group_filter = get_parameter(
|
||||
'filter[id_group_filter]',
|
||||
($filter['id_group_filter'] ?? '')
|
||||
);
|
||||
|
||||
$id_group = get_parameter(
|
||||
'filter[id_group]',
|
||||
($filter['id_group'] ?? '')
|
||||
($filter['id_group'] ?? $id_group_filter)
|
||||
);
|
||||
|
||||
$event_type = get_parameter(
|
||||
'filter[event_type]',
|
||||
($filter['event_type'] ?? '')
|
||||
|
@ -183,7 +189,7 @@ $search_secondary_groups = get_parameter(
|
|||
);
|
||||
$search_recursive_groups = get_parameter(
|
||||
'filter[search_recursive_groups]',
|
||||
0
|
||||
($filter['search_recursive_groups'] ?? '')
|
||||
);
|
||||
$id_group_filter = get_parameter(
|
||||
'filter[id_group_filter]',
|
||||
|
|
|
@ -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');
|
||||
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -3,7 +3,7 @@
|
|||
#
|
||||
%define name pandorafms_console
|
||||
%define version 7.0NG.767
|
||||
%define release 221219
|
||||
%define release 221221
|
||||
|
||||
# User and Group under which Apache is running
|
||||
%define httpd_name httpd
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
#
|
||||
%define name pandorafms_console
|
||||
%define version 7.0NG.767
|
||||
%define release 221219
|
||||
%define release 221221
|
||||
|
||||
# User and Group under which Apache is running
|
||||
%define httpd_name httpd
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
#
|
||||
%define name pandorafms_console
|
||||
%define version 7.0NG.767
|
||||
%define release 221219
|
||||
%define release 221221
|
||||
%define httpd_name httpd
|
||||
# User and Group under which Apache is running
|
||||
%define httpd_name apache2
|
||||
|
|
File diff suppressed because one or more lines are too long
|
@ -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 .= ' » '.__($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);
|
|
@ -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').' » '.__('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,
|
||||
],
|
||||
],
|
||||
],
|
||||
]
|
||||
);
|
||||
}
|
|
@ -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').' » '.$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.' '.html_print_image('images/dot_blue.png', true, ['border' => '0', 'title' => __('Quiet'), 'alt' => '']);
|
||||
} else {
|
||||
$agent_name = "<em'>".$agent_name.' '.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> '.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 .= ' '.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 .= ' ';
|
||||
$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);
|
|
@ -1,5 +1,5 @@
|
|||
package: pandorafms-server
|
||||
Version: 7.0NG.767-221219
|
||||
Version: 7.0NG.767-221221
|
||||
Architecture: all
|
||||
Priority: optional
|
||||
Section: admin
|
||||
|
|
|
@ -14,7 +14,7 @@
|
|||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
|
||||
pandora_version="7.0NG.767-221219"
|
||||
pandora_version="7.0NG.767-221221"
|
||||
|
||||
package_cpan=0
|
||||
package_pandora=1
|
||||
|
|
|
@ -46,7 +46,7 @@ our @EXPORT = qw(
|
|||
|
||||
# version: Defines actual version of Pandora Server for this module only
|
||||
my $pandora_version = "7.0NG.767";
|
||||
my $pandora_build = "221219";
|
||||
my $pandora_build = "221221";
|
||||
our $VERSION = $pandora_version." ".$pandora_build;
|
||||
|
||||
# Setup hash
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -34,7 +34,7 @@ our @ISA = qw(Exporter);
|
|||
|
||||
# version: Defines actual version of Pandora Server for this module only
|
||||
my $pandora_version = "7.0NG.767";
|
||||
my $pandora_build = "221219";
|
||||
my $pandora_build = "221221";
|
||||
our $VERSION = $pandora_version." ".$pandora_build;
|
||||
|
||||
our %EXPORT_TAGS = ( 'all' => [ qw() ] );
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
#
|
||||
%define name pandorafms_server
|
||||
%define version 7.0NG.767
|
||||
%define release 221219
|
||||
%define release 221221
|
||||
|
||||
Summary: Pandora FMS Server
|
||||
Name: %{name}
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
#
|
||||
%define name pandorafms_server
|
||||
%define version 7.0NG.767
|
||||
%define release 221219
|
||||
%define release 221221
|
||||
|
||||
Summary: Pandora FMS Server
|
||||
Name: %{name}
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
# **********************************************************************
|
||||
|
||||
PI_VERSION="7.0NG.767"
|
||||
PI_BUILD="221219"
|
||||
PI_BUILD="221221"
|
||||
|
||||
MODE=$1
|
||||
if [ $# -gt 1 ]; then
|
||||
|
|
|
@ -35,7 +35,7 @@ use PandoraFMS::Config;
|
|||
use PandoraFMS::DB;
|
||||
|
||||
# version: define current version
|
||||
my $version = "7.0NG.767 Build 221219";
|
||||
my $version = "7.0NG.767 Build 221221";
|
||||
|
||||
# Pandora server configuration
|
||||
my %conf;
|
||||
|
|
|
@ -36,7 +36,7 @@ use Encode::Locale;
|
|||
Encode::Locale::decode_argv;
|
||||
|
||||
# version: define current version
|
||||
my $version = "7.0NG.767 Build 221219";
|
||||
my $version = "7.0NG.767 Build 221221";
|
||||
|
||||
# save program name for logging
|
||||
my $progname = basename($0);
|
||||
|
@ -1296,7 +1296,6 @@ sub help_screen_line($$$){
|
|||
|
||||
sub check_values($) {
|
||||
my ($check) = @_;
|
||||
use experimental 'smartmatch';
|
||||
|
||||
my $arg_cont = 2;
|
||||
my $cont = 0;
|
||||
|
@ -1316,7 +1315,7 @@ sub check_values($) {
|
|||
|
||||
# Check values.
|
||||
if (defined($check->[$cont]->{'values'})) {
|
||||
if (!($args[$arg_cont] ~~ $check->[$cont]->{'values'})) {
|
||||
if (!(is_in_array($check->[$cont]->{'values'}, $args[$arg_cont]))) {
|
||||
print "\nError: value `$args[$arg_cont]` is not valid for $check->[$cont]->{'name'}\n";
|
||||
print "\tAvailable options: \t$check->[$cont]->{'values'}->[0]";
|
||||
if (defined($check->[$cont]->{'text_extra'}->[0])) {
|
||||
|
|
Loading…
Reference in New Issue