1<?php
2
3/**
4 * Observium
5 *
6 *   This file is part of Observium.
7 *
8 * @package    observium
9 * @subpackage poller
10 * @copyright  (C) 2006-2013 Adam Armstrong, (C) 2013-2019 Observium Limited
11 *
12 */
13
14// Include description parser (usually ifAlias) if config option set
15$custom_port_parser = FALSE;
16if (isset($config['port_descr_parser']) && is_file($config['install_dir'] . "/" . $config['port_descr_parser']))
17{
18  include_once($config['install_dir'] . "/" . $config['port_descr_parser']);
19
20  if (function_exists('custom_port_parser'))
21  {
22    $custom_port_attribs = array('type', 'descr', 'circuit', 'speed', 'notes');
23    $custom_port_parser = TRUE;
24  } else {
25    print_warning("WARNING: Rewrite your custom ports parser in file [".$config['install_dir'] . "/" . $config['port_descr_parser']."], using a function custom_port_parser().");
26    $custom_port_parser = 'old';
27  }
28}
29
30// Cache ports table from DB
31$ports = array();
32
33// FIXME -- this stuff is a little messy, looping the array to make an array just seems wrong. :>
34//       -- i can make it a function, so that you don't know what it's doing.
35//       -- $ports = adamasMagicFunction($ports_db); ?
36
37$query  = 'SELECT * FROM `ports`';
38//$query .= ' LEFT JOIN `ports-state` USING(`port_id`)';
39$query .= ' WHERE `device_id` = ?';
40
41$ports_db = dbFetchRows($query, array($device['device_id']));
42$ports_attribs = get_device_entities_attribs($device['device_id'], 'port'); // Get all attribs
43//print_vars($ports_attribs);
44foreach ($ports_db as $port)
45{
46  if (isset($ports_attribs['port'][$port['port_id']]))
47  {
48    $port = array_merge($port, $ports_attribs['port'][$port['port_id']]);
49  }
50  $ports[$port['ifIndex']] = $port;
51}
52$ports_ignored_count_db = intval(get_entity_attrib('device', $device, 'ports_ignored_count')); // Cache last ports ignored count
53
54// Ports module options
55$ports_modules = array(); // Ports sub-modules enabled/disabled
56foreach (array_keys($config) as $ports_module)
57{
58  if (!str_starts($ports_module, 'enable_ports_')) { continue; } // Filter only enable_ports_* config entries
59  $ports_module = str_replace('enable_ports_', '', $ports_module);
60
61  $ports_modules[$ports_module] = isset($attribs['enable_ports_' . $ports_module]) ? (bool)$attribs['enable_ports_' . $ports_module] : $config['enable_ports_' . $ports_module];
62}
63// Additionally force enable separate walk feature for some device oses, but only if ports total count >10
64$ports_module = 'separate_walk';
65if (!$ports_modules[$ports_module] && $config['os'][$device['os']]['ports_'.$ports_module])
66{
67  if (isset($attribs['enable_ports_' . $ports_module]) && !$attribs['enable_ports_' . $ports_module]) {} // forcing disabled in device config
68  else
69  {
70    $ports_total_count = $ports_ignored_count_db + dbFetchCell('SELECT COUNT(*) FROM `ports` WHERE `device_id` = ? AND `deleted` = 0', array($device['device_id']));
71    $ports_modules[$ports_module] = $ports_total_count > 10;
72    if (OBS_DEBUG && $ports_modules[$ports_module])
73    {
74      print_debug('Forced ports separate walk feature!');
75    }
76  }
77}
78//print_vars($ports_modules);
79
80$table_rows = array();
81
82// Build SNMP Cache Array
83
84// IF-MIB OIDs that go into the ports table
85
86$data_oids_ifEntry = array(
87  // ifEntry
88  'ifDescr', 'ifType', 'ifMtu', 'ifSpeed', 'ifPhysAddress',
89  'ifAdminStatus', 'ifOperStatus', 'ifLastChange',
90);
91$data_oids_ifXEntry = array(
92  // ifXEntry
93  'ifName', 'ifAlias', 'ifHighSpeed', 'ifPromiscuousMode', 'ifConnectorPresent',
94);
95$data_oids = array_merge($data_oids_ifEntry,
96                         $data_oids_ifXEntry,
97                         array('ifDuplex', 'ifTrunk', 'ifVlan')); // Add additional oids
98
99// IF-MIB statistics OIDs that go into RRD
100
101$stat_oids_ifEntry = array(
102  // ifEntry
103  'ifInOctets',        'ifOutOctets',
104  'ifInUcastPkts',     'ifOutUcastPkts',
105  'ifInNUcastPkts',    'ifOutNUcastPkts', // Note, (In|Out)NUcastPkts deprecated, for HC counters use Broadcast+Multicast instead
106  'ifInDiscards',      'ifOutDiscards',
107  'ifInErrors',        'ifOutErrors',
108  'ifInUnknownProtos',
109);
110
111$stat_oids_ifXEntry = array(
112  // ifXEntry
113  'ifInMulticastPkts', 'ifOutMulticastPkts',
114  'ifInBroadcastPkts', 'ifOutBroadcastPkts',
115  // HC counters
116  'ifHCInOctets',        'ifHCOutOctets',
117  'ifHCInUcastPkts',     'ifHCOutUcastPkts',
118  'ifHCInMulticastPkts', 'ifHCOutMulticastPkts',
119  'ifHCInBroadcastPkts', 'ifHCOutBroadcastPkts',
120);
121
122// This oids definitions used only for Upstream/Downstream interface types
123$upstream_oids = array(
124  // ifEntry
125  'ifInOctets', 'ifInUcastPkts', 'ifInNUcastPkts', 'ifInDiscards', 'ifInErrors',
126  // ifXEntry
127  'ifInMulticastPkts', 'ifInBroadcastPkts', 'ifHCInOctets', 'ifHCInUcastPkts', 'ifHCInMulticastPkts', 'ifHCInBroadcastPkts',
128);
129$downstream_oids = array(
130  // ifEntry
131  'ifOutOctets', 'ifOutUcastPkts', 'ifOutNUcastPkts', 'ifOutDiscards', 'ifOutErrors',
132  // ifXEntry
133  'ifOutMulticastPkts', 'ifOutBroadcastPkts', 'ifHCOutOctets', 'ifHCOutUcastPkts', 'ifHCOutMulticastPkts', 'ifHCOutBroadcastPkts',
134);
135
136// Remove HC oids from stat arrays for SNMP v1
137if ($device['snmp_version'] == 'v1')
138{
139  $hc_oids = array(
140    // HC counters
141    'ifHCInOctets',        'ifHCOutOctets',
142    'ifHCInUcastPkts',     'ifHCOutUcastPkts',
143    'ifHCInMulticastPkts', 'ifHCOutMulticastPkts',
144    'ifHCInBroadcastPkts', 'ifHCOutBroadcastPkts',
145  );
146  $stat_oids_ifXEntry = array_diff($stat_oids_ifXEntry, $hc_oids);
147  $upstream_oids      = array_diff($upstream_oids,      $hc_oids);
148  $downstream_oids    = array_diff($downstream_oids,    $hc_oids);
149}
150
151// Merge stat oids
152$stat_oids = array_merge($stat_oids_ifEntry, $stat_oids_ifXEntry);
153
154// Cisco old locIf OIDs. Currently unused.
155
156$cisco_oids = array('locIfHardType', 'locIfInRunts', 'locIfInGiants', 'locIfInCRC', 'locIfInFrame', 'locIfInOverrun', 'locIfInIgnored', 'locIfInAbort',
157                    'locIfCollisions', 'locIfInputQueueDrops', 'locIfOutputQueueDrops');
158
159// PAgP OIDs
160
161//$pagp_oids = array('pagpOperationMode', 'pagpPortState', 'pagpPartnerDeviceId', 'pagpPartnerLearnMethod', 'pagpPartnerIfIndex', 'pagpPartnerGroupIfIndex',
162//                   'pagpPartnerDeviceName', 'pagpEthcOperationMode', 'pagpDeviceId', 'pagpGroupIfIndex');
163$pagp_oids = array(); // PAgP disabled since r7987, while not moved to new polling style
164
165//$ifmib_oids = array_merge($data_oids, $stat_oids);
166
167print_cli_data_field("Caching Oids");
168$port_stats = array();
169if (is_device_mib($device, "IF-MIB"))
170{
171  if (!$ports_modules['separate_walk'])
172  {
173    print_debug("Used full table ifEntry/ifXEntry snmpwalk.");
174    $ifmib_oids = ['ifEntry', 'ifXEntry'];
175    foreach ($ifmib_oids as $oid)
176    {
177      $has_name = 'has_' . $oid;
178      echo("$oid ");
179      $port_stats = snmpwalk_cache_oid($device, $oid, $port_stats, "IF-MIB");
180      $$has_name = snmp_status() || snmp_error_code() === 2; // $has_ifEntry, $has_ifXEntry
181      //print_vars($$has_name);
182      if ($oid == 'ifEntry')
183      {
184        // Store error_code, 1000 == not exist table, 2 and 3 - not complete request
185        $has_ifEntry_error_code = snmp_error_code();
186      }
187    }
188
189  }
190  else
191  {
192
193    print_debug("Used separate data tables snmpwalk and per port snmpget.");
194
195    $has_ifEntry = FALSE;
196    // Data fields
197    // ifDescr, ifAlias, ifName, ifType, ifOperStatus
198    foreach (['ifDescr', 'ifType', 'ifOperStatus'] as $oid)
199    {
200      echo("$oid ");
201      $port_stats = snmpwalk_cache_oid($device, $oid, $port_stats, "IF-MIB");
202      $has_ifEntry = $has_ifEntry || snmp_status();
203      $has_ifEntry_error_code = snmp_error_code();
204    }
205    $has_ifXEntry = FALSE;
206    foreach (['ifAlias', 'ifName', 'ifHighSpeed'] as $oid)
207    {
208      echo("$oid ");
209      $port_stats = snmpwalk_cache_oid($device, $oid, $port_stats, "IF-MIB");
210      $has_ifXEntry = $has_ifXEntry || $GLOBALS['snmp_status'];
211    }
212
213    // Per port snmpget
214    if ($port_stats)
215    {
216      // Collect oids for per port snmpget
217      if ($has_ifXEntry)
218      {
219        $port_oids = array_merge($stat_oids_ifXEntry, $stat_oids_ifEntry);
220        $port_oids = array_merge($port_oids, array_diff($data_oids_ifEntry, ['ifDescr', 'ifType', 'ifOperStatus']));
221        $port_oids = array_merge($port_oids, array_diff($data_oids_ifXEntry, ['ifAlias', 'ifName', 'ifHighSpeed']));
222      }
223      else
224      {
225        $port_oids = array_merge($stat_oids_ifEntry,
226                                 array_diff($data_oids_ifEntry, ['ifDescr', 'ifType', 'ifOperStatus']));
227      }
228
229      // Use snmpget for each (not ignored) port
230      // NOTE. This method reduce polling time when too many ports (>100)
231      echo(implode(' ', $port_oids) . ", ifIndex: ");
232      foreach ($port_stats as $ifIndex => $port)
233      {
234        $port_disabled = isset($ports[$ifIndex]['disabled']) && $ports[$ifIndex]['disabled']; // Port polling disabled from WUI
235        if (!$port_disabled && is_port_valid($port, $device))
236        {
237          echo("$ifIndex ");
238          $port_oid = implode(".$ifIndex ", $port_oids) . ".$ifIndex";
239          $port_stats = snmp_get_multi_oid($device, $port_oid, $port_stats, "IF-MIB");
240        }
241      }
242    }
243  }
244} else { // End IF-MIB permitted
245  // This part for devices who not have IF-MIB stats, but have own vendor tree with ports
246}
247echo PHP_EOL; // End IF-MIB section
248
249//////////
250foreach (get_device_mibs_permitted($device) as $mib)
251{
252  merge_private_mib($device, 'ports', $mib, $port_stats, NULL);
253}
254////////
255
256// Prevent mark ports as DELETED when ifEntry snmpwalk return not complete data!
257$allow_delete_ports = $has_ifEntry_error_code !== 2 && $has_ifEntry_error_code !== 3;
258
259// Store polled time exactly after walk IF-MIB, for more correct port speed calculate!
260$polled = time();
261
262// Device uptime and polled time (required for ifLastChange)
263if (isset($cache['devices']['uptime'][$device['device_id']]))
264{
265  $device_uptime = &$cache['devices']['uptime'][$device['device_id']];
266} else {
267  print_error("Device uptime not cached, ifLastChange will incorrect. Check polling system module.");
268}
269
270// Subset of IF-MIB OIDs that we put into the state table
271$stat_oids_db = array('ifInOctets', 'ifOutOctets', 'ifInErrors', 'ifOutErrors', 'ifInUcastPkts', 'ifOutUcastPkts', 'ifInNUcastPkts', 'ifOutNUcastPkts',
272                      'ifInBroadcastPkts', 'ifOutBroadcastPkts', 'ifInMulticastPkts', 'ifOutMulticastPkts', 'ifInDiscards', 'ifOutDiscards');
273
274// Subset of IF-MIB OIDs that we put into the ports table
275$data_oids_db = array_diff($data_oids, array('ifLastChange')); // remove ifLastChange, because it added separate
276$data_oids_db = array_merge($data_oids_db, array('port_label', 'port_label_short', 'port_label_base', 'port_label_num'));
277
278// Additional MIBS and modules
279$process_port_functions = array('label' => TRUE); // collect processing functions
280$process_port_db        = array();                // collect processing db fields
281
282// Additionaly include per MIB functions and snmpwalks (uses include_once)
283$port_stats_count = count($port_stats);
284$include_lib = TRUE;
285$include_dir = "includes/polling/ports/";
286include("includes/include-dir-mib.inc.php");
287
288if (count($port_stats))
289{
290  // FIXME This probably needs re-enabled. We need to clear these things when they get unset, too.
291  #foreach ($cisco_oids as $oid)     { $port_stats = snmpwalk_cache_oid($device, $oid, $port_stats, "OLD-CISCO-INTERFACES-MIB"); }
292
293  // If the device is cisco, pull a few cisco-specific MIBs and try to get vlan data from CISCO-VTP-MIB
294  if ($device['os_group'] == "cisco")
295  {
296    //FIXME. All PAGP operations should be moved to separate "stacks" module and separate table (not in ports table)
297    /* PAgP disabled since r7987, while not moved to new polling style
298    foreach ($pagp_oids as $oid)
299    {
300      $port_stats = snmpwalk_cache_oid($device, $oid, $port_stats, "CISCO-PAGP-MIB");
301      // Break if no PAGP tables on device
302      if ($oid == 'pagpOperationMode' && $GLOBALS['snmp_status'] === FALSE) { break; }
303    }
304    */
305  }
306
307  // End Building SNMP Cache Array
308
309  $graphs['bits'] = TRUE; // Create global device_bits graph, since we have ports.
310
311  print_debug_vars($port_stats);
312}
313
314// New interface detection
315$ports_ignored_count = 0;   // Counting ignored ports
316$ports_db_deleted = array(); // MultiUpdate deleted ports db
317$ports_db_state   = array(); // MultiUpdate state ports db
318foreach ($port_stats as $ifIndex => $port)
319{
320  // Always use ifIndex from index part (not from Oid!),
321  // Oid can have incorrect ifIndex, ie:
322  // ifIndex.0 = 1
323  $port['ifIndex'] = $ifIndex;
324
325  if (is_port_valid($port, $device))
326  {
327    if (!is_array($ports[$port['ifIndex']]))
328    {
329      $port_insert = array('device_id' => $device['device_id'],
330                           'ifIndex'  => $ifIndex,
331                           'ignore'   => isset($port['ignore']) ? $port['ignore'] : '0',
332                           'disabled' => isset($port['disabled']) ? $port['disabled'] : '0');
333      $port_id = dbInsert(array('device_id' => $device['device_id'], 'ifIndex' => $ifIndex), 'ports');
334      $ports[$port['ifIndex']] = dbFetchRow("SELECT * FROM `ports` WHERE `port_id` = ?", array($port_id));
335      print_message("Adding: ".$port['ifDescr']."(".$ifIndex.")(".$ports[$port['ifIndex']]['port_id'].")");
336    }
337    else if ($ports[$ifIndex]['deleted'] == "1")
338    {
339      $ports_db_deleted[] = array('port_id' => $ports[$ifIndex]['port_id'], 'ifIndex' => $ifIndex, 'device_id' => $device['device_id'],  // UNIQUE fields
340                                  'deleted' => '0', 'ifLastChange' => date('Y-m-d H:i:s', $polled)); // Update this fields
341      //dbUpdate(array('deleted' => '0'), 'ports', '`port_id` = ?', array($ports[$ifIndex]['port_id']));
342      log_event("Interface DELETED mark removed", $device, 'port', $ports[$ifIndex]);
343      $ports[$ifIndex]['deleted'] = "0";
344    }
345  } else {
346    if (isset($ports[$port['ifIndex']]) && $ports[$port['ifIndex']]['deleted'] != '1' && $allow_delete_ports)
347    {
348      $ports_db_deleted[] = array('port_id' => $ports[$ifIndex]['port_id'], 'ifIndex' => $ifIndex, 'device_id' => $device['device_id'],  // UNIQUE fields
349                                  'deleted' => '1', 'ifLastChange' => date('Y-m-d H:i:s', $polled)); // Update this fields
350      //dbUpdate(array('deleted' => '1', 'ifLastChange' => date('Y-m-d H:i:s', $polled)), 'ports', '`port_id` = ?', array($ports[$ifIndex]['port_id']));
351      log_event("Interface was marked as DELETED", $device, 'port', $ports[$ifIndex]);
352      $ports[$ifIndex]['deleted'] = "1";
353    }
354    $ports_ignored_count++; // Counting ignored ports
355  }
356}
357
358if (!$allow_delete_ports)
359{
360  log_event("WARNING! Ports snmpwalk did not complete. Try to increase SNMP timeout on the device properties page.", $device, 'device', $device['device_id'], 7);
361}
362if ($ports_ignored_count !== $ports_ignored_count_db)
363{
364  set_entity_attrib('device', $device, 'ports_ignored_count', $ports_ignored_count);
365}
366// End New interface detection
367
368echo(PHP_EOL . PHP_EOL);
369
370// Loop ports in the DB and update where necessary
371foreach ($ports as $port)
372{
373  // Notes:
374  // $port_stats - array of ports from snmpwalks
375  // $this_port  - link to port array from snmpwalk
376  // $ports      - array of ports based on current db entries
377  // $port       - current port array from db
378  if ($port['deleted']) { continue; } // Skip updating RRDs and DB if interface marked as DELETED (also skipped bad_if's)
379
380  if ($port_stats[$port['ifIndex']] && $port['disabled'] != "1")
381  { // Check to make sure Port data is cached.
382    $this_port = &$port_stats[$port['ifIndex']];
383
384    $polled_period = $polled - $port['poll_time'];
385
386    $port['update'] = array();
387    $port['state']  = array(); // State field for update
388
389    $port['state']['poll_time'] = $polled;
390    $port['state']['poll_period'] = $polled_period;
391
392    $this_port['port_id'] = $port['port_id'];
393    $this_port['ifIndex'] = $port['ifIndex'];
394    $this_port_indexes    = array('port_id' => $ports[$port['ifIndex']]['port_id'], 'ifIndex' => $port['ifIndex'], 'device_id' => $device['device_id']); // UNIQUE port indexes
395
396    // Store original port walked OIDs for debugging later
397    if ($config['debug_port']['spikes'] || $config['debug_port'][$port['port_id']])
398    {
399      $debug_port = $this_port; // DEBUG
400    }
401
402    // Before process_port_label()
403    // Fix ord (UTF-8) chars, ie:
404    // ifAlias.3 = Conexi<F3>n de <E1>rea local* 3
405    foreach (array('ifAlias', 'ifDescr', 'ifName') as $oid_fix)
406    {
407      if (isset($this_port[$oid_fix])) { $this_port[$oid_fix] = snmp_fix_string($this_port[$oid_fix]); }
408      /// DEVEL, test escaping
409      if ($this_port['port_id'] == 42430)
410      {
411        $this_port[$oid_fix] .= ' <>';
412      }
413    }
414
415    //print_vars($process_port_functions);
416    foreach ($process_port_functions as $func => $ok)
417    {
418      if ($ok && function_exists('process_port_' . $func))
419      {
420        if (OBS_DEBUG > 1)
421        {
422          print_debug("Processing port ifIndex ".$this_port['ifIndex']." with function process_port_{$func}() ");
423        }
424        // Note, used call by array, because parameters for call_user_func() are not passed by reference
425        call_user_func_array('process_port_' . $func, array(&$this_port, $device, $port));
426      }
427    }
428    //print_vars($this_port);
429
430#    // Copy ifHC[In|Out] values to non-HC if they exist
431#    // Check if they're greater than zero to work around stupid devices which expose HC counters, but don't populate them. HERPDERP. - adama
432#    if ($device['os'] == "netapp") { $hc_prefixes = array('HC', '64'); } else { $hc_prefixes = array('HC'); }
433#    foreach ($hc_prefixes as $hc_prefix)
434#    {
435#      foreach (array('Octets', 'UcastPkts', 'BroadcastPkts', 'MulticastPkts') as $hc)
436#      {
437#        $hcin = 'if'.$hc_prefix.'In'.$hc;
438#        $hcout = 'if'.$hc_prefix.'Out'.$hc;
439#        if (is_numeric($this_port[$hcin]) && $this_port[$hcin] > 0 && is_numeric($this_port[$hcout]) && $this_port[$hcout] > 0)
440#        {
441#          // echo(" ".$hc_prefix." $hc, ");
442#          $this_port['ifIn'.$hc]  = $this_port[$hcin];
443#          $this_port['ifOut'.$hc] = $this_port[$hcout];
444#        }
445#      }
446#    }
447
448    // Here special checks for Upstream/Downstream ports, because it have only In or only Out counters
449    if (strpos($this_port['ifType'], 'Upstream') !== FALSE)
450    {
451      // Upstream has only In counters
452      foreach ($upstream_oids as $oid_in)
453      {
454        $oid_out = str_replace('In', 'Out', $oid_in);
455        if (is_numeric($this_port[$oid_in]) && !is_numeric($this_port[$oid_out]))
456        {
457          $this_port[$oid_out] = 0; // Set it all to zero
458        }
459      }
460    }
461    else if (strpos($this_port['ifType'], 'Downstream') !== FALSE)
462    {
463      // Downstream has only Out counters
464      foreach ($downstream_oids as $oid_out)
465      {
466        $oid_in = str_replace('Out', 'In', $oid_out);
467        if (is_numeric($this_port[$oid_out]) && !is_numeric($this_port[$oid_in]))
468        {
469          $this_port[$oid_in] = 0; // Set it all to zero
470        }
471      }
472    }
473
474    // If we're not using SNMPv1, assumt there are 64-bit values and overwrite the 32-bit OIDs.
475    if ($device['snmp_version'] != 'v1')
476    {
477      $hc_prefix = 'HC';
478      $port_has_64bit = is_numeric($this_port['if'.$hc_prefix.'InOctets']) && is_numeric($this_port['if'.$hc_prefix.'OutOctets']);
479
480      // We've never tested for 64bit. Lets do it now. Lots of devices seem to not support 64bit counters for all ports.
481      if ($port['port_64bit'] == NULL)
482      {
483        // We have 64-bit traffic counters. Lets set port_64bit
484        if ($port_has_64bit)
485        {
486          $port['port_64bit'] = 1;
487          $port['update']['port_64bit'] = 1;
488        } else {
489          $port['port_64bit'] = 0;
490          $port['update']['port_64bit'] = 0;
491        }
492      }
493      else if ($has_ifXEntry && $port_has_64bit && !$port['port_64bit'])
494      {
495        // Port changed to 64-bit
496        $port['port_64bit'] = 1;
497        $port['update']['port_64bit'] = 1;
498        log_event('Interface changed: [HC] -> Counter64 (may cause disposable spike)', $device, 'port', $port);
499      }
500
501      $port_has_mcbc = is_numeric($this_port['ifInBroadcastPkts']) && is_numeric($this_port['ifOutBroadcastPkts']) &&
502                       is_numeric($this_port['ifInMulticastPkts']) && is_numeric($this_port['ifOutMulticastPkts']);
503
504      if ($port['port_mcbc'] == NULL)
505      {
506        // We have Broadcast/Multicast traffic counters. Lets set port_mcbc
507        if ($port_has_mcbc)
508        {
509          $port['port_mcbc'] = 1;
510          $port['update']['port_mcbc'] = 1;
511        } else {
512          $port['port_mcbc'] = 0;
513          $port['update']['port_mcbc'] = 0;
514        }
515      }
516      else if ($has_ifXEntry && $port_has_mcbc && !$port['port_mcbc'])
517      {
518        // Port acquired multicast/broadcast!
519        $port['port_mcbc'] = 1;
520        $port['update']['port_mcbc'] = 1;
521        log_event('Interface changed: Separated Multicast/Broadcast statistics appeared.', $device, 'port', $port);
522      }
523
524      //else if (!$port_has_64bit && $port['port_64bit'])
525      //{
526      //  // Port changed to 32-bit
527      //  $port['port_64bit'] = 0;
528      //  $port['update']['port_64bit'] = 0;
529      //  log_event('Interface changed: [HC] -> Counter32', $device, 'port', $port);
530      //}
531
532      if ($port['port_64bit'])
533      {
534        print_debug("64-bit, ");
535        foreach (array('Octets', 'UcastPkts', 'BroadcastPkts', 'MulticastPkts') as $hc)
536        {
537          $hcin  = 'if'.$hc_prefix.'In'.$hc;
538          $hcout = 'if'.$hc_prefix.'Out'.$hc;
539          $this_port['ifIn'.$hc]  = $this_port[$hcin];
540          $this_port['ifOut'.$hc] = $this_port[$hcout];
541        }
542        // Additionally override (In|Out)NUcastPkts
543        // see: http://jira.observium.org/browse/OBSERVIUM-1749
544        $this_port['ifInNUcastPkts']  = int_add($this_port['ifInBroadcastPkts'],  $this_port['ifInMulticastPkts']);
545        $this_port['ifOutNUcastPkts'] = int_add($this_port['ifOutBroadcastPkts'], $this_port['ifOutMulticastPkts']);
546      }
547    }
548
549    // rewrite the ifPhysAddress
550    // IF-MIB::ifPhysAddress.2 = STRING: 66:c:9b:1b:62:7e
551    // IF-MIB::ifPhysAddress.2 = Hex-STRING: 00 02 99 09 E9 84
552    $this_port['ifPhysAddress'] = mac_zeropad($this_port['ifPhysAddress']);
553
554    // ifSpeed processing
555    if (isset($port['ifSpeed_custom']) && $port['ifSpeed_custom'] > 0)
556    {
557      // Custom ifSpeed from WebUI
558      $this_port['ifSpeed'] = intval($port['ifSpeed_custom']);
559      print_debug('Port ifSpeed manually set.');
560    } else {
561      // Detect port speed by ifHighSpeed
562      if (is_numeric($this_port['ifHighSpeed']))
563      {
564        // Use old ifHighSpeed if current speed '0', seems as some error on device
565        if ($this_port['ifHighSpeed'] == '0' && $port['ifHighSpeed'] > '0')
566        {
567          $this_port['ifHighSpeed'] = $port['ifHighSpeed'];
568          print_debug('Port ifHighSpeed fixed from zero.');
569        }
570
571        // Maximum possible ifSpeed value is 4294967295
572        // Overwrite ifSpeed with ifHighSpeed if it's over 4G or ifSpeed equals to zero
573        // ifSpeed is more accurate for low speeds (ie: ifSpeed.60 = 1536000, ifHighSpeed.60 = 2)
574        // other case when (incorrect ifSpeed): ifSpeed.6 = 1000, ifHighSpeed.6 = 1000)
575        $ifSpeed_max = max($this_port['ifHighSpeed'] * 1000000, $this_port['ifSpeed']);
576        if ($this_port['ifHighSpeed'] > 0 &&
577            ($ifSpeed_max > 4000000000 || $this_port['ifSpeed'] == 0 || $this_port['ifSpeed'] == $this_port['ifHighSpeed']))
578        {
579          // echo("HighSpeed, ");
580          $this_port['ifSpeed'] = $ifSpeed_max;
581        }
582      }
583      if ($this_port['ifSpeed'] == '0' && $port['ifSpeed'] > '0')
584      {
585        // Use old ifSpeed if current speed '0', seems as some error on device
586        $this_port['ifSpeed'] = $port['ifSpeed'];
587        print_debug('Port ifSpeed fixed from zero.');
588      }
589    }
590
591    // Simple override of ifAlias -- mostly for testing
592    if(isset($config['ports']['ifAlias_map']['ifIndex'][$device['hostname']][$port['ifIndex']]))
593    {
594      $this_port['ifAlias'] = $config['ports']['ifAlias_map']['ifIndex'][$device['hostname']][$port['ifIndex']];
595    }
596    if(isset($config['ports']['ifAlias_map']['ifName'][$device['hostname']][$port['ifName']]))
597    {
598      $this_port['ifAlias'] = $config['ports']['ifAlias_map']['ifName'][$device['hostname']][$port['ifName']];
599    }
600
601
602    // Update TrustSec
603    if ($this_port['encrypted'])
604    {
605      if ($port['encrypted'] === '0')
606      {
607        log_event("Interface is now encrypted", $device, 'port', $port);
608        $port['update']['encrypted'] = '1';
609      }
610    }
611    else if ($port['encrypted'] === '1')
612    {
613        log_event("Interface is no longer encrypted", $device, 'port', $port);
614        $port['update']['encrypted'] = '0';
615    }
616
617    // Make sure ifOperStatus is valid (FIXME. not exist statuses already "filtered" in is_port_valid())
618    if (isset($this_port['ifOperStatus']) &&
619        !in_array($this_port['ifOperStatus'], array('testing', 'notPresent', 'dormant', 'down', 'lowerLayerDown', 'unknown', 'up', 'monitoring')))
620    {
621      $this_port['ifOperStatus'] = 'unknown';
622    }
623
624    if (isset($this_port['ifAdminStatus']) &&
625       !in_array($this_port['ifAdminStatus'], array('up', 'down', 'testing')))
626    {
627      $this_port['ifAdminStatus'] = ''; // or NULL?
628    }
629
630    if (isset($this_port['ifConnectorPresent']) &&
631       !in_array($this_port['ifConnectorPresent'], array('true', 'false')))
632    {
633      $this_port['ifConnectorPresent'] = NULL;
634    }
635
636    // Update IF-MIB data
637
638    $log_event = array();
639    foreach ($data_oids_db as $oid)
640    {
641      if ($port[$oid] != $this_port[$oid])
642      {
643        if (isset($this_port[$oid]))
644        {
645          $port['update'][$oid] = $this_port[$oid];
646          $msg = "[$oid] '" . $port[$oid] . "' -> '" . $this_port[$oid] . "'";
647        } else {
648          $port['update'][$oid] = array('NULL');
649          $msg = "[$oid] '" . $port[$oid] . "' -> NULL";
650        }
651        if ($oid == 'ifOperStatus' && ($port[$oid] == 'up' || $port[$oid] == 'down') && isset($this_port[$oid]))
652        {
653          // Specific log_event for port Up/Down
654          log_event('Interface '.ucfirst($this_port[$oid]) . ": [$oid] '" . $port[$oid] . "' -> '" . $this_port[$oid] . "'", $device, 'port', $port, 'warning');
655        } else {
656          $log_event[] = $msg;
657        }
658        if (OBS_DEBUG) { echo($msg." "); } // else { echo($oid . " "); }
659      }
660    }
661
662    // ifLastChange
663    if (isset($this_port['ifLastChange']) && $this_port['ifLastChange'] != '')
664    {
665      // Convert ifLastChange from timetick to timestamp
666      /**
667       * The value of sysUpTime at the time the interface entered
668       * its current operational state. If the current state was
669       * entered prior to the last re-initialization of the local
670       * network management subsystem, then this object contains a
671       * zero value.
672       *
673       * NOTE, observium uses last change timestamp.
674       */
675      $if_lastchange_uptime = timeticks_to_sec($this_port['ifLastChange']);
676      if (($device_uptime['sysUpTime'] - $if_lastchange_uptime) > 90)
677      {
678        $if_lastchange = $device_uptime['polled'] - $device_uptime['sysUpTime'] + $if_lastchange_uptime;
679        print_debug('IFLASTCHANGE = '.$device_uptime['polled'].'s - '.$device_uptime['sysUpTime'].'s + '.$if_lastchange_uptime.'s');
680        if (abs($if_lastchange - strtotime($port['ifLastChange'])) > 90)
681        {
682          // Compare lastchange with previous, update only if more than 60 sec (for exclude random dispersion)
683          $port['update']['ifLastChange'] = date('Y-m-d H:i:s', $if_lastchange); // Convert to timestamp
684        }
685      } else {
686        // Device sysUpTime more than if uptime or too small difference.. impossible, seems as bug on device
687        $if_lastchange_uptime = FALSE;
688      }
689    } else {
690      // ifLastChange not exist
691      $if_lastchange_uptime = FALSE;
692    }
693
694    if ($if_lastchange_uptime === FALSE)
695    {
696      if (empty($port['ifLastChange']) || $port['ifLastChange'] == '0000-00-00 00:00:00' || // Newer set (first time)
697          isset($port['update']['ifOperStatus']) || isset($port['update']['ifAdminStatus']) || isset($port['update']['ifSpeed']) || isset($port['update']['ifDuplex']))
698      {
699        $port['update']['ifLastChange'] = date('Y-m-d H:i:s', $polled);
700      }
701      print_debug("IFLASTCHANGE unknown/false, used system times.");
702    }
703    if (isset($port['update']['ifLastChange']))
704    {
705      print_debug("IFLASTCHANGE (" . $port['ifIndex'] . "): " . $port['update']['ifLastChange']);
706      if ($port['ifLastChange'] && $port['ifLastChange'] != '0000-00-00 00:00:00' && count($log_event))
707      {
708        $log_event[] = "[ifLastChange] '" . $port['ifLastChange'] . "' -> '" . $port['update']['ifLastChange'] . "'";
709      }
710    }
711    if ((bool)$log_event) { log_event('Interface changed: ' . implode('; ', $log_event), $device, 'port', $port); }
712
713    // Parse description (usually ifAlias) if config option set
714    if ($custom_port_parser)
715    {
716      $log_event = array();
717      if ($custom_port_parser !== 'old')
718      {
719        $port_ifAlias = custom_port_parser($this_port);
720      } else {
721        $custom_port_attribs = array('type', 'descr', 'circuit', 'speed', 'notes');
722
723        include($config['install_dir'] . "/" . $config['port_descr_parser']);
724      }
725
726      foreach ($custom_port_attribs as $attrib)
727      {
728        $attrib_key = "port_descr_".$attrib;
729        if ($port_ifAlias[$attrib] != $port[$attrib_key])
730        {
731          if (isset($port_ifAlias[$attrib]))
732          {
733            $port['update'][$attrib_key] = $port_ifAlias[$attrib];
734            $msg = "[$attrib] " . $port[$attrib_key] . " -> " . $port_ifAlias[$attrib];
735          } else {
736            $port['update'][$attrib_key] = array('NULL');
737            $msg = "[$attrib] " . $port[$attrib_key] . " -> NULL";
738          }
739          $log_event[] = $msg;
740        }
741      }
742      if ((bool)$log_event) { log_event('Interface changed (attrib): ' . implode('; ', $log_event), $device, 'port', $port); }
743    }
744    // End parse ifAlias
745
746    // Update IF-MIB metrics
747    foreach ($stat_oids_db as $oid)
748    {
749      $port['state'][$oid] = $this_port[$oid];
750      if (isset($port[$oid]) && is_numeric($port[$oid]))
751      {
752        //$oid_diff = $this_port[$oid] - $port[$oid];
753        $oid_diff = int_sub($this_port[$oid], $port[$oid]); // Use accurate substract
754        $oid_rate  = $oid_diff / $polled_period;
755        if ($oid_rate < 0)
756        {
757          print_warning("Negative $oid. Possible spike on next poll!");
758          $port['stats'][$oid.'_negative_rate'] = $oid_rate;
759          $oid_rate = "0";
760        }
761        $port['stats'][$oid.'_rate'] = $oid_rate;
762
763
764        // Perhaps need to protect these from false polls.
765        $port['alert_array'][$oid.'_rate']  = $oid_rate;
766        $port['alert_array'][$oid.'_delta'] = $oid_diff;
767
768        $port['stats'][$oid.'_diff'] = $oid_diff;
769        $port['state'][$oid.'_rate'] = $oid_rate;
770
771        // Record delta in database only for In/Out errors.
772        if ($oid == "ifInErrors" || $oid == "ifOutErrors")
773        {
774          $port['state'][$oid.'_delta'] = $oid_diff;
775        }
776
777        print_debug("\n $oid ($oid_diff B) $oid_rate Bps $polled_period secs");
778      } else {
779        // Add zero defaults for correct multiupdate!
780        $port['state'][$oid] = $port[$oid]; // Keep old value
781        $port['state'][$oid.'_rate'] = '0';
782
783        // Record delta in database only for In/Out errors.
784        if ($oid == "ifInErrors" || $oid == "ifOutErrors")
785        {
786          $port['state'][$oid.'_delta'] = '0';
787        }
788      }
789
790    }
791
792    foreach ($stat_oids as $oid)
793    {
794      // Update StatsD/Carbon
795      if ($config['statsd']['enable'] == TRUE && !strpos($oid, "HC") && is_numeric($this_port[$oid]))
796      {
797        StatsD::gauge(str_replace(".", "_", $device['hostname']).'.'.'port'.'.'.$port['ifIndex'].'.'.$oid, $this_port[$oid]);
798      }
799    }
800
801    $port['stats']['ifInBits_rate']  = round($port['stats']['ifInOctets_rate']  * 8);
802    $port['stats']['ifOutBits_rate'] = round($port['stats']['ifOutOctets_rate'] * 8);
803
804    $port['alert_array']['ifInBits_rate'] =  $port['stats']['ifInBits_rate'];
805    $port['alert_array']['ifOutBits_rate'] =  $port['stats']['ifOutBits_rate'];
806
807    // If we have been told to debug this port, output the counters we collected earlier, with the rates stuck on the end.
808    if ($config['debug_port'][$port['port_id']])
809    {
810      print_debug("Wrote port debugging data");
811      $debug_file   = "/tmp/port_debug_".$port['port_id'].".txt";
812      //FIXME. I think formatted debug out (as for spikes) more informative, but output here more parsable as CSV
813      $port_msg  = $port['port_id']."|".$polled."|".$polled_period."|".$debug_port['ifInOctets']."|".$debug_port['ifOutOctets']."|".$debug_port['ifHCInOctets']."|".$debug_port['ifHCOutOctets'];
814      $port_msg .= "|".formatRates($port['stats']['ifInOctets_rate'])."|".formatRates($port['stats']['ifOutOctets_rate'])."|".$device['snmp_version']."\n";
815      file_put_contents($debug_file, $port_msg, FILE_APPEND);
816    }
817
818    // If we see a spike above ifSpeed or negative rate, output it to /tmp/port_debug_spikes.txt
819    // Example how to read usefull info from this debug by grep:
820    // grep -B 1 -A 6 'ID:\ 520' /tmp/port_debug_spikes.txt
821    if ($config['debug_port']['spikes'] && $this_port['ifSpeed'] > "0" &&
822        ($port['stats']['ifInBits_rate'] > $this_port['ifSpeed'] || $port['stats']['ifOutBits_rate'] > $this_port['ifSpeed'] ||
823         isset($port['stats']['ifInOctets_negative_rate'])       || isset($port['stats']['ifOutOctets_negative_rate'])))
824    {
825      if (!$port['port_64bit']) { $hc_prefix = ''; }
826      print_warning("Spike above ifSpeed or negative rate detected! See debug info here: ");
827      $debug_file   = "/tmp/port_debug_spikes.txt";
828      $debug_format = "| %20s | %20s | %20s |\n";
829      $debug_msg  = sprintf("+%'-68s+\n", '');
830      $debug_msg .= sprintf("|%67s |\n", $device['hostname']." ".$debug_port['ifDescr']." (ID: ".$port['port_id'].") ".formatRates($debug_port['ifSpeed'])." ".($port['port_64bit'] ? 'Counter64' : 'Counter32'));
831      $debug_msg .= sprintf("+%'-68s+\n", '');
832      $debug_msg .= sprintf("| %-20s | %-20s | %-20s |\n", 'Polled time', 'if'.$hc_prefix.'OutOctets', 'if'.$hc_prefix.'InOctets');
833      $debug_msg .= sprintf($debug_format, '(prev) '.$port['poll_time'], $port['ifOutOctets'], $port['ifInOctets']);
834      $debug_msg .= sprintf($debug_format, '(now)  '.$polled, $this_port['ifOutOctets'], $this_port['ifInOctets']);
835      $debug_msg .= sprintf($debug_format, format_unixtime($polled), formatRates($port['stats']['ifOutBits_rate']*8), formatRates($port['stats']['ifInBits_rate']));
836      $debug_msg .= sprintf("%'+70s\n", '');
837      $debug_msg .= sprintf("| %-67s|\n", 'Port dump:');
838      // Added full original port variable dump
839      foreach ($debug_port as $debug_key => $debug_var)
840      {
841        $debug_msg .= sprintf("|  %-66s|\n", "'$debug_key' => '$debug_var',");
842      }
843      $debug_msg .= sprintf("+%'-68s+\n\n", '');
844      file_put_contents($debug_file, $debug_msg, FILE_APPEND);
845    }
846
847    // Put States into alert array
848    foreach (array('ifOperStatus', 'ifAdminStatus', 'ifMtu', 'ifDuplex', 'ifVlan') as $oid)
849    {
850      if (isset($this_port[$oid]))
851      {
852        $port['alert_array'][$oid] = $this_port[$oid];
853      }
854    }
855
856    // If we have a valid ifSpeed we should populate the percentage stats for checking.
857    if (is_numeric($this_port['ifSpeed']))
858    {
859      $port['stats']['ifInBits_perc'] = round($port['stats']['ifInBits_rate'] / $this_port['ifSpeed'] * 100);
860      $port['stats']['ifOutBits_perc'] = round($port['stats']['ifOutBits_rate'] / $this_port['ifSpeed'] * 100);
861      $port['alert_array']['ifSpeed'] = $this_port['ifSpeed'];
862    }
863
864    if (is_numeric($this_port['ifHighSpeed']))
865    {
866      $port['alert_array']['ifHighSpeed'] = $this_port['ifHighSpeed'];
867    }
868
869    $port['state']['ifInOctets_perc'] = $port['stats']['ifInBits_perc'];
870    $port['state']['ifOutOctets_perc'] = $port['stats']['ifOutBits_perc'];
871
872    $port['alert_array']['ifInOctets_perc'] = $port['stats']['ifInBits_perc'];
873    $port['alert_array']['ifOutOctets_perc'] = $port['stats']['ifOutBits_perc'];
874
875    $port['alert_array']['rx_ave_pktsize']   = $port['state']['ifInOctets_delta'] / ($port['state']['ifInUcastPkts_delta'] + $port['state']['ifInNUcastPkts_delta']);
876    $port['alert_array']['tx_ave_pktsize']   = $port['state']['ifOutOctets_delta'] / ($port['state']['ifOutUcastPkts_delta'] + $port['state']['ifOutNUcastPkts_delta']);
877
878    // Store aggregate in/out state
879    $port['state']['ifOctets_rate']    = $port['stats']['ifOutOctets_rate']    + $port['stats']['ifInOctets_rate'];
880    $port['state']['ifUcastPkts_rate'] = $port['stats']['ifOutUcastPkts_rate'] + $port['stats']['ifInUcastPkts_rate'];
881    $port['state']['ifErrors_rate']    = $port['stats']['ifOutErrors_rate']    + $port['stats']['ifInErrors_rate'];
882    $port['state']['ifDiscards_rate']  = $port['stats']['ifOutDiscards_rate']  + $port['stats']['ifInDiscards_rate'];
883
884    // Send aggregate data to alerter too
885    $port['alert_array']['ifOctets_rate']        = $port['state']['ifOctets_rate'];
886    $port['alert_array']['ifUcastPkts_rate']     = $port['state']['ifUcastPkts_rate'];
887    $port['alert_array']['ifNUcastPkts_rate']    = $port['stats']['ifOutNUcastPkts_rate'] + $port['stats']['ifInNUcastPkts_rate'];
888    $port['alert_array']['ifErrors_rate']        = $port['state']['ifErrors_rate'];
889    $port['alert_array']['ifBroadcastPkts_rate'] = $port['stats']['ifOutBroadcastPkts_rate'] + $port['stats']['ifInBroadcastPkts_rate'];
890    $port['alert_array']['ifMulticastPkts_rate'] = $port['stats']['ifOutMulticastPkts_rate'] + $port['stats']['ifInMulticastPkts_rate'];
891    $port['alert_array']['ifDiscards_rate']      = $port['stats']['ifOutDiscards_rate'] + $port['stats']['ifInDiscards_rate'];
892
893    // Update RRDs
894    rrdtool_update_ng($device, 'port', array(
895      'INOCTETS'         => $this_port['ifInOctets'],
896      'OUTOCTETS'        => $this_port['ifOutOctets'],
897      'INERRORS'         => $this_port['ifInErrors'],
898      'OUTERRORS'        => $this_port['ifOutErrors'],
899      'INUCASTPKTS'      => $this_port['ifInUcastPkts'],
900      'OUTUCASTPKTS'     => $this_port['ifOutUcastPkts'],
901      'INNUCASTPKTS'     => $this_port['ifInNUcastPkts'],
902      'OUTNUCASTPKTS'    => $this_port['ifOutNUcastPkts'],
903      'INDISCARDS'       => $this_port['ifInDiscards'],
904      'OUTDISCARDS'      => $this_port['ifOutDiscards'],
905      'INUNKNOWNPROTOS'  => $this_port['ifInUnknownProtos'],
906      'INBROADCASTPKTS'  => $this_port['ifInBroadcastPkts'],
907      'OUTBROADCASTPKTS' => $this_port['ifOutBroadcastPkts'],
908      'INMULTICASTPKTS'  => $this_port['ifInMulticastPkts'],
909      'OUTMULTICASTPKTS' => $this_port['ifOutMulticastPkts'],
910    ), get_port_rrdindex($port), TRUE, ['speed' => $this_port['ifSpeed']]);
911
912    // End Update IF-MIB
913
914    // Update additional MIBS and modules
915    foreach ($process_port_db as $port_module => $oids)
916    {
917      $log_event = array();
918      foreach ($oids as $oid)
919      {
920        if ($port[$oid] != $this_port[$oid])
921        {
922          if (isset($this_port[$oid]))
923          {
924            // Changed Oid
925            $port['update'][$oid] = $this_port[$oid];
926            $msg = "[$oid] '" . $port[$oid] . "' -> '" . $this_port[$oid] . "'";
927          } else {
928            // Removed/empty Oid
929            $port['update'][$oid] = array('NULL');
930            $msg = "[$oid] '" . $port[$oid] . "' -> NULL";
931          }
932          $log_event[] = $msg;
933          if (OBS_DEBUG) { echo($msg." "); }
934        }
935      }
936      if ((bool)$log_event) { log_event('Interface changed ('.$port_module.'): ' . implode('; ', $log_event), $device, 'port', $port); }
937    }
938    // End update additional MIBS
939
940    /* PAgP disabled since r7987, while not moved to new polling style
941    // Update PAgP
942    if ($this_port['pagpOperationMode'] || $port['pagpOperationMode'])
943    {
944      $log_event = array();
945      foreach ($pagp_oids as $oid)
946      { // Loop the OIDs
947        if ($this_port[$oid] != $port[$oid])
948        { // If data has changed, build a query
949          $port['update'][$oid] = $this_port[$oid];
950          $log_event[] = "[$oid] " . $port[$oid] . " -> " . $this_port[$oid];
951        }
952      }
953      if ((bool)$log_event) { log_event('Interface changed (pagp): ' . implode('; ', $log_event), $device, 'port', $port); }
954    }
955    // End Update PAgP
956    */
957
958#    if (OBS_DEBUG > 1) { print_vars($port['alert_array']); echo(PHP_EOL); print_vars($this_port);}
959
960    check_entity('port', $port, $port['alert_array']);
961
962    // Send statistics array via AMQP/JSON if AMQP is enabled globally and for the ports module
963    if ($config['amqp']['enable'] == TRUE && $config['amqp']['modules']['ports'])
964    {
965      $json_data = array_merge($this_port, $port['state']) ;
966      unset($json_data['rrd_update']); // FIXME unset no longer needed when switched to rrdtool_update_ng() !
967      messagebus_send(array('attribs' => array('t' => $polled, 'device' => $device['hostname'], 'device_id' => $device['device_id'], 'e_type' => 'port', 'e_index' => $port['ifIndex']), 'data' => $json_data));
968      unset($json_data);
969    }
970
971#    // Do Alcatel Detailed Stats
972#    if ($device['os'] == "aos") { include("port-alcatel.inc.php"); }
973
974    // Unified state update
975
976    //$port['update'] = array_merge($port['state'], $port['update']);
977    //$updated = dbUpdate($port['update'], 'ports', '`port_id` = ?', array($port['port_id']));
978
979    // Add to MultiUpdate ports state as single query
980    $ports_db_state[] = array_merge($this_port_indexes, $port['state']);
981
982    // Update Database
983    if (count($port['update']))
984    {
985      $updated = dbUpdate($port['update'], 'ports', '`port_id` = ?', array($port['port_id']));
986      //print_debug("PORT updated rows=$updated");
987    }
988
989    // Add table row
990
991    $table_row = array();
992    $table_row[] = $port['ifIndex'];
993    $table_row[] = $port['port_label_short'];
994    $table_row[] = rewrite_iftype($port['ifType']);
995    $table_row[] = formatRates($port['ifSpeed']);
996    $table_row[] = formatRates($port['stats']['ifInBits_rate']);
997    $table_row[] = formatRates($port['stats']['ifOutBits_rate']);
998    $table_row[] = formatStorage($port['stats']['ifInOctets_diff']);
999    $table_row[] = formatStorage($port['stats']['ifOutOctets_diff']);
1000    $table_row[] = format_si($port['stats']['ifInUcastPkts_rate']);
1001    $table_row[] = format_si($port['stats']['ifOutUcastPkts_rate']);
1002    $table_row[] = ($port['port_64bit'] ? "%gY%w" : "%rN%w");
1003    $table_rows[] = $table_row;
1004    unset($table_row);
1005
1006    // End Update Database
1007  }
1008  elseif ($port['disabled'] != "1")
1009  {
1010    print_message("Port Deleted."); // Port missing from SNMP cache.
1011    if (isset($port['ifIndex']) && $port['deleted'] != "1")
1012    {
1013      $ports_db_deleted[] = array('port_id' => $ports[$port['ifIndex']]['port_id'], 'ifIndex' => $port['ifIndex'], 'device_id' => $device['device_id'], // UNIQUE fields
1014                                  'deleted' => '1', 'ifLastChange' => date('Y-m-d H:i:s', $polled)); // Update this fields
1015      //dbUpdate(array('deleted' => '1', 'ifLastChange' => date('Y-m-d H:i:s', $polled)), 'ports',  '`device_id` = ? AND `ifIndex` = ?', array($device['device_id'], $port['ifIndex']));
1016      log_event("Interface was marked as DELETED", $device, 'port', $port);
1017    }
1018  } else {
1019    print_message("Port Disabled.");
1020  }
1021
1022  //echo("\n");
1023
1024  // Clear Per-Port Variables Here
1025  unset($this_port);
1026}
1027
1028// MultiUpdate deleted ports
1029if (count($ports_db_deleted))
1030{
1031  print_debug("MultiUpdate deleted ports DB.");
1032  // MultiUpdate required all UNIQUE keys!
1033  dbUpdateMulti($ports_db_deleted, 'ports', array('deleted', 'ifLastChange'));
1034}
1035
1036// MultiUpdate ports state
1037if (count($ports_db_state))
1038{
1039  print_debug("MultiUpdate ports states DB.");
1040  // MultiUpdate required all UNIQUE keys!
1041  dbUpdateMulti($ports_db_state, 'ports');
1042  // Better to pass keys need to update, but without also normal
1043  //$columns = array_diff(array_keys($port['state']), array_keys($this_port_indexes));
1044  //dbUpdateMulti($ports_db_state, 'ports', $columns);
1045}
1046
1047$headers = array('%WifIndex%n', '%WLabel%n', '%WType%n', '%WSpeed%n', '%WBPS In%n', '%WBPS Out%n', '%WData In%n', '%WData Out%n', '%WPPS In%n', '%WPPS Out%n', '%WHC%n');
1048print_cli_table($table_rows, $headers);
1049
1050echo(PHP_EOL);
1051
1052// Clear Variables Here
1053unset($port_stats, $process_port_functions, $process_port_db, $has_ifEntry, $has_ifXEntry, $has_ifEntry_error_code, $ports_ignored_count, $ports_ignored_count_db);
1054
1055// EOF
1056