__('Any')]; $none = ['0' => __('None')]; $templates = array_rekey( db_fetch_assoc('SELECT DISTINCT ht.id, ht.name FROM host_template AS ht ORDER BY name'), 'id', 'name' ); $templates = $any + $none + $templates; $status = [ '-1' => __('Any'), '-3' => __('Enabled'), '-2' => __('Disabled'), '-4' => __('Not Up'), '3' => __('Up'), '1' => __('Down'), '2' => __('Recovering'), '0' => __('Unknown') ]; return [ 'rows' => [ [ 'filter' => [ 'method' => 'textbox', 'friendly_name' => __('Search'), 'filter' => FILTER_DEFAULT, 'placeholder' => __('Enter a search term'), 'size' => '30', 'default' => '', 'pageset' => true, 'max_length' => '120', 'value' => '' ], 'template_id' => [ 'method' => 'drop_array', 'friendly_name' => __('Template'), 'filter' => FILTER_VALIDATE_INT, 'default' => '-1', 'pageset' => true, 'array' => $templates, 'value' => '-1' ], 'status' => [ 'method' => 'drop_array', 'friendly_name' => __('Status'), 'filter' => FILTER_VALIDATE_INT, 'default' => '-1', 'pageset' => true, 'array' => $status, 'value' => '-1' ], 'rows' => [ 'method' => 'drop_array', 'friendly_name' => $rows_label, 'filter' => FILTER_VALIDATE_INT, 'default' => '-1', 'pageset' => true, 'array' => $item_rows, 'value' => '-1' ] ] ], 'buttons' => [ 'go' => [ 'method' => 'submit', 'display' => __('Go'), 'title' => __('Apply Filter to Table'), ], 'clear' => [ 'method' => 'button', 'display' => __('Clear'), 'title' => __('Reset Filter to Default Values'), ] ], 'sort' => [ 'sort_column' => 'description', 'sort_direction' => 'ASC' ] ]; } /** * Processes and sanitizes the filter for drawing tree devices. * * @param bool $render Whether to render the output. Default is false. * @param string $url The URL to be used in the process. Default is an empty string. * * @return void */ function draw_tree_devices_filter(bool $render = false, string $url = '') : void { $filters = create_tree_devices_filter(); $header = __('Matching Devices'); // create the page filter $pageFilter = new CactiTableFilter($header, $url, 'forms', 'sess_auto_tdm'); $pageFilter->set_filter_array($filters); if ($render) { $pageFilter->render(); } else { $pageFilter->sanitize(); } } /** * Displays the hosts that match the given rule. * * @param array $rule The rule to match hosts against. The type of this parameter depends on the rule_type. * @param int $rule_type The type of rule being applied. This determines how the rule is interpreted. * @param string $url The URL to be used for displaying the matching hosts. * * @return void */ function display_matching_hosts(array $rule, int $rule_type, string $url) : void { global $device_actions, $item_rows; draw_tree_devices_filter(true, $url); $details = automation_get_matching_device_sql($rule, $rule_type); $host_graphs = array_rekey( db_fetch_assoc('SELECT host_id, COUNT(*) AS graphs FROM graph_local GROUP BY host_id'), 'host_id', 'graphs' ); $host_data_sources = array_rekey( db_fetch_assoc('SELECT host_id, COUNT(*) AS data_sources FROM data_local GROUP BY host_id'), 'host_id', 'data_sources' ); $total_rows = cacti_sizeof(db_fetch_assoc($details['rows_query'], false)); $sort_column = sanitize_sql_column((string) grv('sort_column'), ''); $sort_column = api_automation_column_exists($sort_column, ['host', 'graph_local', 'sites', 'graph_templates', 'graph_templates_graph', 'host_template']) ? $sort_column : 'description'; $sort_direction = strtoupper((string) grv('sort_direction')) === 'DESC' ? 'DESC' : 'ASC'; $sortby = str_ends_with($sort_column, 'hostname') ? 'INET_ATON(' . $sort_column . ')' : $sort_column; $sql_query = $details['rows_query'] . ' ORDER BY ' . $sortby . ' ' . $sort_direction . ' LIMIT ' . ($details['rows'] * (gfrv('page') - 1)) . ',' . $details['rows']; $hosts = db_fetch_assoc($sql_query, false); $nav = html_nav_bar($url, MAX_DISPLAY_PAGES, gfrv('page'), $details['rows'], $total_rows, 7, __('Devices'), 'page', 'main'); print $nav; html_start_box('', '100%', false, 3, 'center', ''); $display_text = [ 'description' => [ 'display' => __('Description'), 'sort' => 'ASC' ], 'hostname' => [ 'display' => __('Hostname'), 'sort' => 'ASC' ], 'site_name' => [ 'display' => __('Site'), 'sort' => 'ASC' ], 'location' => [ 'display' => __('Location'), 'sort' => 'ASC' ], 'status' => [ 'display' => __('Status'), 'sort' => 'ASC', 'align' => 'center' ], 'host_template_name' => [ 'display' => __('Device Template Name'), 'sort' => 'ASC' ], 'id' => [ 'display' => __('ID'), 'sort' => 'ASC', 'align' => 'right' ], 'nosort1' => [ 'display' => __('Graphs'), 'align' => 'right' ], 'nosort2' => [ 'display' => __('Data Sources'), 'align' => 'right' ], ]; html_header_sort($display_text, grv('sort_column'), grv('sort_direction'), 1, $url . '?action=edit&id=' . grv('id') . '&page=' . gfrv('page')); if (cacti_sizeof($hosts)) { foreach ($hosts as $host) { form_alternate_row('line' . $host['host_id'], true); form_selectable_cell(filter_value($host['description'], grv('filter'), 'host.php?action=edit&id=' . $host['host_id']), $host['host_id']); form_selectable_cell(filter_value($host['hostname'], grv('filter')), $host['host_id']); form_selectable_cell(filter_value($host['site_name'], grv('filter')), $host['host_id']); form_selectable_cell(filter_value($host['location'], grv('filter')), $host['host_id']); form_selectable_cell(get_colored_device_status((($host['disabled'] == 'on' || $host['site_disabled'] == 'on') ? true : false), $host['status']), $host['host_id'], '', 'center'); form_selectable_cell(filter_value($host['host_template_name'], grv('filter')), $host['host_id']); form_selectable_cell(round(($host['host_id']), 2), $host['host_id'], '', 'right'); form_selectable_cell(($host_graphs[$host['host_id']] ?? 0), $host['host_id'], '', 'right'); form_selectable_cell(($host_data_sources[$host['host_id']] ?? 0), $host['host_id'], '', 'right'); form_end_row(); } } else { print '
'; print_r($replacement); print ''; for ($j = 0; cacti_sizeof($replacement); $j++) { $title = array_shift($replacement); $parent_tree_item_id = (int) create_header_node($title ?? '', $rule, $tree_item, $parent_tree_item_id); cacti_log($function . " - object: '" . $object . "', Header: '" . $title . "', hooked at: " . $parent_tree_item_id, false, 'AUTOM8 TRACE', POLLER_VERBOSITY_HIGH); } } return $parent_tree_item_id; } /** * Create a single tree header node * * @param string $title The title of the header node. * @param array $rule The rule associated with the header node. * @param array $item The item associated with the header node. * @param int $parent_tree_item_id The ID of the parent tree item. * * @return int|bool */ function create_header_node(string $title, array $rule, array $item, int $parent_tree_item_id) : int|bool { $id = 0; // create a new entry $local_graph_id = 0; // headers don't need no graph_id $host_id = 0; // or a host_id $site_id = 0; // or a site_id $propagate = ($item['propagate_changes'] != ''); $function = automation_function_with_pid(__FUNCTION__); if (api_tree_branch_exists($rule['tree_id'], $parent_tree_item_id, $title)) { $new_item = api_tree_get_branch_id($rule['tree_id'], $parent_tree_item_id, $title); cacti_log('NOTE: ' . $function . ' Parent[' . $parent_tree_item_id . '] Tree Item - Already Exists', false, 'AUTOM8', POLLER_VERBOSITY_MEDIUM); } else { $new_item = api_tree_item_save($id, $rule['tree_id'], TREE_ITEM_TYPE_HEADER, $parent_tree_item_id, $title, $local_graph_id, $host_id, $site_id, $rule['host_grouping_type'], $item['sort_type'], $propagate); if ($new_item > 0) { cacti_log('NOTE: ' . $function . ' Parent[' . $parent_tree_item_id . '] Tree Item - Added - id: (' . $new_item . ') Title: (' . $title . ')', false, 'AUTOM8'); } else { cacti_log('WARNING: ' . $function . ' Parent[' . $parent_tree_item_id . '] Tree Item - Not Added', false, 'AUTOM8'); } } return $new_item; } /** * Add a device to the tree * * @param int $host_id The ID of the host. * @param int $parent The parent node or identifier. * @param array $rule The rule set to apply for the device node creation. * * @return int id of new item */ function create_device_node(int $host_id, int $parent, array $rule) : int { $id = 0; // create a new entry $local_graph_id = 0; // hosts don't need no graph_id $site_id = 0; // hosts don't need no site_id $title = ''; // nor a title $sort_type = 0; // nor a sort type $propagate = false; // nor a propagation flag $function = automation_function_with_pid(__FUNCTION__); if (api_tree_host_exists($rule['tree_id'], $parent, $host_id)) { $new_item = (int) db_fetch_cell_prepared('SELECT id FROM graph_tree_items WHERE host_id = ? AND parent = ? AND graph_tree_id = ?', [$host_id, $parent, $rule['tree_id']]); cacti_log('NOTE: ' . $function . ' Device[' . $host_id . '] Tree Item - Already Exists', false, 'AUTOM8', POLLER_VERBOSITY_MEDIUM); } else { $new_item = (int) api_tree_item_save($id, $rule['tree_id'], TREE_ITEM_TYPE_HOST, $parent, $title, $local_graph_id, $host_id, $site_id, $rule['host_grouping_type'], $sort_type, $propagate); if ($new_item > 0) { cacti_log('NOTE: ' . $function . ' Device[' . $host_id . '] Tree Item - Added - Parent[' . $parent . '] Id[' . $new_item . ']', false, 'AUTOM8'); } else { cacti_log('WARNING: ' . $function . ' Device[' . $host_id . '] Tree Item - Not Added', false, 'AUTOM8'); } } return $new_item; } /** * Creates a site node. * * @param int $site_id The ID of the site. * @param int $parent The parent node. * @param array $rule The rule array to apply. * * @return int id of new item */ function create_site_node(int $site_id, int $parent, array $rule) : int { $id = 0; // create a new entry $local_graph_id = 0; // hosts don't need no graph_id $host_id = 0; // hosts don't need no host_id $title = ''; // nor a title $sort_type = 0; // nor a sort type $propagate = false; // nor a propagation flag $function = 'Function[' . __FUNCTION__ . ']'; if (api_tree_site_exists($rule['tree_id'], $parent, $site_id)) { $new_item = (int) db_fetch_cell_prepared('SELECT id FROM graph_tree_items WHERE site_id = ? AND parent = ? AND graph_tree_id = ?', [$site_id, $parent, $rule['tree_id']]); cacti_log('NOTE: ' . $function . ' Site[' . $host_id . '] Tree Item - Already Exists', false, 'AUTOM8', POLLER_VERBOSITY_MEDIUM); } else { $new_item = (int) api_tree_item_save($id, $rule['tree_id'], TREE_ITEM_TYPE_HOST, $parent, $title, $local_graph_id, $host_id, $site_id, $rule['host_grouping_type'], $sort_type, $propagate); if ($new_item > 0) { cacti_log('NOTE: ' . $function . ' Site[' . $site_id . '] Tree Item - Added - id: (' . $new_item . ')', false, 'AUTOM8'); } else { cacti_log('WARNING: ' . $function . ' Site[' . $site_id . '] Tree Item - Not Added', false, 'AUTOM8'); } } return $new_item; } /** * Add a device to the tree * * @param int $graph_id The ID of the graph. * @param int $parent The parent node. * @param array $rule The rule to apply to the graph node. * * @return int id of new item */ function create_graph_node(int $graph_id, int $parent, array $rule) : int { $id = 0; // create a new entry $host_id = 0; // graphs don't need no host_id $site_id = 0; // graphs don't need no site_id $title = ''; // nor a title $sort_type = 0; // nor a sort type $propagate = false; // nor a propagation flag $function = automation_function_with_pid(__FUNCTION__); if (api_tree_graph_exists($rule['tree_id'], $parent, $graph_id)) { $new_item = (int) db_fetch_cell_prepared('SELECT id FROM graph_tree_items WHERE local_graph_id = ? AND parent = ? AND graph_tree_id = ?', [$graph_id, $parent, $rule['tree_id']]); cacti_log('NOTE: ' . $function . ' Graph[' . $graph_id . '] Tree Item - Already Exists', false, 'AUTOM8', POLLER_VERBOSITY_MEDIUM); } else { $new_item = (int) api_tree_item_save($id, $rule['tree_id'], TREE_ITEM_TYPE_GRAPH, $parent, $title, $graph_id, $host_id, $site_id, $rule['host_grouping_type'], $sort_type, $propagate); if ($new_item > 0) { cacti_log('NOTE: ' . $function . ' Graph[' . $graph_id . '] Tree Item - Added - id: (' . $new_item . ')', false, 'AUTOM8'); } else { cacti_log('WARNING: ' . $function . ' Graph[' . $graph_id . '] Tree Item - Not Added', false, 'AUTOM8'); } } return $new_item; } /** * Executes tasks that need to be performed at the end of the poller process. * * @return void */ function automation_poller_bottom() : void { $command_string = cacti_escapeshellcmd((string) read_config_option('path_php_binary')); // If its not set, just assume its in the path if (trim($command_string) == '') { $command_string = 'php'; } $extra_args = ' -q ' . cacti_escapeshellarg(CACTI_PATH_BASE . '/poller_automation.php') . ' -M'; exec_background($command_string, $extra_args); } /** * Adds a device to the automation system. * * @param array $device An associative array containing device details. * @param bool $web Optional. Indicates if the request is coming from a web interface. Default is false. * * @return int The ID of the device that was added. */ function automation_add_device(array $device, bool $web = false) : int { global $plugins; $template_id = $device['host_template']; $snmp_sysName = $device['snmp_sysName']; $description = $device['description'] ?? $snmp_sysName != '' ? $snmp_sysName : ($device['hostname'] == '' ? $device['ip'] : $device['hostname']); $poller_id = $device['poller_id'] ?? read_config_option('default_poller'); $site_id = $device['site_id'] ?? read_config_option('default_site'); $ip = $device['ip'] ?? $device['ip_address']; $snmp_community = $device['snmp_community']; $snmp_ver = $device['snmp_version']; $snmp_username = $device['snmp_username']; $snmp_password = $device['snmp_password']; $snmp_port = $device['snmp_port']; $snmp_timeout = $device['snmp_timeout'] ?? read_config_option('snmp_timeout'); $disable = ''; $availability_method = $device['availability_method'] ?? read_config_option('availability_method'); $ping_method = $device['ping_method'] ?? read_config_option('ping_method'); $ping_port = $device['ping_port'] ?? read_config_option('ping_port'); $ping_timeout = $device['ping_timeout'] ?? read_config_option('ping_timeout'); $ping_retries = $device['ping_retries'] ?? read_config_option('ping_retries'); $notes = $device['notes'] ?? __('Added by Cacti Automation'); $snmp_auth_protocol = $device['snmp_auth_protocol']; $snmp_priv_passphrase = $device['snmp_priv_passphrase']; $snmp_priv_protocol = $device['snmp_priv_protocol']; $snmp_context = $device['snmp_context']; $snmp_engine_id = $device['snmp_engine_id']; $max_oids = $device['max_oids'] ?? 10; $device_threads = $device['device_threads'] ?? 1; $external_id = $device['external_id'] ?? ''; $location = $device['location'] ?? ''; $bulk_walk_size = $device['bulk_walk_size'] ?? -1; automation_debug(' - Adding Device'); $host_id = api_device_save(0, $template_id, $description, $ip, $snmp_community, $snmp_ver, $snmp_username, $snmp_password, $snmp_port, $snmp_timeout, $disable, $availability_method, $ping_method, $ping_port, $ping_timeout, $ping_retries, $notes, $snmp_auth_protocol, $snmp_priv_passphrase, $snmp_priv_protocol, $snmp_context, $snmp_engine_id, $max_oids, $device_threads, $poller_id, $site_id, $external_id, $location, $bulk_walk_size); if ($host_id) { automation_debug(" - Success\n"); // Use the thold plugin if it exists api_plugin_hook_function('device_threshold_autocreate', $host_id); db_execute_prepared('DELETE FROM automation_devices WHERE ip = ? LIMIT 1', [$ip]); } else { automation_debug(" - Failed\n"); } return $host_id; } /** * Adds a host to a specified tree in the automation process. * * @param int $host_id The ID of the host to be added. * @param int $tree The tree structure where the host will be added. * * @return void */ function automation_add_tree(int $host_id, int $tree) : void { automation_debug(" Adding to tree\n"); if ($tree > 1000000) { $tree_id = $tree - 1000000; $parent = 0; } else { $tree_item = db_fetch_row_prepared('SELECT * FROM graph_tree_items WHERE id = ?', [$tree]); if (!isset($tree_item['graph_tree_id'])) { return; } $tree_id = $tree_item['graph_tree_id']; $parent = $tree; } $nodeId = api_tree_item_save(0, $tree_id, 3, $parent, '', 0, $host_id, 0, 1, 1, false); } /** * Finds the operating system based on system description, system object, and system name. * * @param string $sysDescr The system description. * @param string $sysObject The system object identifier. * @param string $sysName The system name. * * @return array|false The matched automation template row, or false if none found. */ function automation_find_os(string $sysDescr, string $sysObject, string $sysName) : array|false { $sql_where = ''; $params = []; if ($sysDescr != '') { $sql_where .= ($sql_where != '' ? ' AND ' : 'WHERE ') . "(? REGEXP CONCAT('/', sysDescr, '/') OR ? LIKE CONCAT('%', sysDescr, '%'))"; $params[] = $sysDescr; $params[] = $sysDescr; } if ($sysObject != '') { $sql_where .= ($sql_where != '' ? ' AND ' : 'WHERE ') . "(? REGEXP CONCAT('/', sysOid, '/') OR ? LIKE CONCAT('%', sysOid, '%'))"; $params[] = $sysObject; $params[] = $sysObject; } if ($sysName != '') { $sql_where .= ($sql_where != '' ? ' AND ' : 'WHERE ') . "(? REGEXP CONCAT('/', sysName, '/') OR ? LIKE CONCAT('%', sysName, '%'))"; $params[] = $sysName; $params[] = $sysName; } $result = db_fetch_row_prepared("SELECT at.*, ht.name FROM automation_templates AS at INNER JOIN host_template AS ht ON ht.id = at.host_template $sql_where ORDER BY sequence LIMIT 1", $params); if (cacti_sizeof($result)) { return $result; } else { return false; } } /** * Logs debug information for automation processes. * * @param string $text The debug text to be logged. * * @return void */ function automation_debug(string $text) : void { global $debug; static $message = ''; if (str_contains($text, "\n")) { $logLevel = POLLER_VERBOSITY_MEDIUM; if ($debug) { $logLevel = POLLER_VERBOSITY_NONE; } $full_message = trim($message . $text); $messages = explode("\n",$full_message); foreach ($messages as $line) { $line = trim($line); if (strlen($line) > 0) { cacti_log(automation_get_pid() . ' ' . $line, false, 'AUTOM8', $logLevel); } } $message = ''; } else { if (!CACTI_WEB) { print $text; } $message .= $text; } } /** * Converts a subnet mask to CIDR notation. * * @param string $mask The subnet mask to convert. * @return float|false The CIDR notation as a float, or false on failure. */ function automation_masktocidr(string $mask) : float|false { $cidr = false; $long = ip2long($mask); if ($long !== false) { $base = ip2long('255.255.255.255'); $cidr = 32 - log(($long ^ $base) + 1, 2); } return $cidr; } /** * Retrieves a valid IP address from the given range. * * @param string $range The range of IP addresses to validate. * @return string|false Returns a valid IP address as a string if found, or false if no valid IP address is found. */ function automation_get_valid_ip(string $range) : string|false { $long = ip2long($range); return $long === false ? false : long2ip($long); } /** * Retrieves a valid subnet CIDR from the given range. * * @param string $range The IP range to validate and extract the subnet CIDR from. * @return array|false Returns an array containing the valid subnet CIDR if found, * otherwise returns false if the range is invalid. */ function automation_get_valid_subnet_cidr(string $range) : array|false { $long = ip2long($range); $cidr = 0; if ($long !== false) { $bin = decbin($long); if (strlen($bin) == 32) { $zero = false; $cidr = 0; foreach (str_split($bin) as $char) { if ($char === '0') { $zero = true; } elseif ($zero) { $long = false; break; } else { $cidr++; } } } else { $long = false; } } return $long === false ? false : ['cidr' => $cidr, 'subnet' => long2ip($long)]; } /** * Retrieves a valid mask for the given range. * * @param string $range The range for which to get the valid mask. * @return array|false The valid mask for the specified range. */ function automation_get_valid_mask(string $range) : array|false { $cidr = false; if (is_numeric($range)) { if ($range > 0 && $range < 33) { $cidr = $range; $mask = [ 'cidr' => $cidr, 'subnet' => long2ip((2 ** $range - 1) << (32 - $range)), ]; } else { $mask = false; } } else { $mask = automation_get_valid_subnet_cidr($range); } if ($mask !== false) { $mask['count'] = bindec(str_repeat('0',$mask['cidr']) . str_repeat('1',32 - $mask['cidr'])); if ($mask['count'] == 0) { $mask['count'] = 1; } } return $mask; } /** * Retrieves network information for a given range. * * @param string $range The network range to retrieve information for. * @return array|false An associative array containing network information. */ function automation_get_network_info(string $range) : array|false { $network = false; $broadcast = false; $mask = []; $detail = []; $range = trim($range); if (str_contains($range, '/')) { // 10.1.0.0/24 or 10.1.0.0/255.255.255.0 $range_parts = explode('/', $range); if (!filter_var($range_parts[0], FILTER_VALIDATE_IP)) { return false; } $mask = automation_get_valid_mask($range_parts[1]); if (cacti_sizeof($mask)) { $network = automation_get_valid_ip($range_parts[0]); if ($network !== false && $mask['cidr'] != 0) { $dec = ip2long($network) & ip2long($mask['subnet']); $count = $mask['cidr'] == 32 ? 0 : $mask['count']; $network = long2ip($dec); $broadcast = long2ip($dec + $count); } } } elseif (str_contains($range, '*') && !str_contains($range, '-')) { $test = str_replace('*', '0', $range); if (!filter_var($test, FILTER_VALIDATE_IP)) { return false; } $range_parts = explode('.', $range); $network = ''; $broadcast = ''; $part_count = 0; foreach ($range_parts as $part) { if ($part != '*') { $part_count++; if (is_numeric($part)) { if ($part >= 0 && $part <= 255) { $network .= $part . '.'; $broadcast .= '255.'; } else { $network = false; break; } } else { $network = false; break; } } else { break; } } if ($part_count == 0 || $part_count > 3) { $network = false; $broadcast = false; } else { while ($part_count < 4) { $part_count += 1; $broadcast .= '0.'; $network .= '0.'; } return automation_get_network_info(rtrim($network,'.') . '/' . rtrim($broadcast,'.')); } } elseif (str_contains($range, '-')) { raise_message('automation_iprange', __('ERROR: IP ranges in the form of range1-range2 are no longer supported.'), MESSAGE_LEVEL_ERROR); return false; } else { $network = automation_get_valid_ip($range); $broadcast = automation_get_valid_ip($range); } if ($network !== false && $broadcast !== false) { if (ip2long($network) <= ip2long($broadcast)) { $detail['network'] = $network; $detail['broadcast'] = $broadcast; $detail['cidr'] = $mask['cidr'] ?? false; if ($network == $broadcast) { $detail['type'] = 'single'; $detail['count'] = 1; $detail['cidr'] = 32; $detail['start'] = $network; $detail['end'] = $network; } else { $detail['type'] = isset($mask['cidr']) ? 'subnet' : 'range'; $detail['count'] = ip2long($broadcast) - ip2long($network) - 1; $detail['start'] = long2ip(ip2long($network) + 1); $detail['end'] = long2ip(ip2long($broadcast) - 1); } } } else { return false; } return $detail; } /** * Calculates the start time for an automation process based on the given range. * * @param string $range The range value used to calculate the start time. * @return string|false The calculated start time. */ function automation_calculate_start(string $range) : string|false { $detail = automation_get_network_info($range); if ($detail) { return $detail['start']; } automation_debug(' Could not calculate starting IP!'); return false; } /** * Calculate the total number of IPs in a given range. * * @param string $range The IP range in CIDR notation (e.g., '192.168.1.0/24'). * @return int|false The total number of IPs in the specified range. */ function automation_calculate_total_ips(string $range) : int|false { $detail = automation_get_network_info($range); if ($detail) { return $detail['count']; } automation_debug(' Could not calculate total IPs!'); return false; } /** * Retrieves the next host in the automation sequence. * * @param string $start The starting point for the host retrieval. * @param int $total The total number of hosts available. * @param int $count The current count of hosts processed. * @param string $range The range within which to retrieve the next host. * @return string|false Returns the next host as a string, or false if no more hosts are available. */ function automation_get_next_host(string $start, int $total, int $count, string $range) : string|false { if ($count == $total || $total < 1) { return false; } if (preg_match('/^([0-9]{1,3}\.[0-9]{1,3}\.)\*(\.[0-9]{1,3})$/', $range, $matches)) { // 10.1.*.1 return $matches[1] . ++$count . $matches[2]; } else { // Offset from the start address. Walking the octets by hand // mishandled the carry for ranges wider than a /16 and emitted // out-of-range octets; convert through the 32-bit integer instead. $base = ip2long($start); if ($base === false) { return false; } return long2ip($base + $count); } } /** * Primes the IP address table for a given network. * * This function initializes or updates the IP address table associated with the specified network ID. * * @param int $network_id The ID of the network for which the IP address table is to be primed. * * @return void */ function automation_primeIPAddressTable(int $network_id) : void { $subNets = db_fetch_cell_prepared('SELECT subnet_range FROM automation_networks WHERE id = ?', [$network_id]); $ignore_ips = db_fetch_cell_prepared('SELECT ignore_ips FROM automation_networks WHERE id = ?', [$network_id]); $subNets = explode(',', trim($subNets)); $total = 0; if ($ignore_ips != '') { $ignore_ips = explode(',', $ignore_ips); foreach ($ignore_ips as $index => $ip) { $ignore_ips[$index] = trim($ip); } } else { $ignore_ips = []; } if (cacti_sizeof($subNets)) { foreach ($subNets as $subNet) { $count = 1; $sql = []; $subNetTotal = automation_calculate_total_ips($subNet); $total += $subNetTotal; $start = automation_calculate_start($subNet); if ($start === false) { continue; } if ($start != '' && !in_array($start, $ignore_ips, true)) { $sql[] = "('$start', '', $network_id, '0', '0', '0')"; } while ($count < $subNetTotal) { $ip = automation_get_next_host($start, $subNetTotal, $count, $subNet); if ($ip != '' && !in_array($ip, $ignore_ips, true)) { $sql[] = "('$ip', '', $network_id, '0', '0', '0')"; $count++; } if ($count % 1000 == 0) { db_execute('INSERT INTO automation_ips (ip_address, hostname, network_id, pid, status, thread) VALUES ' . implode(',', $sql)); $sql = []; } } if (cacti_sizeof($sql)) { db_execute('INSERT INTO automation_ips (ip_address, hostname, network_id, pid, status, thread) VALUES ' . implode(',', $sql)); } } } automation_debug("A Total of $total IP Addresses Primed\n"); } /** * Validates if the given device is a valid SNMP device. * * @param array $device Reference to the device array to be validated. * * @return bool Returns true if the device is a valid SNMP device, false otherwise. */ function automation_valid_snmp_device(array &$device) : bool { global $snmp_logging; // initialize variable $host_up = false; $snmp_logging = false; $device['snmp_status'] = HOST_DOWN; $device['ping_status'] = 0; $session = false; $snmp_sysObjectID = ''; // force php to return numeric oid's cacti_oid_numeric_format(); $snmp_items = db_fetch_assoc_prepared('SELECT * FROM automation_snmp_items WHERE snmp_id = ? ORDER BY sequence ASC', [$device['snmp_id']]); if (cacti_sizeof($snmp_items)) { automation_debug(', SNMP: '); foreach ($snmp_items as $item) { // general options $device['snmp_id'] = $item['snmp_id']; $device['snmp_version'] = $item['snmp_version']; $device['snmp_port'] = $item['snmp_port']; $device['snmp_timeout'] = $item['snmp_timeout']; $device['snmp_retries'] = $item['snmp_retries']; // snmp v1/v2 options $device['snmp_community'] = $item['snmp_community']; // snmp v3 options $device['snmp_username'] = $item['snmp_username']; $device['snmp_password'] = $item['snmp_password']; $device['snmp_auth_protocol'] = $item['snmp_auth_protocol']; $device['snmp_priv_passphrase'] = $item['snmp_priv_passphrase']; $device['snmp_priv_protocol'] = $item['snmp_priv_protocol']; $device['snmp_context'] = $item['snmp_context']; $device['snmp_engine_id'] = $item['snmp_engine_id']; $device['max_oids'] = $item['max_oids']; $device['bulk_walk_size'] = $item['bulk_walk_size']; $session = cacti_snmp_session($device['ip_address'], $device['snmp_community'], $device['snmp_version'], $device['snmp_username'], $device['snmp_password'], $device['snmp_auth_protocol'], $device['snmp_priv_passphrase'], $device['snmp_priv_protocol'], $device['snmp_context'], $device['snmp_engine_id'], $device['snmp_port'], $device['snmp_timeout'], $device['snmp_retries'], $device['max_oids']); if ($session !== false) { // Community string is not used for v3 $snmp_sysObjectID = cacti_snmp_session_get($session, '.1.3.6.1.2.1.1.2.0'); if (is_string($snmp_sysObjectID) && $snmp_sysObjectID != 'U') { $snmp_sysObjectID = str_replace('enterprises', '.1.3.6.1.4.1', $snmp_sysObjectID); $snmp_sysObjectID = str_replace('OID: ', '', $snmp_sysObjectID); $snmp_sysObjectID = str_replace('.iso', '.1', $snmp_sysObjectID); if ((strlen($snmp_sysObjectID)) && (!substr_count($snmp_sysObjectID, 'No Such Object')) && (!substr_count($snmp_sysObjectID, 'Error In'))) { $snmp_sysObjectID = trim(str_replace('"', '', $snmp_sysObjectID)); $device['snmp_status'] = HOST_UP; $host_up = true; break; } } } if ($host_up) { break; } } if ($host_up && $session !== false) { $device['snmp_sysObjectID'] = $snmp_sysObjectID; // get system name $snmp_sysName = cacti_snmp_session_get($session, '.1.3.6.1.2.1.1.5.0'); if ($snmp_sysName != '') { $snmp_sysName = trim(strtr($snmp_sysName,'"',' ')); $device['snmp_sysName'] = $snmp_sysName; automation_debug($snmp_sysName); } else { automation_debug('Unknown System'); } // get system location $snmp_sysLocation = cacti_snmp_session_get($session, '.1.3.6.1.2.1.1.6.0'); if ($snmp_sysLocation != '') { $snmp_sysLocation = trim(strtr($snmp_sysLocation,'"',' ')); $device['snmp_sysLocation'] = $snmp_sysLocation; } // get system contact $snmp_sysContact = cacti_snmp_session_get($session, '.1.3.6.1.2.1.1.4.0'); if ($snmp_sysContact != '') { $snmp_sysContact = trim(strtr($snmp_sysContact,'"',' ')); $device['snmp_sysContact'] = $snmp_sysContact; } // get system description $snmp_sysDescr = cacti_snmp_session_get($session, '.1.3.6.1.2.1.1.1.0'); if ($snmp_sysDescr != '') { $snmp_sysDescr = trim(strtr($snmp_sysDescr,'"',' ')); $device['snmp_sysDescr'] = $snmp_sysDescr; } // get system uptime $snmp_engine_time = cacti_snmp_session_get($session, '.1.3.6.1.6.3.10.2.1.3.0'); $snmp_system_uptime = cacti_snmp_session_get($session, '.1.3.6.1.2.1.1.3.0'); $snmp_sysUptime = cacti_snmp_select_uptime($snmp_system_uptime, $snmp_engine_time); if ($snmp_sysUptime !== false) { $device['snmp_sysUptime'] = (string) $snmp_sysUptime; } $session->close(); } else { automation_debug('No response'); } } return $host_up; } /** * Retrieves the DNS name associated with a given IP address. * * @param string $ip The IP address to look up. * @param string $dns The DNS server to use for the lookup. * @param int $timeout The timeout for the DNS lookup in milliseconds. Default is 1000 ms. * @return string The DNS name associated with the IP address, or null if not found. */ function automation_get_dns_from_ip(string $ip, string $dns, int $timeout = 1000) : string { // random transaction number (for routers etc to get the reply back) $data = (string) random_int(10, 99); // trim it to 2 bytes $data = substr($data, 0, 2); // create request header $data .= "\1\0\0\1\0\0\0\0\0\0"; // split IP into octets $octets = explode('.', $ip); // perform a quick error check if (cacti_count($octets) != 4) { return 'ERROR'; } // needs a byte to indicate the length of each segment of the request for ($x = 3; $x >= 0; $x--) { switch (strlen($octets[$x])) { case 1: // 1 byte long segment $data .= "\1"; break; case 2: // 2 byte long segment $data .= "\2"; break; case 3: // 3 byte long segment $data .= "\3"; break; default: // segment is too big, invalid IP return 'ERROR'; } // and the segment itself $data .= $octets[$x]; } // and the final bit of the request $data .= "\7in-addr\4arpa\0\0\x0C\0\1"; // create UDP socket $handle = @fsockopen("udp://$dns", 53); if ($handle === false) { return cacti_strtoupper($ip); } @stream_set_timeout($handle, intval(floor($timeout / 1000)), ($timeout * 1000) % 1000000); @stream_set_blocking($handle, true); // send our request (and store request size so we can cheat later) $requestsize = @fwrite($handle, $data); // get the response $response = @fread($handle, 1000); // check to see if it timed out $info = @stream_get_meta_data($handle); // close the socket @fclose($handle); if ($info['timed_out'] == true) { return 'timed_out'; } // more error handling if ($response == '' || $requestsize == false || strlen($response) <= $requestsize) { return $ip; } // parse the response and find the response type $type = @unpack('s', substr($response, $requestsize + 2)); if (isset($type[1]) && $type[1] == 0x0C00) { // set up our variables $host = ''; $len = 0; /* set our pointer at the beginning of the hostname uses the request size from earlier rather than work it out. */ $position = $requestsize + 12; // reconstruct the hostname do { // get segment size $len = unpack('c', substr($response, $position)); if (!is_array($len)) { break; } // null terminated string, so length 0 = finished if ($len[1] == 0) { // return the hostname, without the trailing '.' return cacti_strtoupper(substr($host, 0, strlen($host) - 1)); } // add the next segment to our host $host .= substr($response, $position + 1, $len[1]) . '.'; // move pointer on to the next segment $position += $len[1] + 1; } while ($len != false); } // error - return the hostname return cacti_strtoupper($ip); } /** * Pings a NetBIOS name for a given IP address. * * @param string $ip - The IP address to ping. * @param int $timeout_ms - The timeout in milliseconds for the ping. Default is 1000 ms. * * @return mixed - Returns the netbios name is successful or false otherwise */ function ping_netbios_name(string $ip, int $timeout_ms = 1000) : mixed { $handle = @fsockopen("udp://$ip", 137); if (is_resource($handle)) { stream_set_timeout($handle, intval(floor($timeout_ms / 1000)), ($timeout_ms * 1000) % 1000000); stream_set_blocking($handle, true); $packet = "\x99\x99\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x20\x43\x4b" . str_repeat("\x41", 30) . "\x00\x00\x21\x00\x01"; // send our request (and store request size so we can cheat later) $requestsize = @fwrite($handle, $packet); // get the response $response = @fread($handle, 2048); // check to see if it timed out $info = @stream_get_meta_data($handle); // close the socket fclose($handle); if ($info['timed_out']) { return false; } if (!isset($response[56])) { return false; } // parse the response and find the response type $names = hexdec((string) ord($response[56])); if ($names > 0) { $host = ''; for ($i = 57; $i < strlen($response); $i += 1) { if (hexdec((string) ord($response[$i])) == 0) { break; } $host .= $response[$i]; } return trim(cacti_strtolower($host)); } else { return false; } } return false; } /** * Updates the automation settings for a specific device. * * @param int $host_id - The ID of the host device to update. * * @return void */ function automation_update_device(int $host_id) : void { $function = automation_function_with_pid(__FUNCTION__); cacti_log($function . ' Device[' . $host_id . ']', true, 'AUTOM8 TRACE', POLLER_VERBOSITY_MEDIUM); /* select all graph templates associated with this host, but exclude those where * a graph already exists (table graph_local has a known entry for this host/template) */ $sql = 'SELECT gt.* FROM graph_templates AS gt INNER JOIN host_graph AS hg ON gt.id=hg.graph_template_id WHERE hg.host_id=' . $host_id . ' AND gt.id NOT IN ( SELECT gl.graph_template_id FROM graph_local AS gl WHERE host_id=' . $host_id . ' )'; $graph_templates = db_fetch_assoc($sql); cacti_log($function . ' Device[' . $host_id . '], sql: ' . str_replace("\n",' ', $sql), true, 'AUTOM8 TRACE', POLLER_VERBOSITY_DEBUG); if ($host_id > 0) { object_cache_get_totals('device_state', $host_id); } // create all graph template graphs if (cacti_sizeof($graph_templates)) { foreach ($graph_templates as $graph_template) { cacti_log($function . ' Found GT[' . $graph_template['id'] . '] for Device[' . $host_id . ']', true, 'AUTOM8 TRACE', POLLER_VERBOSITY_HIGH); automation_execute_graph_template($host_id, $graph_template['id']); } } // all associated data queries $data_queries = db_fetch_assoc_prepared('SELECT sq.*, hsq.reindex_method FROM snmp_query AS sq INNER JOIN host_snmp_query AS hsq ON sq.id=hsq.snmp_query_id WHERE hsq.host_id = ?', [$host_id]); // create all data query graphs if (cacti_sizeof($data_queries)) { foreach ($data_queries as $data_query) { cacti_log($function . ' Found DQ[' . $data_query['id'] . '] for Device[' . $host_id . ']', true, 'AUTOM8 TRACE', POLLER_VERBOSITY_MEDIUM); automation_execute_data_query($host_id, $data_query['id']); } } if ($host_id > 0) { object_cache_get_totals('device_state', $host_id, true); object_cache_update_totals('diff'); } // now handle tree rules for that host cacti_log($function . ' Create Tree for Device[' . $host_id . ']', true, 'AUTOM8 TRACE', POLLER_VERBOSITY_MEDIUM); automation_execute_device_create_tree($host_id); } /** * Executes an automation function with a process ID. * * @param string $functionName The name of the function to be executed. * @return string The function name with the process ID appended. */ function automation_function_with_pid(string $functionName) : string { return automation_get_pid() . ' ' . $functionName . '()'; } /** * Retrieves the process ID (PID) for the automation process. * * @return string The PID of the automation process. */ function automation_get_pid() : string { static $pid; if (!isset($pid)) { $pid = getmypid(); } return "[PID: $pid]"; } /** * Changes the type of a tree rule leaf. * * @param string $leaf_type The new type of the leaf. * @param string $rule_id The ID of the rule to be updated. * * @return void */ function automation_change_tree_rule_leaf_type(string $leaf_type, string $rule_id) : void { $function = automation_function_with_pid(__FUNCTION__); $leaf_old = db_fetch_cell_prepared('SELECT leaf_type FROM automation_tree_rules WHERE id = ?', [$rule_id]); if ($leaf_old != $leaf_type) { cacti_log($function . ' Found leaf change from Leaf[' . $leaf_old . '] to Leaf[' . $leaf_type . '] for TreeRule[' . $rule_id . ']', true, 'AUTOM8 TRACE', POLLER_VERBOSITY_HIGH); if ($leaf_type == 3) { cacti_log($function . ' Found leaf changed to \'Device\' for TreeRule[' . $rule_id . ']', true, 'AUTOM8 TRACE', POLLER_VERBOSITY_HIGH); $rule_items = db_fetch_assoc_prepared('SELECT * FROM automation_tree_rule_items WHERE rule_id = ? AND (field like \'gtg.%\' or field like \'gt.%\')', [$rule_id]); if (cacti_sizeof($rule_items)) { cacti_log($function . ' ' . cacti_sizeof($rule_items) . ' invalid Tree Creation rule items found for TreeRule[' . $rule_id . ']', true, 'AUTOM8 TRACE', POLLER_VERBOSITY_DEBUG); foreach ($rule_items as $rule_item) { cacti_log($function . ' Removing invalid Tree Creation rule item TreeRule[' . $rule_id . '] TreeRuleItem[' . $rule_item['id'] . '] Field[' . htmle($rule_item['field']) . '] with Search[' . htmle($rule_item['search_pattern']) . '] Replace[' . htmle($rule_item['replace_pattern']) . ']', true, 'AUTOM8 TRACE', POLLER_VERBOSITY_DEBUG); db_execute_prepared('DELETE FROM automation_tree_rule_items WHERE id = ?', [$rule_item['id']]); } } else { cacti_log($function . ' No invalid Tree Creation rule items found for TreeRule[' . $rule_id . ']', true, 'AUTOM8 TRACE', POLLER_VERBOSITY_DEBUG); } $match_items = db_fetch_assoc_prepared('SELECT * FROM automation_match_rule_items WHERE rule_id = ? AND (field like \'gtg.%\' or field like \'gt.%\')', [$rule_id]); if (cacti_sizeof($match_items)) { cacti_log($function . ' ' . cacti_sizeof($match_items) . ' invalid Object Selection rule items found for TreeRule[' . $rule_id . ']', true, 'AUTOM8 TRACE', POLLER_VERBOSITY_DEBUG); foreach ($match_items as $match_item) { cacti_log($function . ' Removing invalid Object Selection rule item TreeRule[' . $rule_id . '] TreeMatchItem[' . $match_item['id'] . '] Field[' . htmle($match_item['field']) . '] with Pattern[' . htmle($match_item['pattern']) . ']', true, 'AUTOM8 TRACE', POLLER_VERBOSITY_DEBUG); db_execute_prepared('DELETE FROM automation_match_rule_items WHERE id = ?', [$match_item['id']]); } } else { cacti_log($function . ' No invalid Object Selection rule items found for TreeRule[' . $rule_id . ']', true, 'AUTOM8 TRACE', POLLER_VERBOSITY_DEBUG); } } db_execute_prepared('UPDATE automation_tree_rules SET leaf_type = ? WHERE id = ?', [$leaf_type, $rule_id]); } } /** * Converts an automation type to its corresponding database table name. * * @param string $type The type of automation to convert. * @return string The name of the corresponding database table. */ function automation_type_to_table(string $type) : string { $table = ''; $table = match ($type) { 'network' => 'automation_networks', 'device' => 'automation_templates', 'device_rules' => 'automation_templates_rules', 'graph' => 'automation_graph_rules', 'graph_items' => 'automation_graph_rule_items', 'tree' => 'automation_tree_rules', 'tree_items' => 'automation_tree_rule_items', 'snmp' => 'automation_snmp', 'snmp_items' => 'automation_snmp_items', default => $table, }; return $table; } /** * Converts an automation ID to a hash. * * @param string $table The name of the table. * @param int $id The ID to be converted. * * @return mixed - The resulting hash. */ function automation_id_to_hash(string $table, int $id) : mixed { $table = automation_type_to_table($table); if ($table != '') { return db_fetch_cell_prepared("SELECT hash FROM $table WHERE id = ?", [$id]); } return false; } /** * Converts a given hash to its corresponding ID based on the specified type. * * @param string $type The type of the entity for which the hash is being converted. * @param string $hash The hash value that needs to be converted to an ID. * @return int|false The ID corresponding to the given hash and type. */ function automation_hash_to_id(string $type, string $hash) : int|false { $table = automation_type_to_table($type); if ($table != '') { return db_fetch_cell_prepared("SELECT id FROM $table WHERE hash = ?", [$hash]); } return false; } /** * Updates the automation hashes. * * This function is responsible for updating the hashes used in the automation process. * * @return void */ function automation_update_hashes() : void { $tables = [ 'automation_templates', 'automation_templates_rules', 'automation_graph_rules', 'automation_graph_rule_items', 'automation_tree_rules', 'automation_tree_rule_items', 'automation_match_rule_items', 'automation_snmp', 'automation_snmp_items', 'automation_networks' ]; foreach ($tables as $table) { $items = db_fetch_assoc("SELECT id FROM $table WHERE hash = ''"); if (cacti_sizeof($items)) { foreach ($items as $item) { $hash = get_hash_automation(0, $table); db_execute_prepared("UPDATE $table SET hash = ? WHERE id = ?", [$hash, $item['id']]); } } } } /** * Exports network data for the given network IDs. * * @param mixed $network_ids An array of network IDs to export or a single id. * * @return array An array containing the exported network data. */ function automation_network_export(mixed $network_ids) : array { if (!is_array($network_ids)) { $export_name = db_fetch_cell_prepared("SELECT CONCAT('automation_network_', name) FROM automation_networks WHERE id = ?", [$network_ids]); $network_ids = [$network_ids]; } else { $export_name = 'automation_network_multiple'; } $json_array = []; $json_array['name'] = clean_up_name(cacti_strtolower($export_name)); $json_array['export_name'] = $json_array['name'] . '.json'; if (cacti_sizeof($network_ids)) { $networks = []; foreach ($network_ids as $id) { // get the row of data $network = db_fetch_row_prepared('SELECT * FROM automation_networks WHERE id = ?', [$id]); if (!is_array($network)) { continue; } $snmp_id = $network['snmp_id']; $network['snmp_id'] = db_fetch_cell_prepared('SELECT hash FROM automation_snmp WHERE id = ?', [$network['snmp_id']]); // set some safe defaults $network['poller_id'] = 1; // remove objects that have a hash unset($network['id']); unset($network['up_hosts']); unset($network['snmp_hosts']); unset($network['last_runtime']); unset($network['last_started']); unset($network['last_status']); // get the snmp options data $snmp = db_fetch_row_prepared('SELECT * FROM automation_snmp WHERE id = ?', [$snmp_id]); // remove objects that have a hash unset($snmp['id']); // get the snmp options item data $snmp_items = db_fetch_assoc_prepared('SELECT * FROM automation_snmp_items WHERE snmp_id = ?', [$snmp_id]); // remove objects that have a hash foreach ($snmp_items as $index => $item) { unset($snmp_items[$index]['id']); unset($snmp_items[$index]['snmp_id']); if ($item['snmp_version'] == 3) { unset($snmp_items[$index]['snmp_community']); } else { unset($snmp_items[$index]['snmp_username']); unset($snmp_items[$index]['snmp_password']); unset($snmp_items[$index]['snmp_auth_protocol']); unset($snmp_items[$index]['snmp_priv_protocol']); unset($snmp_items[$index]['snmp_priv_passphrase']); unset($snmp_items[$index]['snmp_context']); unset($snmp_items[$index]['snmp_engine_id']); } } // collapse the snmp items into snmp $snmp['snmp_items'] = $snmp_items; // collapse the snmp object into the network $network['snmp'] = $snmp; // collapse the data object into the json object $networks[] = $network; } $json_array['network'] = $networks; } return $json_array; } /** * Exports automation device rules based on the provided template IDs. * * @param mixed $template_ids An array of template IDs to export the device rules for or a single id. * * @return array An array containing the exported device rules. */ function automation_device_rule_export(mixed $template_ids) : array { if (!is_array($template_ids)) { $export_name = db_fetch_cell_prepared("SELECT CONCAT('automation_device_rule_', name) FROM automation_templates AS at INNER JOIN host_template AS ht ON at.host_template = ht.id WHERE at.id = ?", [$template_ids]); $template_ids = [$template_ids]; } else { $export_name = 'automation_device_rules_multiple'; } $json_array = []; $json_array['name'] = clean_up_name(cacti_strtolower($export_name)); $json_array['export_name'] = $json_array['name'] . '.json'; if (cacti_sizeof($template_ids)) { $devices = []; foreach ($template_ids as $id) { // get the row of data $device = db_fetch_row_prepared('SELECT * FROM automation_templates WHERE id = ?', [$id]); if (!is_array($device)) { continue; } $device['host_template'] = db_fetch_cell_prepared('SELECT hash FROM host_template WHERE id = ?', [$device['host_template']]); // remove objects that have a hash unset($device['id']); // get the snmp options data $device_rule_items = db_fetch_assoc_prepared('SELECT * FROM automation_templates_rules WHERE template_id = ?', [$id]); foreach ($device_rule_items as $index => $rule) { if ($rule['rule_type'] == 1) { $device_rule_items[$index]['rule_id'] = db_fetch_cell_prepared('SELECT hash FROM automation_graph_rules WHERE id = ?', [$rule['rule_id']]); } else { $device_rule_items[$index]['rule_id'] = db_fetch_cell_prepared('SELECT hash FROM automation_tree_rules WHERE id = ?', [$rule['rule_id']]); } unset($device_rule_items[$index]['id']); unset($device_rule_items[$index]['template_id']); } $device['device_rules'] = $device_rule_items; // get the snmp options item data $graph_rules = db_fetch_assoc_prepared('SELECT * FROM automation_graph_rules WHERE id IN (SELECT rule_id FROM automation_templates_rules WHERE rule_type = 1 AND template_id = ?)', [$id]); // remove objects that have a hash foreach ($graph_rules as $index => $rule) { $rule_id = $rule['id']; $graph_rules[$index]['snmp_query_id'] = db_fetch_cell_prepared('SELECT hash FROM snmp_query WHERE id = ?', [$rule['snmp_query_id']]); $graph_rules[$index]['graph_type_id'] = db_fetch_cell_prepared('SELECT hash FROM snmp_query_graph WHERE id = ?', [$rule['graph_type_id']]); unset($graph_rules[$index]['id']); // get the snmp options item data $graph_rule_items = db_fetch_assoc_prepared('SELECT gri.* FROM automation_graph_rule_items AS gri WHERE gri.rule_id = ?', [$rule_id]); // remove objects that have a hash foreach ($graph_rule_items as $grindex => $rule_item) { unset($graph_rule_items[$grindex]['id']); unset($graph_rule_items[$grindex]['rule_id']); } // collapse the graph rule items $graph_rules[$index]['graph_rule_items'] = $graph_rule_items; // match items $graph_match_items = db_fetch_assoc_prepared('SELECT mri.* FROM automation_match_rule_items AS mri WHERE mri.rule_id = ? AND mri.rule_type IN (1,2)', [$rule_id]); /* remove objects that have a hash * rule_types 1,2 are Graph Rules * rule_types 3,4 are Tree Rules */ foreach ($graph_match_items as $gmindex => $rule_item) { unset($graph_match_items[$gmindex]['id']); unset($graph_match_items[$gmindex]['rule_id']); } $graph_rules[$index]['graph_match_items'] = $graph_match_items; } $device['graph_rules'] = $graph_rules; // get the snmp options item data $tree_rules = db_fetch_assoc_prepared('SELECT * FROM automation_tree_rules WHERE id IN (SELECT rule_id FROM automation_templates_rules WHERE rule_type = 2 AND template_id = ?)', [$id]); // remove objects that have a hash foreach ($tree_rules as $index => $rule) { $rule_id = $rule['id']; // get the snmp options item data $tree_rule_items = db_fetch_assoc_prepared('SELECT gri.* FROM automation_tree_rule_items AS gri WHERE gri.rule_id = ?', [$rule_id]); // remove objects that have a hash foreach ($tree_rule_items as $trindex => $rule_item) { unset($tree_rule_items[$trindex]['id']); $tree_rule_items[$trindex]['rule_id'] = db_fetch_cell_prepared('SELECT hash FROM automation_tree_rules WHERE id = ?', [$rule_item['id']]); } // unset the rule id unset($tree_rules[$index]['id']); // pick up the tree and branch name as they may not be on the foreign system $tree_rules[$index]['tree_data'] = db_fetch_row_prepared('SELECT name, sort_type FROM graph_tree WHERE id = ?', [$rule['tree_id']]); $tree_rules[$index]['tree_branch_data'] = automation_device_rule_export_branches($rule['tree_id'], $rule['tree_item_id']); // collapse the tree rule items $tree_rules[$index]['tree_rule_items'] = $tree_rule_items; // match items $tree_match_items = db_fetch_assoc_prepared('SELECT mri.* FROM automation_match_rule_items AS mri WHERE mri.rule_id = ? AND mri.rule_type IN (3,4)', [$rule_id]); /* remove objects that have a hash * rule_types 1,2 are Graph Rules * rule_types 3,4 are Tree Rules */ foreach ($tree_match_items as $tmindex => $rule_item) { unset($tree_match_items[$tmindex]['id']); unset($tree_match_items[$tmindex]['rule_id']); } $tree_rules[$index]['tree_match_items'] = $tree_match_items; } $device['tree_rules'] = $tree_rules; if (db_table_exists('plugin_thold_host_template')) { $host_template_id = db_fetch_cell_prepared('SELECT host_template FROM automation_templates WHERE id = ?', [$id]); $rules = db_fetch_assoc_prepared('SELECT * FROM plugin_thold_host_template WHERE host_template_id = ?', [$host_template_id]); $thold_rules = []; if (cacti_sizeof($rules)) { foreach ($rules as $r) { $ht_hash = db_fetch_cell_prepared('SELECT hash FROM host_template WHERE id = ?', [$r['host_template_id']]); $tt_details = db_fetch_row_prepared('SELECT * FROM thold_template WHERE id = ?', [$r['thold_template_id']]); if (!is_array($tt_details)) { continue; } $data_source_hash = db_fetch_cell_prepared('SELECT hash FROM data_template_rrd WHERE id = ?', [$tt_details['data_source_id']]); $tt_details['data_source_hash'] = $data_source_hash; // unset id's that will be recalculated on import unset($tt_details['id']); unset($rr_details['data_source_id']); unset($tt_details['data_template_id']); $thold_rules[] = [ 'host_template_id' => $ht_hash, 'thold_template_id' => $tt_details['hash'], 'thold_template' => $tt_details ]; } } $device['thold_rules'] = $thold_rules; } // collapse the data object into the json object $devices[] = $device; } $json_array['device'] = $devices; } return $json_array; } /** * Creates a tree structure based on the provided tree and branch data. * * @param array $tree_data An associative array containing the tree data. * @param array $branch_data Data related to the branches of the tree. * * @return array The resulting tree structure as an associative array. */ function automation_tree_rule_create_tree(array $tree_data, array $branch_data) : array { if (cacti_sizeof($tree_data)) { $tree_id = db_fetch_cell_prepared('SELECT id FROM graph_tree WHERE name = ?', [$tree_data['name']]); $parent = 0; if (empty($tree_id)) { $save = []; $save['id'] = 0; $save['name'] = $tree_data['name']; $save['sort_type'] = $tree_data['sort_type']; $save['last_modified'] = date('Y-m-d H:i:s', time()); $save['enabled'] = 'on'; $save['modified_by'] = $_SESSION[SESS_USER_ID]; $save['sequence'] = api_tree_get_max_sequence() + 1; $save['user_id'] = $_SESSION[SESS_USER_ID]; $tree_id = sql_save($save, 'graph_tree'); } if (!empty($tree_id)) { if (cacti_sizeof($branch_data)) { $parent = 0; foreach ($branch_data as $branch) { $branch_id = db_fetch_cell_prepared('SELECT id FROM graph_tree_items WHERE parent = ? AND title = ?', [$parent, $branch['title']]); if (empty($branch_id)) { $save = []; $save = $branch; // what we know from the imported object $save['id'] = 0; $save['parent'] = $parent; $save['graph_tree_id'] = $tree_id; // since we are only dealing with branches $save['local_graph_id'] = 0; $save['host_id'] = 0; $save['site_id'] = 0; $parent = sql_save($save, 'graph_tree_items'); } else { $parent = $branch_id; } } } } return [$tree_id, $parent]; } else { return [0, 0]; } } /** * Exports the branches of a device rule automation for a given tree and branch. * * @param int $tree_id The ID of the tree. * @param int $branch_id The ID of the branch. * @return array The exported branches. */ function automation_device_rule_export_branches(int $tree_id, int $branch_id) : array { if ($branch_id == 0) { return []; } $branches = []; while (true) { $branch = db_fetch_row_prepared('SELECT * FROM graph_tree_items WHERE graph_tree_id = ? AND id = ?', [$tree_id, $branch_id]); if (cacti_sizeof($branch)) { $parent = $branch['parent']; unset($branch['id']); unset($branch['parent']); unset($branch['graph_tree_id']); unset($branch['local_graph_id']); unset($branch['host_id']); unset($branch['site_id']); $branches[] = $branch; if ($parent == 0) { break; } else { $branch_id = $parent; } } else { break; } } return array_reverse($branches); } /** * Exports automation graph rules based on the provided graph rule IDs. * * @param mixed $graph_rule_ids An array of graph rule IDs to be exported or a single id. * * @return array The exported graph rules. */ function automation_graph_rule_export(mixed $graph_rule_ids) : array { if (!is_array($graph_rule_ids)) { $export_name = db_fetch_cell_prepared("SELECT CONCAT('automation_graphs_rule_', name) FROM automation_graph_rules WHERE id = ?", [$graph_rule_ids]); $graph_rule_ids = [$graph_rule_ids]; } else { $export_name = 'automation_graph_rules_multiple'; } $json_array = []; $json_array['name'] = clean_up_name(cacti_strtolower($export_name)); $json_array['export_name'] = $json_array['name'] . '.json'; if (cacti_sizeof($graph_rule_ids)) { $graph_rules = []; foreach ($graph_rule_ids as $rule_id) { // get the snmp options item data $graph_rule = db_fetch_row_prepared('SELECT * FROM automation_graph_rules WHERE id = ?', [$rule_id]); if (!is_array($graph_rule)) { continue; } $graph_rule['snmp_query_id'] = db_fetch_cell_prepared('SELECT hash FROM snmp_query WHERE id = ?', [$graph_rule['snmp_query_id']]); $graph_rule['graph_type_id'] = db_fetch_cell_prepared('SELECT hash FROM snmp_query_graph WHERE id = ?', [$graph_rule['graph_type_id']]); // get the snmp options item data $graph_rule_items = db_fetch_assoc_prepared('SELECT gri.* FROM automation_graph_rule_items AS gri WHERE gri.rule_id = ?', [$rule_id]); // remove objects that have a hash foreach ($graph_rule_items as $grindex => $rule_item) { unset($graph_rule_items[$grindex]['id']); unset($graph_rule_items[$grindex]['rule_id']); } // unset the rule id unset($graph_rule['id']); // collapse the graph rule items $graph_rule['graph_rule_items'] = $graph_rule_items; // match items $graph_match_items = db_fetch_assoc_prepared('SELECT mri.* FROM automation_match_rule_items AS mri WHERE mri.rule_id = ? AND mri.rule_type IN (1,2)', [$rule_id]); // remove objects that have a hash foreach ($graph_match_items as $gmindex => $rule_item) { unset($graph_match_items[$gmindex]['id']); unset($graph_match_items[$gmindex]['rule_id']); } $graph_rule['graph_match_items'] = $graph_match_items; $graph_rules[] = $graph_rule; } $json_array['graph_rules'] = $graph_rules; } return $json_array; } /** * Exports automation tree rules based on the provided tree rule IDs. * * @param mixed $tree_rule_ids An array of tree rule IDs to be exported or a single id. * * @return array The exported tree rules. */ function automation_tree_rule_export(mixed $tree_rule_ids) : array { if (!is_array($tree_rule_ids)) { $export_name = db_fetch_cell_prepared("SELECT CONCAT('automation_tree_rule_', name) FROM automation_tree_rules WHERE id = ?", [$tree_rule_ids]); $tree_rule_ids = [$tree_rule_ids]; } else { $export_name = 'automation_tree_rules_multiple'; } $json_array = []; $json_array['name'] = clean_up_name(cacti_strtolower($export_name)); $json_array['export_name'] = $json_array['name'] . '.json'; if (cacti_sizeof($tree_rule_ids)) { $tree_rules = []; foreach ($tree_rule_ids as $rule_id) { // get the snmp options item data $tree_rule = db_fetch_row_prepared('SELECT * FROM automation_tree_rules WHERE id = ?', [$rule_id]); if (!cacti_sizeof($tree_rule)) { raise_message('rule_missing', __esc('Can not find the Tree Rule with the ID %s', $rule_id), MESSAGE_LEVEL_ERROR); return []; } // get the snmp options item data $tree_rule_items = db_fetch_assoc_prepared('SELECT gri.* FROM automation_tree_rule_items AS gri WHERE gri.rule_id = ?', [$rule_id]); // remove objects that have a hash foreach ($tree_rule_items as $trindex => $rule_item) { unset($tree_rule_items[$trindex]['id']); unset($tree_rule_items[$trindex]['rule_id']); } // unset the rule id unset($tree_rule['id']); // pick up the tree and branch name as they may not be on the foreign system $tree_rule['tree_data'] = db_fetch_row_prepared('SELECT name, sort_type FROM graph_tree WHERE id = ?', [$tree_rule['tree_id']]); $tree_rule['tree_branch_data'] = automation_device_rule_export_branches($tree_rule['tree_id'], $tree_rule['tree_item_id']); // collapse the graph rule items $tree_rule['tree_rule_items'] = $tree_rule_items; // match items $tree_match_items = db_fetch_assoc_prepared('SELECT mri.* FROM automation_match_rule_items AS mri WHERE mri.rule_id = ? AND mri.rule_type IN (3,4)', [$rule_id]); // remove objects that have a hash foreach ($tree_match_items as $tmindex => $rule_item) { unset($tree_match_items[$tmindex]['id']); unset($tree_match_items[$tmindex]['rule_id']); } $tree_rule['tree_match_items'] = $tree_match_items; $tree_rules[] = $tree_rule; } $json_array['tree_rules'] = $tree_rules; } return $json_array; } /** * Exports SNMP options based on the provided SNMP option IDs. * * @param mixed $snmp_option_ids An array of SNMP option IDs to be exported or a single id. * * @return array An array containing the exported SNMP options. */ function automation_snmp_option_export(mixed $snmp_option_ids) : array { if (!is_array($snmp_option_ids)) { $export_name = db_fetch_cell_prepared("SELECT CONCAT('automation_snmp_option_', name) FROM automation_snmp WHERE id = ?", [$snmp_option_ids]); $snmp_option_ids = [$snmp_option_ids]; } else { $export_name = 'automation_snmp_options_multiple'; } $json_array = []; $json_array['name'] = clean_up_name(cacti_strtolower($export_name)); $json_array['export_name'] = $json_array['name'] . '.json'; if (cacti_sizeof($snmp_option_ids)) { $options = []; foreach ($snmp_option_ids as $option) { // get the snmp options data $snmp_option = db_fetch_row_prepared('SELECT * FROM automation_snmp WHERE id = ?', [$option]); // remove objects that have a hash unset($snmp_option['id']); // get the snmp options item data $snmp_items = db_fetch_assoc_prepared('SELECT * FROM automation_snmp_items WHERE snmp_id = ?', [$option]); // remove objects that have a hash foreach ($snmp_items as $index => $item) { unset($snmp_items[$index]['id']); unset($snmp_items[$index]['snmp_id']); if ($item['snmp_version'] == 3) { unset($snmp_items[$index]['snmp_community']); } else { unset($snmp_items[$index]['snmp_username']); unset($snmp_items[$index]['snmp_password']); unset($snmp_items[$index]['snmp_auth_protocol']); unset($snmp_items[$index]['snmp_priv_protocol']); unset($snmp_items[$index]['snmp_priv_passphrase']); unset($snmp_items[$index]['snmp_context']); unset($snmp_items[$index]['snmp_engine_id']); } } // collapse the snmp items into snmp $snmp_option['snmp_items'] = $snmp_items; $options[] = $snmp_option; } $json_array['snmp'] = $options; } return $json_array; } /** * Validates the uploaded automation file. * * @return array|bool An array containing the uploaded data if the file is valid, or false if the file is invalid. */ function automation_validate_upload() : array|bool { // check file transfer if used if (isset($_FILES['import_file'])) { // check for errors first if ($_FILES['import_file']['error'] != 0) { switch ($_FILES['import_file']['error']) { case 1: raise_message('ftb', __('The file is too big.'), MESSAGE_LEVEL_ERROR); break; case 2: raise_message('ftb2', __('The file is too big.'), MESSAGE_LEVEL_ERROR); break; case 3: raise_message('ift', __('Incomplete file transfer.'), MESSAGE_LEVEL_ERROR); break; case 4: raise_message('nfu', __('No file uploaded.'), MESSAGE_LEVEL_ERROR); break; case 6: raise_message('tfm', __('Temporary folder missing.'), MESSAGE_LEVEL_ERROR); break; case 7: raise_message('ftwf', __('Failed to write file to disk'), MESSAGE_LEVEL_ERROR); break; case 8: raise_message('fusbe', __('File upload stopped by extension'), MESSAGE_LEVEL_ERROR); break; } if (is_error_message()) { return false; } } // check mine type of the uploaded file if ($_FILES['import_file']['type'] != 'application/json') { raise_message('ife', __('Invalid file extension.'), MESSAGE_LEVEL_ERROR); return false; } $content = file_get_contents($_FILES['import_file']['tmp_name']); if ($content === false) { return false; } return json_decode($content, true); } raise_message('nfu2', __('No file uploaded.'), MESSAGE_LEVEL_ERROR); return false; } /** * Imports SNMP options for automation. * * @param array $snmp An array containing SNMP options to be imported. * @return array An array containing the results of the import operation. */ function automation_snmp_option_import(array $snmp) : array { $debug_data = []; $snmp_id = 0; foreach ($snmp as $column => $coldata) { switch($column) { case 'hash': $save['id'] = automation_hash_to_id('snmp', $coldata); $save['hash'] = $coldata; break; case 'name': $save['name'] = $coldata; $snmp_id = sql_save($save, 'automation_snmp'); if ($snmp_id) { if (CACTI_WEB) { $debug_data['success'][] = __esc('Automation Network SNMP Rule \'%s\' %s!', $save['name'], ($save['id'] > 0 ? __('Updated') : __('Imported'))); } else { $debug_data['success'][] = __('Automation Network SNMP Rule \'%s\' %s!', $save['name'], ($save['id'] > 0 ? __('Updated') : __('Imported'))); } } else { if (CACTI_WEB) { $debug_data['failure'][] = __esc('Automation Network SNMP Rule \'%s\' %s Failed!', $save['name'], ($save['id'] > 0 ? __('Update') : __('Import'))); } else { $debug_data['failure'][] = __('Automation Network SNMP Rule \'%s\' %s Failed!', $save['name'], ($save['id'] > 0 ? __('Update') : __('Import'))); } } break; case 'snmp_items': foreach ($coldata as $snmp_options) { $save = []; $save['snmp_id'] = $snmp_id; foreach ($snmp_options as $option => $optdata) { switch($option) { case 'hash': $save['id'] = automation_hash_to_id('snmp_items', $optdata); $save['hash'] = $optdata; break; default: $save[$option] = $optdata; break; } } $opt_id = sql_save($save, 'automation_snmp_items'); if ($opt_id) { if (CACTI_WEB) { $debug_data['success'][] = __esc('Automation Network SNMP Option %s!', ($save['id'] > 0 ? __('Updated') : __('Imported'))); } else { $debug_data['success'][] = __('Automation Network SNMP Option %s!', ($save['id'] > 0 ? __('Updated') : __('Imported'))); } } else { if (CACTI_WEB) { $debug_data['failure'][] = __esc('Automation Network SNMP Option %s Failed!', ($save['id'] > 0 ? __('Update') : __('Import'))); } else { $debug_data['failure'][] = __('Automation Network SNMP Option %s Failed!', ($save['id'] > 0 ? __('Update') : __('Import'))); } } } break; } } return $debug_data; } /** * Imports network data from a JSON string. * * @param mixed $json_data The JSON string containing network data to be imported. * * @return array An array containing the results of the import operation. */ function automation_network_import(mixed $json_data) : array { $debug_data = []; /** * This routine will work from the bottom up so that we can maintain hash to id rules * all the way up the rules. So, in this case we will import the snmp_options first * followed by the network's. */ if (cacti_sizeof($json_data) && isset($json_data['network'])) { $error = false; $save = []; foreach ($json_data['network'] as $data) { if (isset($data['snmp'])) { $debug_data += automation_snmp_option_import($data['snmp']); unset($data['snmp']); $save = []; } else { $error = true; $debug_data['errors'][] = __('The Automation Network Rule does not include any SNMP Options!'); } if (!automation_validate_import_columns('automation_networks', $data, $debug_data)) { $debug_data['errors'][] = __('The Automation Network Rule import columns do not match the database schema'); $error = true; } $name = 'Unknown Device Template'; if (!$error) { foreach ($data as $column => $coldata) { switch($column) { case 'hash': $save['id'] = automation_hash_to_id('network', $coldata); $save['hash'] = $coldata; break; case 'snmp_id': $save['snmp_id'] = automation_hash_to_id('snmp', $coldata); break; case 'host_template': $template = db_fetch_row_prepared('SELECT id, name FROM host_template WHERE hash = ?', [$coldata]); if (!cacti_sizeof($template)) { $debug_data['errors'][] = __('The Device Template related to the Network Rule is not loaded in this Cacti System. Please edit this Network and set the Device Template, or Import the Device Template and then re-import this Automation Rule.'); $error = true; $save['host_template'] = 0; $name = __('Unknown Device Template'); } else { $save['host_template'] = $template['id']; $name = $template['name']; } break; default: $save[$column] = $coldata; break; } } // save the automation network first $id = sql_save($save, 'automation_networks'); if ($id) { if (CACTI_WEB) { $debug_data['success'][] = __esc('Automation Network Rule \'%s\' %s!', $name, ($save['id'] > 0 ? __('Updated') : __('Imported'))); } else { $debug_data['success'][] = __('Automation Network Rule \'%s\' %s!', $name, ($save['id'] > 0 ? __('Updated') : __('Imported'))); } } else { if (CACTI_WEB) { $debug_data['failure'][] = __esc('Automation Network Rule \'%s\' %s Failed!', $name, ($save['id'] > 0 ? __('Update') : __('Import'))); } else { $debug_data['failure'][] = __('Automation Network Rule \'%s\' %s Failed!', $name, ($save['id'] > 0 ? __('Update') : __('Import'))); } } } } } else { $debug_data['failure'][] = __('Automation Network Rule Import data is either for another object type or not JSON formatted.'); } return $debug_data; } /** * Imports automation graph rules from JSON data. * * @param mixed $json_data The JSON data containing the automation graph rules. * * @return array An array containing the results of the import process. */ function automation_graph_rule_import(mixed $json_data) : array { $debug_data = []; /** * This routine will take two passes through the data. In the first pass, we will ensure * that all the required Cacti Template objects are in the database. If they are not * then we will abandon the import process. * * Once we have verified all the hashes to id's we will commence with the import from * top to bottom in the JSON array. */ if (cacti_sizeof($json_data) && isset($json_data['graph_rules'])) { $error = false; $save = []; $sqids = []; $sqgtids = []; foreach ($json_data['graph_rules'] as $rule) { $hash = $rule['hash']; if (isset($rule['snmp_query_id']) && $rule['snmp_query_id'] != '') { $hash = $rule['snmp_query_id']; $snmp_query_id = db_fetch_cell_prepared('SELECT id FROM snmp_query WHERE hash = ?', [$hash]); if (empty($snmp_query_id)) { $error = true; $debug_data['errors'][] = __('The Cacti install does not include the Data Query with the hash \'%s\'!', $hash); } else { $sqids[$hash] = $snmp_query_id; } } if (isset($rule['graph_type_id']) && $rule['graph_type_id'] != '') { $hash = $rule['graph_type_id']; $graph_type_id = db_fetch_cell_prepared('SELECT id FROM snmp_query_graph WHERE hash = ?', [$hash]); if (empty($graph_type_id)) { $error = true; $debug_data['errors'][] = __('The Cacti install does not include the Data Query Graph mapping with the hash \'%s\'!', $hash); } else { $sqgtids[$hash] = $graph_type_id; } } } if (!$error) { foreach ($json_data['graph_rules'] as $rule) { $hash = $rule['hash']; // prepare the save array $save = $rule; // remove object that don't belong unset($save['graph_rule_items']); unset($save['graph_match_items']); // Save the name $name = $save['name']; $save['id'] = db_fetch_cell_prepared('SELECT id FROM automation_graph_rules WHERE hash = ?', [$hash]); $save['snmp_query_id'] = $sqids[$rule['snmp_query_id']]; $save['graph_type_id'] = $sqgtids[$rule['graph_type_id']]; $graph_rule_id = sql_save($save, 'automation_graph_rules'); $graph_rule_ids[] = $graph_rule_id; if ($graph_rule_id) { if (CACTI_WEB) { $debug_data['success'][] = __esc('Automation Graph Rule \'%s\' %s!', $name, ($save['id'] > 0 ? __('Updated') : __('Imported'))); } else { $debug_data['success'][] = __('Automation Graph Rule \'%s\' %s!', $name, ($save['id'] > 0 ? __('Updated') : __('Imported'))); } } else { if (CACTI_WEB) { $debug_data['failure'][] = __esc('Automation Graph Rule \'%s\' %s Failed!', $name, ($save['id'] > 0 ? __('Update') : __('Import'))); } else { $debug_data['failure'][] = __('Automation Graph Rule \'%s\' %s Failed!', $name, ($save['id'] > 0 ? __('Update') : __('Import'))); } } if (cacti_sizeof($rule['graph_rule_items'])) { foreach ($rule['graph_rule_items'] as $rule_item) { $hash = $rule_item['hash']; $save = $rule_item; $save['id'] = db_fetch_cell_prepared('SELECT id FROM automation_graph_rule_items WHERE hash = ?', [$hash]); $save['rule_id'] = $graph_rule_id; $rule_item_id = sql_save($save, 'automation_graph_rule_items'); $graph_rule_item_ids[] = $rule_item_id; if ($rule_item_id) { if (CACTI_WEB) { $debug_data['success'][] = __esc('Automation Graph Rule Item \'%s\' %s!', $name, ($save['id'] > 0 ? __('Updated') : __('Imported'))); } else { $debug_data['success'][] = __('Automation Graph Rule Item \'%s\' %s!', $name, ($save['id'] > 0 ? __('Updated') : __('Imported'))); } } else { if (CACTI_WEB) { $debug_data['failure'][] = __esc('Automation Graph Rule Item \'%s\' %s Failed!', $name, ($save['id'] > 0 ? __('Update') : __('Import'))); } else { $debug_data['failure'][] = __('Automation Graph Rule Item \'%s\' %s Failed!', $name, ($save['id'] > 0 ? __('Update') : __('Import'))); } } } } if (cacti_sizeof($rule['graph_match_items'])) { foreach ($rule['graph_match_items'] as $match_item) { $hash = $match_item['hash']; $save = $match_item; $save['id'] = db_fetch_cell_prepared('SELECT id FROM automation_match_rule_items WHERE hash = ?', [$hash]); $save['rule_id'] = $graph_rule_id; $rule_item_id = sql_save($save, 'automation_match_rule_items'); $graph_rule_item_ids[] = $rule_item_id; if ($rule_item_id) { if (CACTI_WEB) { $debug_data['success'][] = __esc('Automation Graph Rule Match Item \'%s\' %s!', $name, ($save['id'] > 0 ? __('Updated') : __('Imported'))); } else { $debug_data['success'][] = __('Automation Graph Rule Match Item \'%s\' %s!', $name, ($save['id'] > 0 ? __('Updated') : __('Imported'))); } } else { if (CACTI_WEB) { $debug_data['failure'][] = __esc('Automation Graph Rule Match Item \'%s\' %s Failed!', $name, ($save['id'] > 0 ? __('Update') : __('Import'))); } else { $debug_data['failure'][] = __('Automation Graph Rule Match Item \'%s\' %s Failed!', $name, ($save['id'] > 0 ? __('Update') : __('Import'))); } } } } } } } else { $debug_data['failure'][] = __('Automation Graph Rule Import data is either for another object type or not JSON formatted.'); } return $debug_data; } /** * Imports automation tree rules from JSON data. * * @param mixed $json_data The JSON data containing the automation tree rules. * @param bool $tree_branches Optional. Whether to include tree branches in the import. Default is false. * * @return array An array containing the results of the import process. */ function automation_tree_rule_import(mixed $json_data, bool $tree_branches = false) : array { $debug_data = []; /** * This routine will take two passes through the data. In the first pass, we will ensure * that all the required Cacti Template objects are in the database. If they are not * then we will abandon the import process. * * Once we have verified all the hashes to id's we will commence with the import from * top to bottom in the JSON array. */ if (cacti_sizeof($json_data) && isset($json_data['tree_rules'])) { $error = false; $save = []; foreach ($json_data['tree_rules'] as $rule) { $hash = $rule['hash']; $name = $rule['name']; // prepare the save array $save = $rule; // unset the Tree and Branch ids $save['tree_id'] = 0; $save['tree_item_id'] = 0; // until we get the tree create done unset($save['tree_data']); unset($save['tree_branch_data']); if ($tree_branches) { if (isset($rule['tree_data']) && isset($rule['tree_branch_data'])) { [$save['tree_id'], $save['tree_item_id']] = automation_tree_rule_create_tree($rule['tree_data'], $rule['tree_branch_data']); } } $save['id'] = db_fetch_cell_prepared('SELECT id FROM automation_tree_rules WHERE hash = ?', [$hash]); // unset things that don't belong unset($save['tree_rule_items']); unset($save['tree_match_items']); $tree_rule_id = sql_save($save, 'automation_tree_rules'); $tree_rule_ids[] = $tree_rule_id; if ($tree_rule_id) { if (CACTI_WEB) { $debug_data['success'][] = __esc('Automation Tree Rule \'%s\' %s!', $name, ($save['id'] > 0 ? __('Updated') : __('Imported'))); } else { $debug_data['success'][] = __('Automation Tree Rule \'%s\' %s!', $name, ($save['id'] > 0 ? __('Updated') : __('Imported'))); } } else { if (CACTI_WEB) { $debug_data['failure'][] = __esc('Automation Tree Device Rule \'%s\' %s Failed!', $name, ($save['id'] > 0 ? __('Update') : __('Import'))); } else { $debug_data['failure'][] = __('Automation Tree Device Rule \'%s\' %s Failed!', $name, ($save['id'] > 0 ? __('Update') : __('Import'))); } } if (cacti_sizeof($rule['tree_rule_items'])) { foreach ($rule['tree_rule_items'] as $rule_item) { $hash = $rule_item['hash']; $save = $rule_item; $save['id'] = db_fetch_cell_prepared('SELECT id FROM automation_tree_rule_items WHERE hash = ?', [$hash]); $save['rule_id'] = $tree_rule_id; $rule_item_id = sql_save($save, 'automation_tree_rule_items'); if ($rule_item_id) { if (CACTI_WEB) { $debug_data['success'][] = __esc('Automation Tree Rule Item \'%s\' %s!', $name, ($save['id'] > 0 ? __('Updated') : __('Imported'))); } else { $debug_data['success'][] = __('Automation Tree Rule Item \'%s\' %s!', $name, ($save['id'] > 0 ? __('Updated') : __('Imported'))); } } else { if (CACTI_WEB) { $debug_data['failure'][] = __esc('Automation Tree Device Rule Item \'%s\' %s Failed!', $name, ($save['id'] > 0 ? __('Update') : __('Import'))); } else { $debug_data['failure'][] = __('Automation Tree Device Rule Item \'%s\' %s Failed!', $name, ($save['id'] > 0 ? __('Update') : __('Import'))); } } $tree_rule_item_ids[] = $rule_item_id; } } if (cacti_sizeof($rule['tree_match_items'])) { foreach ($rule['tree_match_items'] as $match_item) { $hash = $match_item['hash']; $save = $match_item; $save['id'] = db_fetch_cell_prepared('SELECT id FROM automation_match_rule_items WHERE hash = ?', [$hash]); $save['rule_id'] = $tree_rule_id; $rule_item_id = sql_save($save, 'automation_match_rule_items'); $tree_rule_item_ids[] = $rule_item_id; if ($rule_item_id) { if (CACTI_WEB) { $debug_data['success'][] = __esc('Automation Tree Rule Match Item \'%s\' %s!', $name, ($save['id'] > 0 ? __('Updated') : __('Imported'))); } else { $debug_data['success'][] = __('Automation Tree Rule Match Item \'%s\' %s!', $name, ($save['id'] > 0 ? __('Updated') : __('Imported'))); } } else { if (CACTI_WEB) { $debug_data['failure'][] = __esc('Automation Tree Device Rule Match Item \'%s\' %s Failed!', $name, ($save['id'] > 0 ? __('Update') : __('Import'))); } else { $debug_data['failure'][] = __('Automation Tree Device Rule Match Item \'%s\' %s Failed!', $name, ($save['id'] > 0 ? __('Update') : __('Import'))); } } } } } } else { $debug_data['failure'][] = __('Automation Tree Rule Import data is either for another object type or not JSON formatted.'); } return $debug_data; } /** * Imports an automation template from JSON data. * * @param mixed $json_data The JSON data to import. * @param bool $tree_branches Optional. Whether to include tree branches in the import. Default is false. * * @return array An array containing the result of the import process. */ function automation_template_import(mixed $json_data, bool $tree_branches = false) : array { $debug_data = []; /** * This routine will take two passes through the data. In the first pass, we will ensure * that all the required Cacti Template objects are in the database. If they are not * then we will abandon the import process. * * Once we have verified all the hashes to id's we will commence with the import from * top to bottom in the JSON array. */ if (cacti_sizeof($json_data) && isset($json_data['device'])) { $error = false; $save = []; $htids = []; $sqids = []; $sqgtids = []; foreach ($json_data['device'] as $data) { $hash = $data['host_template']; $host_template_id = db_fetch_cell_prepared('SELECT id FROM host_template WHERE hash = ?', [$hash]); if (empty($host_template_id)) { $error = true; $debug_data['errors'][] = __('The Cacti install does not include the Device Template with the hash \'%s\'!', $hash); } else { $htids[$hash] = $host_template_id; } if (cacti_sizeof($data['graph_rules'])) { foreach ($data['graph_rules'] as $rule) { $hash = $rule['hash']; if (isset($rule['snmp_query_id']) && $rule['snmp_query_id'] != '') { $hash = $rule['snmp_query_id']; $snmp_query_id = db_fetch_cell_prepared('SELECT id FROM snmp_query WHERE hash = ?', [$hash]); if (empty($snmp_query_id)) { $error = true; $debug_data['errors'][] = __('The Cacti install does not include the Data Query with the hash \'%s\'!', $hash); } else { $sqids[$hash] = $snmp_query_id; } } if (isset($rule['graph_type_id']) && $rule['graph_type_id'] != '') { $hash = $rule['graph_type_id']; $graph_type_id = db_fetch_cell_prepared('SELECT id FROM snmp_query_graph WHERE hash = ?', [$hash]); if (empty($graph_type_id)) { $error = true; $debug_data['errors'][] = __('The Cacti install does not include the Data Query Graph mapping with the hash \'%s\'!', $hash); } else { $sqgtids[$hash] = $graph_type_id; } } } } } if (!$error) { foreach ($json_data['device'] as $data) { $hash = $data['hash']; // prepare the save array $save = $data; $save['id'] = db_fetch_cell_prepared('SELECT id FROM automation_templates WHERE hash = ?', [$hash]); $save['host_template'] = $htids[$save['host_template']]; unset($save['device_rules']); unset($save['graph_rules']); unset($save['tree_rules']); unset($save['thold_rules']); $device_rule_id = sql_save($save, 'automation_templates'); $name = db_fetch_cell_prepared('SELECT name FROM host_template WHERE id = ?', [$save['host_template']]); if ($device_rule_id) { if (CACTI_WEB) { $debug_data['success'][] = __esc('Automation Device Rule \'%s\' %s!', $name, ($save['id'] > 0 ? __('Updated') : __('Imported'))); } else { $debug_data['success'][] = __('Automation Device Rule \'%s\' %s!', $name, ($save['id'] > 0 ? __('Updated') : __('Imported'))); } } else { if (CACTI_WEB) { $debug_data['failure'][] = __esc('Automation Device Rule \'%s\' %s Failed!', $name, ($save['id'] > 0 ? __('Update') : __('Import'))); } else { $debug_data['failure'][] = __('Automation Device Rule \'%s\' %s Failed!', $name, ($save['id'] > 0 ? __('Update') : __('Import'))); } } /** * We have to do Graph Rules and Tree Rules Next as * the Device Template rules require that they exist first. * Also, it must be noted that the Tree rules specify a * destination Tree, which may or may not exist on the * foreign system. * * So, for these next set of data, we have to go out of order. * We will process in the order below. * * - Graph Rules * - Tree Rules (where the tree_id will be set to 0 * - Device Rules * */ // we will use these ID's to remove deleted objects $device_rule_ids = []; $graph_rule_ids = []; $graph_rule_items_ids = []; $graph_match_item_ids = []; if (cacti_sizeof($data['graph_rules'])) { foreach ($data['graph_rules'] as $rule) { $hash = $rule['hash']; // prepare the save array $save = $rule; // Set the name $name = $save['name']; // remove object that don't belong unset($save['graph_rule_items']); unset($save['graph_match_items']); $save['id'] = db_fetch_cell_prepared('SELECT id FROM automation_graph_rules WHERE hash = ?', [$hash]); $save['snmp_query_id'] = $sqids[$rule['snmp_query_id']]; $save['graph_type_id'] = $sqgtids[$rule['graph_type_id']]; $graph_rule_id = sql_save($save, 'automation_graph_rules'); $graph_rule_ids[] = $graph_rule_id; if ($graph_rule_id) { if (CACTI_WEB) { $debug_data['success'][] = __esc('Automation Graph Rule \'%s\' %s!', $name, ($save['id'] > 0 ? __('Updated') : __('Imported'))); } else { $debug_data['success'][] = __('Automation Graph Rule \'%s\' %s!', $name, ($save['id'] > 0 ? __('Updated') : __('Imported'))); } } else { if (CACTI_WEB) { $debug_data['failure'][] = __esc('Automation Graph Rule \'%s\' %s Failed!', $name, ($save['id'] > 0 ? __('Update') : __('Import'))); } else { $debug_data['failure'][] = __('Automation Graph Rule \'%s\' %s Failed!', $name, ($save['id'] > 0 ? __('Update') : __('Import'))); } } if (cacti_sizeof($rule['graph_rule_items'])) { foreach ($rule['graph_rule_items'] as $rule_item) { $hash = $rule_item['hash']; $save = $rule_item; $save['id'] = db_fetch_cell_prepared('SELECT id FROM automation_graph_rule_items WHERE hash = ?', [$hash]); $save['rule_id'] = $graph_rule_id; $rule_item_id = sql_save($save, 'automation_graph_rule_items'); $graph_rule_item_ids[] = $rule_item_id; if ($rule_item_id) { if (CACTI_WEB) { $debug_data['success'][] = __esc('Automation Graph Rule Item \'%s\' %s!', $name, ($save['id'] > 0 ? __('Updated') : __('Imported'))); } else { $debug_data['success'][] = __('Automation Graph Rule Item \'%s\' %s!', $name, ($save['id'] > 0 ? __('Updated') : __('Imported'))); } } else { if (CACTI_WEB) { $debug_data['failure'][] = __esc('Automation Graph Rule Item \'%s\' %s Failed!', $name, ($save['id'] > 0 ? __('Update') : __('Import'))); } else { $debug_data['failure'][] = __('Automation Graph Rule Item \'%s\' %s Failed!', $name, ($save['id'] > 0 ? __('Update') : __('Import'))); } } } } if (cacti_sizeof($rule['graph_match_items'])) { foreach ($rule['graph_match_items'] as $match_item) { $hash = $match_item['hash']; $save = $match_item; $save['id'] = db_fetch_cell_prepared('SELECT id FROM automation_match_rule_items WHERE hash = ?', [$hash]); $save['rule_id'] = $graph_rule_id; $rule_item_id = sql_save($save, 'automation_match_rule_items'); $graph_rule_item_ids[] = $rule_item_id; if ($rule_item_id) { if (CACTI_WEB) { $debug_data['success'][] = __esc('Automation Graph Rule Match Item \'%s\' %s!', $name, ($save['id'] > 0 ? __('Updated') : __('Imported'))); } else { $debug_data['success'][] = __('Automation Graph Rule Match Item \'%s\' %s!', $name, ($save['id'] > 0 ? __('Updated') : __('Imported'))); } } else { if (CACTI_WEB) { $debug_data['failure'][] = __esc('Automation Graph Rule Match Item \'%s\' %s Failed!', $name, ($save['id'] > 0 ? __('Update') : __('Import'))); } else { $debug_data['failure'][] = __('Automation Graph Rule Match Item \'%s\' %s Failed!', $name, ($save['id'] > 0 ? __('Update') : __('Import'))); } } } } } } if (cacti_sizeof($data['tree_rules'])) { foreach ($data['tree_rules'] as $rule) { $hash = $rule['hash']; // prepare the save array $save = $rule; // unset the Tree id // ToDo: Actually Export the names of these to recreate them $save['tree_id'] = 0; $save['tree_item_id'] = 0; // Save the name for logging $name = $save['name']; // until we get the tree create done unset($save['tree_data']); unset($save['tree_branch_data']); if ($tree_branches) { if (isset($rule['tree_data']) && isset($rule['tree_branch_data'])) { [$save['tree_id'], $save['tree_item_id']] = automation_tree_rule_create_tree($rule['tree_data'], $rule['tree_branch_data']); } } $save['id'] = db_fetch_cell_prepared('SELECT id FROM automation_tree_rules WHERE hash = ?', [$hash]); // unset things that don't belong unset($save['tree_rule_items']); unset($save['tree_match_items']); $tree_rule_id = sql_save($save, 'automation_tree_rules'); $tree_rule_ids[] = $tree_rule_id; if ($tree_rule_id) { if (CACTI_WEB) { $debug_data['success'][] = __esc('Automation Tree Rule \'%s\' %s!', $name, ($save['id'] > 0 ? __('Updated') : __('Imported'))); } else { $debug_data['success'][] = __('Automation Tree Rule \'%s\' %s!', $name, ($save['id'] > 0 ? __('Updated') : __('Imported'))); } } else { if (CACTI_WEB) { $debug_data['failure'][] = __esc('Automation Tree Device Rule \'%s\' %s Failed!', $name, ($save['id'] > 0 ? __('Update') : __('Import'))); } else { $debug_data['failure'][] = __('Automation Tree Device Rule \'%s\' %s Failed!', $name, ($save['id'] > 0 ? __('Update') : __('Import'))); } } if (cacti_sizeof($rule['tree_rule_items'])) { foreach ($rule['tree_rule_items'] as $rule_item) { $hash = $rule_item['hash']; $save = $rule_item; $save['id'] = db_fetch_cell_prepared('SELECT id FROM automation_tree_rule_items WHERE hash = ?', [$hash]); $save['rule_id'] = $tree_rule_id; $rule_item_id = sql_save($save, 'automation_tree_rule_items'); if ($rule_item_id) { if (CACTI_WEB) { $debug_data['success'][] = __esc('Automation Tree Rule Item \'%s\' %s!', $name, ($save['id'] > 0 ? __('Updated') : __('Imported'))); } else { $debug_data['success'][] = __('Automation Tree Rule Item \'%s\' %s!', $name, ($save['id'] > 0 ? __('Updated') : __('Imported'))); } } else { if (CACTI_WEB) { $debug_data['failure'][] = __esc('Automation Tree Device Rule Item \'%s\' %s Failed!', $name, ($save['id'] > 0 ? __('Update') : __('Import'))); } else { $debug_data['failure'][] = __('Automation Tree Device Rule Item \'%s\' %s Failed!', $name, ($save['id'] > 0 ? __('Update') : __('Import'))); } } $tree_rule_item_ids[] = $rule_item_id; } } if (cacti_sizeof($rule['tree_match_items'])) { foreach ($rule['tree_match_items'] as $match_item) { $hash = $match_item['hash']; $save = $match_item; $save['id'] = db_fetch_cell_prepared('SELECT id FROM automation_match_rule_items WHERE hash = ?', [$hash]); $save['rule_id'] = $tree_rule_id; $rule_item_id = sql_save($save, 'automation_match_rule_items'); $tree_rule_item_ids[] = $rule_item_id; if ($rule_item_id) { if (CACTI_WEB) { $debug_data['success'][] = __esc('Automation Tree Rule Match Item \'%s\' %s!', $name, ($save['id'] > 0 ? __('Updated') : __('Imported'))); } else { $debug_data['success'][] = __('Automation Tree Rule Match Item \'%s\' %s!', $name, ($save['id'] > 0 ? __('Updated') : __('Imported'))); } } else { if (CACTI_WEB) { $debug_data['failure'][] = __esc('Automation Tree Device Rule Match Item \'%s\' %s Failed!', $name, ($save['id'] > 0 ? __('Update') : __('Import'))); } else { $debug_data['failure'][] = __('Automation Tree Device Rule Match Item \'%s\' %s Failed!', $name, ($save['id'] > 0 ? __('Update') : __('Import'))); } } } } } } /** * When we process these actions, we must be aware that the rule types for devices are: * rule type 1 - Graph Rule * rule type 2 - Tree Rule */ if (cacti_sizeof($data['device_rules'])) { foreach ($data['device_rules'] as $rule) { $hash = $rule['hash']; $save = $rule; $save['id'] = db_fetch_cell_prepared('SELECT id FROM automation_templates_rules WHERE hash = ?', [$hash]); $save['template_id'] = $device_rule_id; $name = db_fetch_cell_prepared('SELECT name FROM host_template WHERE id = ?', [$save['template_id']]); if ($rule['rule_type'] == 1) { // Graph Rules $save['rule_id'] = db_fetch_cell_prepared('SELECT id FROM automation_graph_rules WHERE hash = ?', [$rule['rule_id']]); } elseif ($rule['rule_type'] == 2) { // Tree Rules $save['rule_id'] = db_fetch_cell_prepared('SELECT id FROM automation_tree_rules WHERE hash = ?', [$rule['rule_id']]); } // save the automation network first $id = sql_save($save, 'automation_templates_rules'); if ($id) { if (CACTI_WEB) { $debug_data['success'][] = __esc('Automation Device Rule Item \'%s\' %s!', $name, ($save['id'] > 0 ? __('Updated') : __('Imported'))); } else { $debug_data['success'][] = __('Automation Device Rule Item \'%s\' %s!', $name, ($save['id'] > 0 ? __('Updated') : __('Imported'))); } } else { if (CACTI_WEB) { $debug_data['failure'][] = __esc('Automation Device Rule Item \'%s\' %s Failed!', $name, ($save['id'] > 0 ? __('Update') : __('Import'))); } else { $debug_data['failure'][] = __('Automation Device Rule Item \'%s\' %s Failed!', $name, ($save['id'] > 0 ? __('Update') : __('Import'))); } } } } } if (isset($data['thold_rules']) && cacti_sizeof($data['thold_rules'])) { if (!db_table_exists('plugin_thold_host_template')) { $debug_data['failure'][] = __('The Thold Plugin is not Installed on this system. So, Thresholds will not be imported.'); } else { foreach ($data['thold_rules'] as $rule) { $thold_name = $rule['thold_template']['name']; $thold_template_id = db_fetch_cell_prepared('SELECT id FROM thold_template WHERE hash = ?', [$rule['thold_template_id']]); $host_template_id = db_fetch_cell_prepared('SELECT id FROM host_template WHERE hash = ?', [$rule['host_template_id']]); if (empty($thold_template_id)) { $save = $rule; $save['id'] = 0; $save['data_template_id'] = db_fetch_cell_prepared('SELECT id FROM data_template WHERE hash = ?', [$rule['thold_template']['data_template_hash']]); $save['data_source_id'] = db_fetch_cell_prepared('SELECT id FROM data_template_rrd WHERE hash = ?', [$rule['thold_template']['data_source_hash']]); unset($save['data_source_hash']); $thold_template_id = sql_save($save, 'thold_template'); if ($thold_template_id) { if (CACTI_WEB) { $debug_data['success'][] = __esc('Automation Threshold Template \'%s\' %s!', $thold_name, ($save['id'] > 0 ? __('Updated') : __('Imported'))); } else { $debug_data['success'][] = __('Automation Threshold Template \'%s\' %s!', $thold_name, ($save['id'] > 0 ? __('Updated') : __('Imported'))); } } else { if (CACTI_WEB) { $debug_data['failure'][] = __esc('Automation Threshold Template Device \'%s\' %s Failed!', $thold_name, ($save['id'] > 0 ? __('Update') : __('Import'))); } else { $debug_data['failure'][] = __('Automation Threshold Template Device \'%s\' %s Failed!', $thold_name, ($save['id'] > 0 ? __('Update') : __('Import'))); } } } if ($thold_template_id > 0) { db_execute_prepared('REPLACE INTO plugin_thold_host_template (host_template_id, thold_template_id) VALUES (?, ?)', [$host_template_id, $thold_template_id]); } } } } } } else { $debug_data['failure'][] = __('Automation Device Rule Import data is either for another object type or not JSON formatted.'); } return $debug_data; } /** * Validates the import columns for a given table. * * @param string $table The name of the table to validate. * @param array &$data The data to be validated. * @param array &$debug_data An array to store debug information. * @return bool Returns true if the columns are valid, false otherwise. */ function automation_validate_import_columns(string $table, array &$data, array &$debug_data) : bool { if (cacti_sizeof($data)) { foreach ($data as $column => $cdata) { if (!db_column_exists($table, $column)) { $debug_data['errors'][] = __('Template column \'' . $column . '\' is not valid column.'); cacti_log('Template column \'' . $column . '\' is not valid column.', false, 'AUTOM8'); return false; } } } else { return false; } return true; } /** * Logs a string to Cacti's log file or optionally to the browser * * @param string $string The message to log. * @param int $level The log level, default is AUTOMATION_LOG_LOW. * * @return void */ function automation_log(string $string, int $level = AUTOMATION_LOG_LOW) : void { if (!defined('AUTOMATION_LEVEL')) { define('AUTOMATION_LEVEL', read_config_option('automation_log_level')); } if (AUTOMATION_LEVEL >= $level) { cacti_log($string, false, 'AUTOMATION'); } } /** * api_networks_save - Saves an automation network. * * @param array $post The post variables from the form. * * @return mixed The ID of the saved network or false on failure. */ function api_networks_save(array $post) : mixed { if (empty($post['network_id'])) { $save['id'] = CactiValidator::validateInput($post['id'], 'id', [new Assert\NotBlank(), new Assert\Regex('/^[0-9]+$/')]); $save['hash'] = get_hash_automation($post['id'], 'automation_networks'); // general information $save['name'] = CactiValidator::validateInput($post['name'], 'name', [new Assert\NotBlank()]); $save['poller_id'] = CactiValidator::validateInput($post['poller_id'], 'poller_id', [new Assert\NotBlank(), new Assert\Regex('/^[0-9]+$/')]); $save['site_id'] = CactiValidator::validateInput($post['site_id'], 'site_id', [new Assert\NotBlank(), new Assert\Regex('/^[0-9]+$/')]); $save['subnet_range'] = CactiValidator::validateInput($post['subnet_range'], 'subnet_range', [new Assert\NotBlank()]); $save['ignore_ips'] = CactiValidator::validateInput($post['ignore_ips'], 'ignore_ips', []); $save['dns_servers'] = CactiValidator::validateInput($post['dns_servers'], 'dns_servers', []); $save['threads'] = CactiValidator::validateInput($post['threads'], 'threads', [new Assert\NotBlank(), new Assert\Regex('/^[0-9]+$/')]); $save['run_limit'] = CactiValidator::validateInput($post['run_limit'], 'run_limit', [new Assert\NotBlank(), new Assert\Regex('/^[0-9]+$/')]); $save['enabled'] = (isset($post['enabled']) ? 'on' : ''); // notification settings $save['notification_enabled'] = (isset($post['notification_enabled']) ? 'on' : ''); $save['notification_email'] = CactiValidator::validateInput($post['notification_email'], 'notification_email', [new Assert\Email(mode: 'html5')]); $save['notification_fromname'] = CactiValidator::validateInput($post['notification_fromname'], 'notification_fromname', []); $save['notification_fromemail'] = CactiValidator::validateInput($post['notification_fromemail'], 'notification_fromemail', [new Assert\Email(mode: 'html5')]); $save['enable_netbios'] = (isset($post['enable_netbios']) ? 'on' : ''); $save['add_to_cacti'] = (isset($post['add_to_cacti']) ? 'on' : ''); $save['same_sysname'] = (isset($post['same_sysname']) ? 'on' : ''); $save['rerun_data_queries'] = (isset($post['rerun_data_queries']) ? 'on' : ''); // discovery connectivity settings $save['snmp_id'] = CactiValidator::validateInput($post['snmp_id'], 'snmp_id', [new Assert\NotBlank(), new Assert\Regex('/^[0-9]+$/')]); $save['ping_method'] = CactiValidator::validateInput($post['ping_method'], 'ping_method', [new Assert\NotBlank(), new Assert\Regex('/^[0-9]+$/')]); $save['ping_port'] = CactiValidator::validateInput($post['ping_port'], 'ping_port', [new Assert\NotBlank(), new Assert\Regex('/^[0-9]+$/')]); $save['ping_timeout'] = CactiValidator::validateInput($post['ping_timeout'], 'ping_timeout', [new Assert\NotBlank(), new Assert\Regex('/^[0-9]+$/')]); $save['ping_retries'] = CactiValidator::validateInput($post['ping_retries'], 'ping_retries', [new Assert\NotBlank(), new Assert\Regex('/^[0-9]+$/')]); $save = api_scheduler_augment_save($save, $post); // validate the network definitions and rais error if failed $continue = true; $total_ips = 0; $networks = explode(',', $save['subnet_range']); if (cacti_sizeof($networks)) { foreach ($networks as $net) { $ips = automation_calculate_total_ips($net); if ($ips !== false) { $total_ips += $ips; } else { $continue = false; raise_message('automation_message', __esc('ERROR: Network \'%s\' is Invalid.', $net), MESSAGE_LEVEL_ERROR); break; } } } if ($continue) { $save['total_ips'] = $total_ips; $network_id = 0; if (!is_error_message()) { $network_id = sql_save($save, 'automation_networks'); if ($network_id) { raise_message(1); } else { raise_message(2); } } return $network_id; } } return false; }