1<?php 2/** 3 * Observium 4 * 5 * This file is part of Observium. 6 * 7 * @package observium 8 * @subpackage entities 9 * @copyright (C) 2006-2013 Adam Armstrong, (C) 2013-2019 Observium Limited 10 * 11 * 12 */ 13 14// Get port id by ip address (using cache) 15// DOCME needs phpdoc block 16// TESTME needs unit testing 17function get_port_id_by_ip_cache($device, $ip) 18{ 19 global $cache; 20 21 $ip_version = get_ip_version($ip); 22 23 if (is_array($device) && isset($device['device_id'])) 24 { 25 $device_id = $device['device_id']; 26 } 27 elseif (is_numeric($device)) 28 { 29 $device_id = $device; 30 } 31 if (!isset($device_id) || !$ip_version) 32 { 33 print_error("Invalid arguments passed into function get_port_id_by_ip_cache(). Please report to developers."); 34 return FALSE; 35 } 36 37 if ($ip_version == 6) 38 { 39 $ip = Net_IPv6::uncompress($ip, TRUE); 40 } 41 42 if (isset($cache['port_ip'][$device_id][$ip])) 43 { 44 return $cache['port_ip'][$device_id][$ip]; 45 } 46 47 $ips = dbFetchRows('SELECT `port_id`, `ifOperStatus`, `ifAdminStatus` FROM `ipv'.$ip_version.'_addresses` 48 LEFT JOIN `ports` USING(`port_id`) 49 WHERE `deleted` = 0 AND `device_id` = ? AND `ipv'.$ip_version.'_address` = ?', array($device_id, $ip)); 50 if (count($ips) === 1) 51 { 52 // Simple 53 $port = current($ips); 54 //return $port['port_id']; 55 } else { 56 foreach ($ips as $entry) 57 { 58 if ($entry['ifAdminStatus'] == 'up' && $entry['ifOperStatus'] == 'up') 59 { 60 // First UP entry 61 $port = $entry; 62 break; 63 } 64 elseif ($entry['ifAdminStatus'] == 'up') 65 { 66 // Admin up, but port down or other state 67 $ips_up[] = $entry; 68 } else { 69 // Admin down 70 $ips_down[] = $entry; 71 } 72 } 73 if (!isset($port)) 74 { 75 if ($ips_up) 76 { 77 $port = current($ips_up); 78 } else { 79 $port = current($ips_down); 80 } 81 } 82 } 83 $cache['port_ip'][$device_id][$ip] = $port['port_id'] ? $port['port_id'] : FALSE; 84 85 return $cache['port_ip'][$device_id][$ip]; 86 87} 88 89function get_port_by_ent_index($device, $entPhysicalIndex, $allow_snmp = FALSE) 90{ 91 $mib = 'ENTITY-MIB'; 92 if (!is_numeric($entPhysicalIndex) || 93 !is_numeric($device['device_id']) || 94 !is_device_mib($device, $mib)) 95 { 96 return FALSE; 97 } 98 99 $allow_snmp = $allow_snmp || is_cli(); // Allow snmpwalk queries in poller/discovery or if in wui passed TRUE! 100 101 if (isset($GLOBALS['cache']['snmp'][$mib][$device['device_id']])) 102 { 103 // Cached 104 $entity_array = $GLOBALS['cache']['snmp'][$mib][$device['device_id']]; 105 if (empty($entity_array)) 106 { 107 // Force DB queries 108 $allow_snmp = FALSE; 109 } 110 } 111 elseif ($allow_snmp) 112 { 113 // Inventory module disabled, this DB empty, try to cache 114 $entity_array = array(); 115 $oids = array('entPhysicalDescr', 'entPhysicalName', 'entPhysicalClass', 'entPhysicalContainedIn', 'entPhysicalParentRelPos'); 116 if (is_device_mib($device, 'ARISTA-ENTITY-SENSOR-MIB')) 117 { 118 $oids[] = 'entPhysicalAlias'; 119 } 120 foreach ($oids as $oid) 121 { 122 $entity_array = snmpwalk_cache_multi_oid($device, $oid, $entity_array, snmp_mib_entity_vendortype($device, 'ENTITY-MIB')); 123 if (!$GLOBALS['snmp_status']) { break; } 124 } 125 $entity_array = snmpwalk_cache_twopart_oid($device, 'entAliasMappingIdentifier', $entity_array, 'ENTITY-MIB:IF-MIB'); 126 if (empty($entity_array)) 127 { 128 // Force DB queries 129 $allow_snmp = FALSE; 130 } 131 $GLOBALS['cache']['snmp'][$mib][$device['device_id']] = $entity_array; 132 } else { 133 // Or try to use DB 134 } 135 136 //print_debug_vars($entity_array); 137 $sensor_index = $entPhysicalIndex; // Initial ifIndex 138 $sensor_name = ''; 139 do 140 { 141 if ($allow_snmp) 142 { 143 // SNMP (discovery) 144 $sensor_port = $entity_array[$sensor_index]; 145 } else { 146 // DB (web) 147 $sensor_port = dbFetchRow('SELECT * FROM `entPhysical` WHERE `device_id` = ? AND `entPhysicalIndex` = ?', array($device['device_id'], $sensor_index)); 148 } 149 print_debug_vars($sensor_index, 1); 150 print_debug_vars($sensor_port, 1); 151 152 if ($sensor_port['entPhysicalClass'] == 'sensor') 153 { 154 // Need to store initial sensor name, for multi-lane ports 155 $sensor_name = $sensor_port['entPhysicalName']; 156 } 157 158 if ($sensor_port['entPhysicalClass'] === 'port') 159 { 160 // Port found, get mapped ifIndex 161 unset($entAliasMappingIdentifier); 162 foreach (array(0, 1, 2) as $i) 163 { 164 if (isset($sensor_port[$i]['entAliasMappingIdentifier'])) 165 { 166 $entAliasMappingIdentifier = $sensor_port[$i]['entAliasMappingIdentifier']; 167 break; 168 } 169 } 170 if (isset($entAliasMappingIdentifier) && str_contains($entAliasMappingIdentifier, 'fIndex')) 171 { 172 list(, $ifIndex) = explode('.', $entAliasMappingIdentifier); 173 174 $port = get_port_by_index_cache($device['device_id'], $ifIndex); 175 if (is_array($port)) 176 { 177 // Hola, port really found 178 //$options['entPhysicalIndex_measured'] = $ifIndex; 179 //$options['measured_class'] = 'port'; 180 //$options['measured_entity'] = $port['port_id']; 181 print_debug("Port is found: ifIndex = $ifIndex, port_id = " . $port['port_id']); 182 return $port; 183 } 184 } 185 elseif (!$allow_snmp && $sensor_port['ifIndex']) 186 { 187 $ifIndex = $sensor_port['ifIndex']; 188 $port = get_port_by_index_cache($device['device_id'], $ifIndex); 189 print_debug("Port is found: ifIndex = $ifIndex, port_id = " . $port['port_id']); 190 return $port; 191 } 192 193 break; // Exit do-while 194 } 195 elseif ($device['os'] == 'arista_eos' && $sensor_port['entPhysicalClass'] == 'container' && strlen($sensor_port['entPhysicalAlias'])) 196 { 197 // Arista not have entAliasMappingIdentifier, but used entPhysicalAlias as ifDescr 198 $port_id = get_port_id_by_ifDescr($device['device_id'], $sensor_port['entPhysicalAlias']); 199 if (is_numeric($port_id)) 200 { 201 // Hola, port really found 202 $port = get_port_by_id($port_id); 203 $ifIndex = $port['ifIndex']; 204 //$options['entPhysicalIndex_measured'] = $ifIndex; 205 //$options['measured_class'] = 'port'; 206 //$options['measured_entity'] = $port_id; 207 print_debug("Port is found: ifIndex = $ifIndex, port_id = " . $port_id); 208 return $port; 209 //break; // Exit do-while 210 } 211 $sensor_index = $sensor_port['entPhysicalContainedIn']; // Next ifIndex 212 } 213 elseif ($sensor_index == $sensor_port['entPhysicalContainedIn']) 214 { 215 break; // Break if current index same as next to avoid loop 216 } else { 217 $sensor_index = $sensor_port['entPhysicalContainedIn']; // Next ifIndex 218 219 // See: http://jira.observium.org/browse/OBS-2295 220 // IOS-XE and IOS-XR can store in module index both: sensors and port 221 $sensor_transceiver = $sensor_port['entPhysicalClass'] == 'sensor' && 222 str_icontains($sensor_port['entPhysicalName'] . $sensor_port['entPhysicalDescr'] . $sensor_port['entPhysicalVendorType'], array('transceiver', '-PORT-')); 223 // This is multi-lane optical transceiver, ie 100G, 40G, multiple sensors for each port 224 $sensor_multilane = $sensor_port['entPhysicalClass'] == 'container' && 225 (in_array($sensor_port['entPhysicalVendorType'], ['cevContainer40GigBasePort', 'cevContainerCXP', 'cevContainerCPAK', ]) || // Known Cisco specific containers 226 str_contains($sensor_port['entPhysicalName'] . $sensor_port['entPhysicalDescr'], array('Optical'))); 227 if ($sensor_transceiver) 228 { 229 $tmp_index = dbFetchCell('SELECT `entPhysicalIndex` FROM `entPhysical` WHERE `device_id` = ? AND `entPhysicalContainedIn` = ? AND `entPhysicalClass` = ?', array($device['device_id'], $sensor_index, 'port')); 230 if (is_numeric($tmp_index) && $tmp_index > 0) 231 { 232 // If port index found, try this entPhysicalIndex in next round 233 $sensor_index = $tmp_index; 234 } 235 } 236 elseif ($sensor_multilane) 237 { 238 $entries = dbFetchRow('SELECT `entPhysicalIndex`, `entPhysicalName` FROM `entPhysical` WHERE `device_id` = ? AND `entPhysicalContainedIn` = ? AND `entPhysicalClass` = ?', array($device['device_id'], $sensor_index, 'port')); 239 if (count($entries) > 1 && 240 preg_match('/(?<start>\D{2})(?<num>\d+(?:\/\d+)+).*Lane\s*(?<lane>\d+)/', $sensor_name, $matches)) // detect port numeric part and lane 241 { 242 // There is each Line associated with breakout port, mostly is QSFP+ 40G 243 // FortyGigE0/0/0/0-Tx Lane 1 Power -> 0/RP0-TenGigE0/0/0/0/1 244 // FortyGigE0/0/0/0-Tx Lane 2 Power -> 0/RP0-TenGigE0/0/0/0/2 245 $lane_num = $matches['start'] . $matches['num'] . '/' . $matches['lane']; // FortyGigE0/0/0/0-Tx Lane 1 -> gE0/0/0/0/1 246 foreach ($entries as $entry) 247 { 248 if (str_ends($entry['entPhysicalName'], $lane_num)) 249 { 250 $sensor_index = $entry['entPhysicalIndex']; 251 break; 252 } 253 } 254 255 } 256 elseif (is_numeric($entries[0]['entPhysicalIndex']) && $entries[0]['entPhysicalIndex'] > 0) 257 { 258 // Single multi-lane port association, ie 100G 259 $sensor_index = $entries[0]['entPhysicalIndex']; 260 } 261 } 262 } 263 // NOTE for self: entPhysicalParentRelPos >= 0 because on iosxr trouble 264 } while ($sensor_port['entPhysicalClass'] !== 'port' && $sensor_port['entPhysicalContainedIn'] > 0 && ($sensor_port['entPhysicalParentRelPos'] >= 0 || $device['os'] == 'arista_eos')); 265 266} 267 268// Get port array by ifIndex (using cache) 269// DOCME needs phpdoc block 270// TESTME needs unit testing 271function get_port_by_index_cache($device, $ifIndex, $deleted = 0) 272{ 273 global $cache; 274 275 if (is_array($device) && isset($device['device_id'])) 276 { 277 $device_id = $device['device_id']; 278 } 279 elseif (is_numeric($device)) 280 { 281 $device_id = $device; 282 } 283 if (!isset($device_id) || !is_numeric($ifIndex)) 284 { 285 print_error("Invalid arguments passed into function get_port_by_index_cache(). Please report to developers."); 286 } 287 288 if (isset($cache['port_index'][$device_id][$ifIndex]) && is_numeric($cache['port_index'][$device_id][$ifIndex])) 289 { 290 $id = $cache['port_index'][$device_id][$ifIndex]; 291 } else { 292 $deleted = $deleted ? 1 : 0; // Just convert boolean to 0 or 1 293 294 $id = dbFetchCell("SELECT `port_id` FROM `ports` WHERE `device_id` = ? AND `ifIndex` = ? AND `deleted` = ? LIMIT 1", array($device_id, $ifIndex, $deleted)); 295 if (!$deleted && is_numeric($id)) 296 { 297 // Cache port IDs (except deleted) 298 $cache['port_index'][$device_id][$ifIndex] = $id; 299 } 300 } 301 302 $port = get_port_by_id_cache($id); 303 if (is_array($port)) { return $port; } 304 305 return FALSE; 306} 307 308// Get port array by ifIndex 309// DOCME needs phpdoc block 310// TESTME needs unit testing 311function get_port_by_ifIndex($device_id, $ifIndex) 312{ 313 $port = dbFetchRow("SELECT * FROM `ports` WHERE `device_id` = ? AND `ifIndex` = ? LIMIT 1", array($device_id, $ifIndex)); 314 315 if (is_array($port)) 316 { 317 humanize_port($port); 318 return $port; 319 } 320 321 return FALSE; 322} 323 324// Get port ID by ifDescr (i.e. 'TenGigabitEthernet1/1') or ifName (i.e. 'Te1/1') 325// DOCME needs phpdoc block 326// TESTME needs unit testing 327function get_port_id_by_ifDescr($device_id, $ifDescr, $deleted = 0) 328{ 329 $port_id = dbFetchCell("SELECT `port_id` FROM `ports` WHERE `device_id` = ? AND (`ifDescr` = ? OR `ifName` = ?) AND `deleted` = ? LIMIT 1", array($device_id, $ifDescr, $ifDescr, $deleted)); 330 331 if (is_numeric($port_id)) 332 { 333 return $port_id; 334 } else { 335 return FALSE; 336 } 337} 338 339// Get port ID by ifAlias (interface description) 340// DOCME needs phpdoc block 341// TESTME needs unit testing 342function get_port_id_by_ifAlias($device_id, $ifAlias, $deleted = 0) 343{ 344 $port_id = dbFetchCell("SELECT `port_id` FROM `ports` WHERE `device_id` = ? AND `ifAlias` = ? AND `deleted` = ? LIMIT 1", array($device_id, $ifAlias, $deleted)); 345 346 if (is_numeric($port_id)) 347 { 348 return $port_id; 349 } else { 350 return FALSE; 351 } 352} 353 354// Get port ID by customer params (see http://www.observium.org/wiki/Interface_Description_Parsing) 355// DOCME needs phpdoc block 356// TESTME needs unit testing 357function get_port_id_by_customer($customer) 358{ 359 $where = ' WHERE 1'; 360 if (is_array($customer)) 361 { 362 foreach ($customer as $var => $value) 363 { 364 if ($value != '') 365 { 366 switch ($var) 367 { 368 case 'device': 369 case 'device_id': 370 $where .= generate_query_values($value, 'device_id'); 371 break; 372 case 'type': 373 case 'descr': 374 case 'circuit': 375 case 'speed': 376 case 'notes': 377 $where .= generate_query_values($value, 'port_descr_'.$var); 378 break; 379 } 380 } 381 } 382 } else { 383 return FALSE; 384 } 385 386 $query = 'SELECT `port_id` FROM `ports` ' . $where . ' ORDER BY `ifOperStatus` DESC'; 387 $ids = dbFetchColumn($query); 388 389 //print_vars($ids); 390 switch (count($ids)) 391 { 392 case 0: 393 return FALSE; 394 case 1: 395 return $ids[0]; 396 break; 397 default: 398 foreach ($ids as $port_id) 399 { 400 $port = get_port_by_id_cache($port_id); 401 $device = device_by_id_cache($port['device_id']); 402 if ($device['disabled'] || !$device['status']) 403 { 404 continue; // switch to next ID 405 } 406 break; 407 } 408 return $port_id; 409 } 410 return FALSE; 411} 412 413// Delete port from database and associated rrd files 414// DOCME needs phpdoc block 415// TESTME needs unit testing 416function delete_port($int_id, $delete_rrd = TRUE) 417{ 418 global $config; 419 420 $port = dbFetchRow("SELECT * FROM `ports` 421 LEFT JOIN `devices` USING (`device_id`) 422 WHERE `port_id` = ?", array($int_id)); 423 $ret = "> Deleted interface from ".$port['hostname'].": id=$int_id (".$port['ifDescr'].")\n"; 424 425 // Remove entities from common tables 426 $deleted_entities = array(); 427 foreach ($config['entity_tables'] as $table) 428 { 429 $where = '`entity_type` = ?' . generate_query_values($int_id, 'entity_id'); 430 $table_status = dbDelete($table, $where, array('port')); 431 if ($table_status) { $deleted_entities['port'] = 1; } 432 } 433 if (count($deleted_entities)) 434 { 435 $ret .= ' * Deleted common entity entries linked to port.' . PHP_EOL; 436 } 437 438 // FIXME, move to definitions 439 $port_tables = array('eigrp_ports', 'ipv4_addresses', 'ipv6_addresses', 440 'ip_mac', 'juniAtmVp', 'mac_accounting', 'ospf_nbrs', 'ospf_ports', 441 'ports_adsl', 'ports_cbqos', 'ports_vlans', 'pseudowires', 'vlans_fdb', 442 'neighbours', 'ports'); 443 $deleted_tables = []; 444 foreach ($port_tables as $table) 445 { 446 $table_status = dbDelete($table, "`port_id` = ?", array($int_id)); 447 if ($table_status) { $deleted_tables[] = $table; } 448 } 449 450 $table_status = dbDelete('ports_stack', "`port_id_high` = ? OR `port_id_low` = ?", array($int_id, $int_id)); 451 if ($table_status) { $deleted_tables[] = 'ports_stack'; } 452 $table_status = dbDelete('entity_permissions', "`entity_type` = 'port' AND `entity_id` = ?", array($int_id)); 453 if ($table_status) { $deleted_tables[] = 'entity_permissions'; } 454 $table_status = dbDelete('alert_table', "`entity_type` = 'port' AND `entity_id` = ?", array($int_id)); 455 if ($table_status) { $deleted_tables[] = 'alert_table'; } 456 $table_status = dbDelete('group_table', "`entity_type` = 'port' AND `entity_id` = ?", array($int_id)); 457 if ($table_status) { $deleted_tables[] = 'group_table'; } 458 459 $ret .= ' * Deleted interface entries from tables: '.implode(', ', $deleted_tables).PHP_EOL; 460 461 if ($delete_rrd) 462 { 463 $rrd_types = array('adsl', 'dot3', 'fdbcount', 'poe', NULL); 464 $deleted_rrds = []; 465 foreach ($rrd_types as $type) 466 { 467 $rrdfile = get_port_rrdfilename($port, $type, TRUE); 468 if (is_file($rrdfile)) 469 { 470 unlink($rrdfile); 471 $deleted_rrds[] = $rrdfile; 472 } 473 } 474 $ret .= ' * Deleted interface RRD files: ' . implode(', ', $deleted_rrds) . PHP_EOL; 475 } 476 477 return $ret; 478} 479 480// DOCME needs phpdoc block 481// TESTME needs unit testing 482function get_port_rrdindex($port) 483{ 484 global $config; 485 486 $device = device_by_id_cache($port['device_id']); 487 488 $device_identifier = strtolower($config['os'][$device['os']]['port_rrd_identifier']); 489 490 // default to ifIndex 491 $this_port_identifier = $port['ifIndex']; 492 493 if ($device_identifier == "ifname" && $port['ifName'] != "") 494 { 495 $this_port_identifier = strtolower(str_replace("/", "-", $port['ifName'])); 496 } 497 498 return $this_port_identifier; 499} 500 501// CLEANME DEPRECATED 502function get_port_rrdfilename($port, $suffix = NULL, $fullpath = FALSE) 503{ 504 $this_port_identifier = get_port_rrdindex($port); 505 506 if ($suffix == "") 507 { 508 $filename = "port-" . $this_port_identifier . ".rrd"; 509 } else { 510 $filename = "port-" . $this_port_identifier . "-" . $suffix . ".rrd"; 511 } 512 513 if ($fullpath) 514 { 515 $device = device_by_id_cache($port['device_id']); 516 $filename = get_rrd_path($device, $filename); 517 } 518 519 return $filename; 520} 521 522// EOF