0) { // If port index found, try this entPhysicalIndex in next round $sensor_index = $tmp_index; } } elseif ($sensor_multilane) { $entries = dbFetchRows('SELECT `entPhysicalIndex`, `entPhysicalName` FROM `entPhysical` WHERE `device_id` = ? AND `entPhysicalContainedIn` = ? AND `entPhysicalClass` = ? AND `deleted` IS NULL', array($device['device_id'], $sensor_index, 'port')); print_debug("Multi-Lane entries:"); print_debug_vars($entries, 1); if (count($entries) > 1 && preg_match('/(?\D{2})(?\d+(?:\/\d+)+).*Lane\s*(?\d+)/', $sensor_name, $matches)) { // detect port numeric part and lane // There is each Line associated with breakout port, mostly is QSFP+ 40G // FortyGigE0/0/0/0-Tx Lane 1 Power -> 0/RP0-TenGigE0/0/0/0/1 // FortyGigE0/0/0/0-Tx Lane 2 Power -> 0/RP0-TenGigE0/0/0/0/2 $lane_num = $matches['start'] . $matches['num'] . '/' . $matches['lane']; // FortyGigE0/0/0/0-Tx Lane 1 -> gE0/0/0/0/1 foreach ($entries as $entry) { if (str_ends($entry['entPhysicalName'], $lane_num)) { $sensor_index = $entry['entPhysicalIndex']; break; } } } elseif (is_numeric($entries[0]['entPhysicalIndex']) && $entries[0]['entPhysicalIndex'] > 0) { // Single multi-lane port association, ie 100G $sensor_index = $entries[0]['entPhysicalIndex']; } } } // NOTE for self: entPhysicalParentRelPos >= 0 because on iosxr trouble } while ($sensor_port['entPhysicalClass'] !== 'port' && $sensor_port['entPhysicalContainedIn'] > 0 && ($sensor_port['entPhysicalParentRelPos'] >= 0 || $device['os'] === 'arista_eos')); return NULL; } // Get port array by ID (using cache) // DOCME needs phpdoc block // TESTME needs unit testing function get_port_by_id_cache($port_id) { return get_entity_by_id_cache('port', $port_id); } // Get port array by ID (with port state) // NOTE get_port_by_id(ID) != get_port_by_id_cache(ID) // DOCME needs phpdoc block // TESTME needs unit testing function get_port_by_id($port_id) { if (is_numeric($port_id)) { //$port = dbFetchRow("SELECT * FROM `ports` LEFT JOIN `ports-state` ON `ports`.`port_id` = `ports-state`.`port_id` WHERE `ports`.`port_id` = ?", array($port_id)); $port = dbFetchRow("SELECT * FROM `ports` WHERE `ports`.`port_id` = ?", array($port_id)); } if (is_array($port)) { //$port['port_id'] = $port_id; // It corrects the situation, when `ports-state` is empty humanize_port($port); return $port; } return FALSE; } // Get port array by ifIndex (using cache) // DOCME needs phpdoc block // TESTME needs unit testing function get_port_by_index_cache($device, $ifIndex, $deleted = 0) { global $cache; if (is_array($device) && isset($device['device_id'])) { $device_id = $device['device_id']; } elseif (is_numeric($device)) { $device_id = $device; } if (!isset($device_id) || !is_intnum($ifIndex)) { print_error("Invalid arguments passed into function get_port_by_index_cache(). Please report to developers."); return FALSE; } if (OBS_PROCESS_NAME === 'poller' && !isset($cache['port_index'][$device_id]) && !$deleted) { // Pre-cache all ports in poller for speedup db queries foreach (dbFetchRows('SELECT * FROM `ports` WHERE `device_id` = ? AND `deleted` = ?', [ $device_id, 0 ]) as $entity) { if (is_numeric($entity['port_id'])) { // Cache ifIndex to port ID translations $cache['port_index'][$device_id][$entity['ifIndex']] = $entity['port_id']; // Same caching as in get_entity_by_id_cache() humanize_port($entity); entity_rewrite('port', $entity); $cache['port'][$entity['port_id']] = $entity; } } } if (isset($cache['port_index'][$device_id][$ifIndex]) && is_numeric($cache['port_index'][$device_id][$ifIndex])) { $id = $cache['port_index'][$device_id][$ifIndex]; } else { $deleted = $deleted ? 1 : 0; // Just convert boolean to 0 or 1 $id = dbFetchCell("SELECT `port_id` FROM `ports` WHERE `device_id` = ? AND `ifIndex` = ? AND `deleted` = ? LIMIT 1", [ $device_id, $ifIndex, $deleted ]); if (!$deleted && is_numeric($id)) { // Cache port IDs (except deleted) $cache['port_index'][$device_id][$ifIndex] = $id; } } $port = get_entity_by_id_cache('port', $id); if (is_array($port)) { return $port; } return FALSE; } // Get port array by ifIndex // DOCME needs phpdoc block // TESTME needs unit testing function get_port_by_ifIndex($device_id, $ifIndex) { if (is_array($device_id)) { $device_id = $device_id['device_id']; } $port = dbFetchRow("SELECT * FROM `ports` WHERE `device_id` = ? AND `ifIndex` = ? LIMIT 1", array($device_id, $ifIndex)); if (is_array($port)) { humanize_port($port); return $port; } return FALSE; } // Get port ID by ifDescr (i.e. 'TenGigabitEthernet1/1') or ifName (i.e. 'Te1/1') // DOCME needs phpdoc block // TESTME needs unit testing function get_port_id_by_ifDescr($device_id, $ifDescr, $deleted = 0) { if (is_array($device_id)) { $device_id = $device_id['device_id']; } $port_id = dbFetchCell("SELECT `port_id` FROM `ports` WHERE `device_id` = ? AND (`ifDescr` = ? OR `ifName` = ? OR `port_label_short` = ?) AND `deleted` = ? LIMIT 1", [ $device_id, $ifDescr, $ifDescr, $ifDescr, $deleted ]); if (is_numeric($port_id)) { return $port_id; } return FALSE; } // Get port ID by ifAlias (interface description) // DOCME needs phpdoc block // TESTME needs unit testing function get_port_id_by_ifAlias($device_id, $ifAlias, $deleted = 0) { if (is_array($device_id)) { $device_id = $device_id['device_id']; } $port_id = dbFetchCell("SELECT `port_id` FROM `ports` WHERE `device_id` = ? AND `ifAlias` = ? AND `deleted` = ? LIMIT 1", array($device_id, $ifAlias, $deleted)); if (is_numeric($port_id)) { return $port_id; } return FALSE; } // Get port ID by customer params (see http://www.observium.org/wiki/Interface_Description_Parsing) // DOCME needs phpdoc block // TESTME needs unit testing function get_port_id_by_customer($customer) { $where = ' WHERE 1'; if (is_array($customer)) { foreach ($customer as $var => $value) { if ($value != '') { switch ($var) { case 'device': case 'device_id': $where .= generate_query_values($value, 'device_id'); break; case 'type': case 'descr': case 'circuit': case 'speed': case 'notes': $where .= generate_query_values($value, 'port_descr_'.$var); break; } } } } else { return FALSE; } $query = 'SELECT `port_id` FROM `ports` ' . $where . ' ORDER BY `ifOperStatus` DESC'; $ids = dbFetchColumn($query); //print_vars($ids); switch (count($ids)) { case 0: return FALSE; case 1: return $ids[0]; break; default: foreach ($ids as $port_id) { $port = get_port_by_id_cache($port_id); $device = device_by_id_cache($port['device_id']); if ($device['disabled'] || !$device['status']) { continue; // switch to next ID } break; } return $port_id; } return FALSE; } function get_device_ids_by_customer($type, $customer) { if ($customer === '' || is_null($customer)) { return NULL; } // Recursive merge if (is_array($customer)) { $ids = []; foreach ($customer as $entry) { if ($entry_ids = get_device_ids_by_customer($type, $entry)) { $ids = array_merge($ids, $entry_ids); } } return $ids; } $where = ' WHERE 1'; switch ($type) { case 'device': case 'device_id': $where .= generate_query_values($customer, 'device_id'); break; case 'type': case 'descr': case 'circuit': case 'speed': case 'notes': $where .= generate_query_values($customer, 'port_descr_'.$type); break; default: $where .= generate_query_values($customer, 'port_descr_descr'); } $query = 'SELECT DISTINCT `device_id` FROM `ports` ' . $where; return dbFetchColumn($query); } // DOCME needs phpdoc block // TESTME needs unit testing function is_port_valid($port, $device) { global $config; // Ignore non standard ifOperStatus // See http://tools.cisco.com/Support/SNMP/do/BrowseOID.do?objectInput=ifOperStatus $valid_ifOperStatus = [ 'testing', 'dormant', 'down', 'lowerLayerDown', 'unknown', 'up', 'monitoring' ]; if (isset($port['ifOperStatus']) && strlen($port['ifOperStatus']) && !in_array($port['ifOperStatus'], $valid_ifOperStatus)) { print_debug("ignored (by ifOperStatus = notPresent or invalid value)."); return FALSE; } // Per os definition $def = isset($config['os'][$device['os']]['ports_ignore']) ? $config['os'][$device['os']]['ports_ignore'] : []; // Ignore ports with empty ifType if (isset($def['allow_empty'])) { $ports_allow_empty = (bool) $def['allow_empty']; unset($def['allow_empty']); } else { $ports_allow_empty = FALSE; } if (!isset($port['ifType']) && !$ports_allow_empty) { /* Some devices (ie D-Link) report ports without any useful info, example: [74] => Array ( [ifName] => po22 [ifInMulticastPkts] => 0 [ifInBroadcastPkts] => 0 [ifOutMulticastPkts] => 0 [ifOutBroadcastPkts] => 0 [ifLinkUpDownTrapEnable] => enabled [ifHighSpeed] => 0 [ifPromiscuousMode] => false [ifConnectorPresent] => false [ifAlias] => po22 [ifCounterDiscontinuityTime] => 0:0:00:00.00 ) */ print_debug("ignored (by empty ifType)."); return FALSE; } // This happens on some liebert UPS devices or when device have memory leak (ie Eaton Powerware) if (isset($config['os'][$device['os']]['ifType_ifDescr']) && $config['os'][$device['os']]['ifType_ifDescr'] && $port['ifIndex']) { $len = strlen($port['ifDescr']); $type = rewrite_iftype($port['ifType']); if ($type && ($len === 0 || $len > 255 || isHexString($port['ifDescr']) || preg_match('/(.)\1{4,}/', $port['ifDescr']))) { $port['ifDescr'] = $type . ' ' . $port['ifIndex']; } } //$if = ($config['os'][$device['os']]['ifname'] ? $port['ifName'] : $port['ifDescr']); $valid_ifDescr = !safe_empty($port['ifDescr']); $valid_ifName = !safe_empty($port['ifName']); // Ignore ports with empty ifName and ifDescr (while not possible store in db) if (!$valid_ifDescr && !$valid_ifName) { print_debug("ignored (by empty ifDescr and ifName)."); return FALSE; } // ifName not same as ifDescr (speedup label checks) $nosame_ifName = $port['ifDescr'] !== $port['ifName']; // Complex Oid based checks foreach ($def as $i => $array) { if (!is_numeric($i)) { continue; } // Ignore old definitions $count = safe_count($array); // count required Oids // Oids: ifType, ifAlias, ifDescr, ifName, label $found = 0; $table_rows = []; foreach ($array as $oid => $entry) { switch (strtolower($oid)) { case 'type': case 'iftype': if (str_contains_array($port['ifType'], $entry)) { $table_rows[] = [ 'ifType', $GLOBALS['str_last_needle'], $port['ifType'] ]; $found++; } break; // This defs always regex! case 'descr': case 'ifdescr': if (!$valid_ifDescr) { break; } foreach ((array)$entry as $pattern) { if (preg_match($pattern, $port['ifDescr'])) { $table_rows[] = [ 'ifDescr', $pattern, $port['ifDescr'] ]; $found++; break; } } break; case 'name': case 'ifname': if (!$valid_ifName) { break; } foreach ((array)$entry as $pattern) { if (preg_match($pattern, $port['ifName'])) { $table_rows[] = [ 'ifName', $pattern, $port['ifName'] ]; $found++; break; } } break; case 'label': if ($valid_ifDescr) { foreach ((array)$entry as $pattern) { if (preg_match($pattern, $port['ifDescr'])) { $table_rows[] = [ 'ifDescr', $pattern, $port['ifDescr'] ]; $found++; break; } } } if ($valid_ifName && $nosame_ifName) { foreach ((array)$entry as $pattern) { if (preg_match($pattern, $port['ifName'])) { $table_rows[] = [ 'ifName', $pattern, $port['ifName'] ]; $found++; break; } } } break; case 'alias': case 'ifalias': foreach ((array)$entry as $pattern) { if (preg_match($pattern, $port['ifAlias'])) { $table_rows[] = [ 'ifAlias', $pattern, $port['ifAlias'] ]; $found++; break; } } break; } } if ($count && $found === $count) { // Show matched Oids print_debug("ignored (by Oids):"); $table_headers = [ '%WOID%n', '%WMatched definition%n', '%WValue%n' ]; print_cli_table($table_rows, $table_headers); return FALSE; } } /* Global Configs */ // Ignore ports by ifAlias if (isset($config['bad_ifalias_regexp'])) { foreach ((array)$config['bad_ifalias_regexp'] as $bi) { if (preg_match($bi, $port['ifAlias'])) { print_debug("ignored (by ifAlias): ".$port['ifAlias']." [ $bi ]"); return FALSE; } } } // Ignore ports by ifName/ifDescr (do not forced as case insensitive) if (isset($config['bad_if_regexp'])) { foreach ((array)$config['bad_if_regexp'] as $bi) { if ($valid_ifDescr && preg_match($bi, $port['ifDescr'])) { print_debug("ignored (by ifDescr regexp): ".$port['ifDescr']." [ $bi ]"); return FALSE; } if ($valid_ifName && $nosame_ifName && preg_match($bi, $port['ifName'])) { print_debug("ignored (by ifName regexp): ".$port['ifName']." [ $bi ]"); return FALSE; } } } // FIXME. Prefer regexp if ($valid_ifDescr && str_icontains_array($port['ifDescr'], (array)$config['bad_if'])) { $bi = $GLOBALS['str_last_needle']; print_debug("ignored (by ifDescr): ".$port['ifDescr']." [ $bi ]"); return FALSE; } if ($valid_ifName && $nosame_ifName && str_icontains_array($port['ifName'], (array)$config['bad_if'])) { $bi = $GLOBALS['str_last_needle']; print_debug("ignored (by ifName): ".$port['ifName']." [ $bi ]"); return FALSE; } // Ignore ports by ifType if (isset($config['bad_iftype']) && str_contains_array($port['ifType'], (array)$config['bad_iftype'])) { $bi = $GLOBALS['str_last_needle']; print_debug("ignored (by ifType): ".$port['ifType']." [ $bi ]"); return FALSE; } return TRUE; } // Delete port from database and associated rrd files // DOCME needs phpdoc block // TESTME needs unit testing function delete_port($int_id, $delete_rrd = TRUE) { global $config; $port = dbFetchRow("SELECT * FROM `ports` LEFT JOIN `devices` USING (`device_id`) WHERE `port_id` = ?", array($int_id)); $ret = "> Deleted interface from ".$port['hostname'].": id=$int_id (".$port['ifDescr'].")\n"; // Remove entities from common tables $deleted_entities = array(); foreach ($config['entity_tables'] as $table) { $where = '`entity_type` = ?' . generate_query_values($int_id, 'entity_id'); $table_status = dbDelete($table, $where, array('port')); if ($table_status) { $deleted_entities['port'] = 1; } } if (count($deleted_entities)) { $ret .= ' * Deleted common entity entries linked to port.' . PHP_EOL; } // FIXME, move to definitions $port_tables = array('eigrp_ports', 'ipv4_addresses', 'ipv6_addresses', 'ip_mac', 'juniAtmVp', 'mac_accounting', 'ospf_nbrs', 'ospf_ports', 'ports_adsl', 'ports_cbqos', 'ports_vlans', 'pseudowires', 'vlans_fdb', 'neighbours', 'ports'); $deleted_tables = []; foreach ($port_tables as $table) { $table_status = dbDelete($table, "`port_id` = ?", array($int_id)); if ($table_status) { $deleted_tables[] = $table; } } $table_status = dbDelete('ports_stack', "`port_id_high` = ? OR `port_id_low` = ?", array($port['ifIndex'], $port['ifIndex'])); if ($table_status) { $deleted_tables[] = 'ports_stack'; } $table_status = dbDelete('entity_permissions', "`entity_type` = 'port' AND `entity_id` = ?", array($int_id)); if ($table_status) { $deleted_tables[] = 'entity_permissions'; } $table_status = dbDelete('alert_table', "`entity_type` = 'port' AND `entity_id` = ?", array($int_id)); if ($table_status) { $deleted_tables[] = 'alert_table'; } $table_status = dbDelete('group_table', "`entity_type` = 'port' AND `entity_id` = ?", array($int_id)); if ($table_status) { $deleted_tables[] = 'group_table'; } $ret .= ' * Deleted interface entries from tables: '.implode(', ', $deleted_tables).PHP_EOL; if ($delete_rrd) { $rrd_types = array('adsl', 'dot3', 'fdbcount', 'poe', NULL); $deleted_rrds = []; foreach ($rrd_types as $type) { $rrdfile = get_port_rrdfilename($port, $type, TRUE); if (is_file($rrdfile)) { unlink($rrdfile); $deleted_rrds[] = $rrdfile; } } $ret .= ' * Deleted interface RRD files: ' . implode(', ', $deleted_rrds) . PHP_EOL; } return $ret; } // DOCME needs phpdoc block // TESTME needs unit testing function port_html_class($ifOperStatus, $ifAdminStatus, $encrypted = FALSE) { $ifclass = "interface-upup"; if ($ifAdminStatus == "down") { $ifclass = "gray"; } elseif ($ifAdminStatus == "up") { if ($ifOperStatus == "down") { $ifclass = "red"; } elseif ($ifOperStatus == "lowerLayerDown") { $ifclass = "orange"; } elseif ($ifOperStatus == "monitoring") { $ifclass = "green"; } //elseif ($encrypted === '1') { $ifclass = "olive"; } elseif ($encrypted) { $ifclass = "olive"; } elseif ($ifOperStatus == "up") { $ifclass = ""; } else { $ifclass = "purple"; } } return $ifclass; } // DOCME needs phpdoc block // TESTME needs unit testing function get_port_rrdindex($port) { global $config; if (isset($port['device_id'])) { $device_id = $port['device_id']; } else { // In poller, device_id not always passed $port_tmp = get_port_by_id_cache($port['port_id']); $device_id = $port_tmp['device_id']; } $device = device_by_id_cache($device_id); if (isset($config['os'][$device['os']]['port_rrd_identifier'])) { $device_identifier = strtolower($config['os'][$device['os']]['port_rrd_identifier']); } else { $device_identifier = 'ifindex'; } // default to ifIndex $this_port_identifier = $port['ifIndex']; if ($device_identifier == "ifname" && $port['ifName'] != "") { $this_port_identifier = strtolower(str_replace("/", "-", $port['ifName'])); } return $this_port_identifier; } // CLEANME DEPRECATED function get_port_rrdfilename($port, $suffix = NULL, $fullpath = FALSE) { $this_port_identifier = get_port_rrdindex($port); if ($suffix == "") { $filename = "port-" . $this_port_identifier . ".rrd"; } else { $filename = "port-" . $this_port_identifier . "-" . $suffix . ".rrd"; } if ($fullpath) { if (isset($port['device_id'])) { $device_id = $port['device_id']; } else { // In poller, device_id not always passed $port_tmp = get_port_by_id_cache($port['port_id']); $device_id = $port_tmp['device_id']; } $device = device_by_id_cache($device_id); $filename = get_rrd_path($device, $filename); } return $filename; } // EOF