<?php

// Build SNMP Cache Array
$data_oids = array(
    'ifName',
    'ifDescr',
    'ifAlias',
    'ifAdminStatus',
    'ifOperStatus',
    'ifMtu',
    'ifSpeed',
    'ifHighSpeed',
    'ifType',
    'ifPhysAddress',
    'ifPromiscuousMode',
    'ifConnectorPresent',
    'ifDuplex',
    'ifTrunk',
    'ifVlan',
);

$stat_oids = array(
    'ifInErrors',
    'ifOutErrors',
    'ifInUcastPkts',
    'ifOutUcastPkts',
    'ifInNUcastPkts',
    'ifOutNUcastPkts',
    'ifHCInMulticastPkts',
    'ifHCInBroadcastPkts',
    'ifHCOutMulticastPkts',
    'ifHCOutBroadcastPkts',
    'ifInOctets',
    'ifOutOctets',
    'ifHCInOctets',
    'ifHCOutOctets',
    'ifInDiscards',
    'ifOutDiscards',
    'ifInUnknownProtos',
    'ifInBroadcastPkts',
    'ifOutBroadcastPkts',
    'ifInMulticastPkts',
    'ifOutMulticastPkts',
);

$stat_oids_db = array(
    'ifInOctets',
    'ifOutOctets',
    'ifInErrors',
    'ifOutErrors',
    'ifInUcastPkts',
    'ifOutUcastPkts',
);

$stat_oids_db_extended = array(
    'ifInNUcastPkts',
    'ifOutNUcastPkts',
    'ifInDiscards',
    'ifOutDiscards',
    'ifInUnknownProtos',
    'ifInBroadcastPkts',
    'ifOutBroadcastPkts',
    'ifInMulticastPkts',
    'ifOutMulticastPkts',
);

// From above for DB
$etherlike_oids = array(
    'dot3StatsAlignmentErrors',
    'dot3StatsFCSErrors',
    'dot3StatsSingleCollisionFrames',
    'dot3StatsMultipleCollisionFrames',
    'dot3StatsSQETestErrors',
    'dot3StatsDeferredTransmissions',
    'dot3StatsLateCollisions',
    'dot3StatsExcessiveCollisions',
    'dot3StatsInternalMacTransmitErrors',
    'dot3StatsCarrierSenseErrors',
    'dot3StatsFrameTooLongs',
    'dot3StatsInternalMacReceiveErrors',
    'dot3StatsSymbolErrors',
);

$cisco_oids = array(
    'locIfHardType',
    'locIfInRunts',
    'locIfInGiants',
    'locIfInCRC',
    'locIfInFrame',
    'locIfInOverrun',
    'locIfInIgnored',
    'locIfInAbort',
    'locIfCollisions',
    'locIfInputQueueDrops',
    'locIfOutputQueueDrops',
);

$pagp_oids = array(
    'pagpOperationMode',
    'pagpPortState',
    'pagpPartnerDeviceId',
    'pagpPartnerLearnMethod',
    'pagpPartnerIfIndex',
    'pagpPartnerGroupIfIndex',
    'pagpPartnerDeviceName',
    'pagpEthcOperationMode',
    'pagpDeviceId',
    'pagpGroupIfIndex',
);

$ifmib_oids = array_merge($data_oids, $stat_oids);

$ifmib_oids = array(
    'ifEntry',
    'ifXEntry',
);

echo 'Caching Oids: ';
foreach ($ifmib_oids as $oid) {
    echo "$oid ";
    $port_stats = snmpwalk_cache_oid($device, $oid, $port_stats, 'IF-MIB');
}

if ($config['enable_ports_etherlike']) {
    echo 'dot3Stats ';
    $port_stats = snmpwalk_cache_oid($device, 'dot3StatsEntry', $port_stats, 'EtherLike-MIB');
}
else {
    echo 'dot3StatsDuplexStatus';
    $port_stats = snmpwalk_cache_oid($device, 'dot3StatsDuplexStatus', $port_stats, 'EtherLike-MIB');
}

if ($config['enable_ports_adsl']) {
    $device['adsl_count'] = dbFetchCell("SELECT COUNT(*) FROM `ports` WHERE `device_id` = ? AND `ifType` = 'adsl'", array($device['device_id']));
}

if ($device['adsl_count'] > '0') {
    echo 'ADSL ';
    $port_stats = snmpwalk_cache_oid($device, '.1.3.6.1.2.1.10.94.1.1.1.1', $port_stats, 'ADSL-LINE-MIB');
    $port_stats = snmpwalk_cache_oid($device, '.1.3.6.1.2.1.10.94.1.1.2.1', $port_stats, 'ADSL-LINE-MIB');
    $port_stats = snmpwalk_cache_oid($device, '.1.3.6.1.2.1.10.94.1.1.3.1', $port_stats, 'ADSL-LINE-MIB');
    $port_stats = snmpwalk_cache_oid($device, '.1.3.6.1.2.1.10.94.1.1.4.1', $port_stats, 'ADSL-LINE-MIB');
    $port_stats = snmpwalk_cache_oid($device, '.1.3.6.1.2.1.10.94.1.1.5.1', $port_stats, 'ADSL-LINE-MIB');
    $port_stats = snmpwalk_cache_oid($device, '.1.3.6.1.2.1.10.94.1.1.6.1.1', $port_stats, 'ADSL-LINE-MIB');
    $port_stats = snmpwalk_cache_oid($device, '.1.3.6.1.2.1.10.94.1.1.6.1.2', $port_stats, 'ADSL-LINE-MIB');
    $port_stats = snmpwalk_cache_oid($device, '.1.3.6.1.2.1.10.94.1.1.6.1.3', $port_stats, 'ADSL-LINE-MIB');
    $port_stats = snmpwalk_cache_oid($device, '.1.3.6.1.2.1.10.94.1.1.6.1.4', $port_stats, 'ADSL-LINE-MIB');
    $port_stats = snmpwalk_cache_oid($device, '.1.3.6.1.2.1.10.94.1.1.6.1.5', $port_stats, 'ADSL-LINE-MIB');
    $port_stats = snmpwalk_cache_oid($device, '.1.3.6.1.2.1.10.94.1.1.6.1.6', $port_stats, 'ADSL-LINE-MIB');
    $port_stats = snmpwalk_cache_oid($device, '.1.3.6.1.2.1.10.94.1.1.6.1.7', $port_stats, 'ADSL-LINE-MIB');
    $port_stats = snmpwalk_cache_oid($device, '.1.3.6.1.2.1.10.94.1.1.6.1.8', $port_stats, 'ADSL-LINE-MIB');
    $port_stats = snmpwalk_cache_oid($device, '.1.3.6.1.2.1.10.94.1.1.7.1.1', $port_stats, 'ADSL-LINE-MIB');
    $port_stats = snmpwalk_cache_oid($device, '.1.3.6.1.2.1.10.94.1.1.7.1.2', $port_stats, 'ADSL-LINE-MIB');
    $port_stats = snmpwalk_cache_oid($device, '.1.3.6.1.2.1.10.94.1.1.7.1.3', $port_stats, 'ADSL-LINE-MIB');
    $port_stats = snmpwalk_cache_oid($device, '.1.3.6.1.2.1.10.94.1.1.7.1.4', $port_stats, 'ADSL-LINE-MIB');
    $port_stats = snmpwalk_cache_oid($device, '.1.3.6.1.2.1.10.94.1.1.7.1.5', $port_stats, 'ADSL-LINE-MIB');
    $port_stats = snmpwalk_cache_oid($device, '.1.3.6.1.2.1.10.94.1.1.7.1.6', $port_stats, 'ADSL-LINE-MIB');
    $port_stats = snmpwalk_cache_oid($device, '.1.3.6.1.2.1.10.94.1.1.7.1.7', $port_stats, 'ADSL-LINE-MIB');
}//end if

if ($config['enable_ports_poe']) {
    $port_stats = snmpwalk_cache_oid($device, 'pethPsePortEntry', $port_stats, 'POWER-ETHERNET-MIB');
    $port_stats = snmpwalk_cache_oid($device, 'cpeExtPsePortEntry', $port_stats, 'CISCO-POWER-ETHERNET-EXT-MIB');
}

// FIXME This probably needs re-enabled. We need to clear these things when they get unset, too.
// foreach ($etherlike_oids as $oid) { $port_stats = snmpwalk_cache_oid($device, $oid, $port_stats, "EtherLike-MIB"); }
// foreach ($cisco_oids as $oid)     { $port_stats = snmpwalk_cache_oid($device, $oid, $port_stats, "OLD-CISCO-INTERFACES-MIB"); }
// foreach ($pagp_oids as $oid)      { $port_stats = snmpwalk_cache_oid($device, $oid, $port_stats, "CISCO-PAGP-MIB"); }
if ($device['os_group'] == 'cisco') {
    $port_stats = snmp_cache_portIfIndex($device, $port_stats);
    $port_stats = snmp_cache_portName($device, $port_stats);
    foreach ($pagp_oids as $oid) {
        $port_stats = snmpwalk_cache_oid($device, $oid, $port_stats, 'CISCO-PAGP-MIB');
    }

    $data_oids[] = 'portName';

    // Grab data to put ports into vlans or make them trunks
    // FIXME we probably shouldn't be doing this from the VTP MIB, right?
    $port_stats = snmpwalk_cache_oid($device, 'vmVlan', $port_stats, 'CISCO-VLAN-MEMBERSHIP-MIB');
    $port_stats = snmpwalk_cache_oid($device, 'vlanTrunkPortEncapsulationOperType', $port_stats, 'CISCO-VTP-MIB');
    $port_stats = snmpwalk_cache_oid($device, 'vlanTrunkPortNativeVlan', $port_stats, 'CISCO-VTP-MIB');
}
else {
    $port_stats = snmpwalk_cache_oid($device, 'dot1qPortVlanTable', $port_stats, 'Q-BRIDGE-MIB');
}//end if

$polled = time();

// End Building SNMP Cache Array
d_echo($port_stats);

// By default libreNMS uses the ifIndex to associate ports on devices with ports discoverd/polled
// before and stored in the database. On Linux boxes this is a problem as ifIndexes may be
// unstable between reboots or (re)configuration of tunnel interfaces (think: GRE/OpenVPN/Tinc/...)
// The port association configuration allows to choose between association via ifIndex, ifName,
// or maybe other means in the future. The default port association mode still is ifIndex for
// compatibility reasons.
$port_association_mode = $config['default_port_association_mode'];
if ($device['port_association_mode'])
    $port_association_mode = get_port_assoc_mode_name ($device['port_association_mode']);

// Query known ports and mapping table in order of discovery to make sure
// the latest discoverd/polled port is in the mapping tables.
$ports_mapped = get_ports_mapped ($device['device_id'], true);
$ports = $ports_mapped['ports'];

//
// Rename any old RRD files still named after the previous ifIndex based naming schema.
foreach ($ports_mapped['maps']['ifIndex'] as $ifIndex => $port_id) {
    foreach (array ('', 'adsl', 'dot3') as $suffix) {
        $suffix_tmp = '';
        if ($suffix)
            $suffix_tmp = "-$suffix";

        $old_rrd_path = trim ($config['rrd_dir']) . '/' . $device['hostname'] . "/port-$ifIndex$suffix_tmp.rrd";
        $new_rrd_path = get_port_rrdfile_path ($device['hostname'], $port_id, $suffix);

        if (is_file ($old_rrd_path)) {
            rename ($old_rrd_path, $new_rrd_path);
        }
    }
}


$ports_found = array ();
// New interface detection
foreach ($port_stats as $ifIndex => $port) {
    // Store ifIndex in port entry and prefetch ifName as we'll need it multiple times
    $port['ifIndex'] = $ifIndex;
    $ifName = $port['ifName'];

    // Get port_id according to port_association_mode used for this device
    $port_id = get_port_id ($ports_mapped, $port, $port_association_mode);

    if (is_port_valid($port, $device)) {
        echo 'valid';

        // Port newly discovered?
        if (! $ports[$port_id]) {
            /**
              * When using the ifName or ifDescr as means to map discovered ports to
              * known ports in the DB (think of port association mode) it's possible
              * that we're facing the problem that the ifName or ifDescr polled from
              * the device is unset or an empty string (like when querying some ubnt
              * devices...). If this happends we have no way to map this port to any
              * port found in the database. As reported this situation may occur for
              * the time of one poll and might resolve automagically before the next
              * poller run happens. Without this special case this would lead to new
              * ports added to the database each time this situation occurs. To give
              * the user the choice between »a lot of new ports« and »some poll runs
              * are missed but ports stay stable« the 'ignore_unmapable_port' option
              * has been added to configure this behaviour. To skip the port in this
              * loop is sufficient as the next loop is looping only over ports found
              * in the database and "maps back". As we did not add a new port to the
              * DB here, there's no port to be mapped to.
              *
              * I'm using the in_array() check here, as I'm not sure if an "ifIndex"
              * can be legally set to 0, which would yield True when checking if the
              * value is empty().
              */
            if ($config['ignore_unmapable_port'] === True and in_array ($port[$port_association_mode], array ('', Null))) {
                continue;
            }

            $port_id         = dbInsert(array('device_id' => $device['device_id'], 'ifIndex' => $ifIndex, 'ifName' => $ifName), 'ports');
            dbInsert(array('port_id' => $port_id), 'ports_statistics');
            $ports[$port_id] = dbFetchRow('SELECT * FROM `ports` WHERE `port_id` = ?', array($port_id));
            echo 'Adding: '.$ifName.'('.$ifIndex.')('.$port_id.')';
            // print_r($ports);
        }

        // Port re-discovered after previous deletion?
        else if ($ports[$port_id]['deleted'] == 1) {
            dbUpdate(array('deleted' => '0'), 'ports', '`port_id` = ?', array($port_id));
            $ports[$port_id]['deleted'] = '0';
        }
        if ($ports[$port_id]['ports_statistics_port_id'] === null) {
            // in case the port was created before we created the table
            dbInsert(array('port_id' => $port_id), 'ports_statistics');
        }

        /** Assure stable bidirectional port mapping between DB and polled data
          *
          * Store the *current* ifIndex in the port info array containing all port information
          * fetched from the database, as this is the only means we have to map ports_stats we
          * just polled from the device to a port in $ports. All code below an includeed below
          * will and has to map a port using it's ifIndex.
          */
        $ports[$port_id]['ifIndex'] = $ifIndex;
        $port_stats[$ifIndex]['port_id'] = $port_id;

	/* Build a list of all ports, identified by their port_id, found within this poller run. */
	$ports_found[] = $port_id;
    }

    // Port vanished (mark as deleted)
    else {
        if ($ports[$port_id]['deleted'] != '1') {
            dbUpdate(array('deleted' => '1'), 'ports', '`port_id` = ?', array($port_id));
            $ports[$port_id]['deleted'] = '1';
        }
    }
} // End new interface detection


echo "\n";
// Loop ports in the DB and update where necessary
foreach ($ports as $port) {
    $port_id = $port['port_id'];
    $ifIndex = $port['ifIndex'];

    $port_info_string = 'Port ' . $port['ifName'] . ': ' . $port['ifDescr'] . " ($ifIndex / #$port_id) ";

    /* We don't care for disabled ports, go on */
    if ($port['disabled'] == 1) {
        echo "$port_info_string disabled.\n";
        continue;
    }

    /**
     * If this port did not show up in $port_stats before it has been deleted
     * since the last poller run. Mark it deleted in the database and go on.
     */
    if (! in_array ($port_id, $ports_found)) {
        if ($port['deleted'] != '1') {
            dbUpdate(array('deleted' => '1'), 'ports', '`device_id` = ? AND `port_id` = ?', array($device['device_id'], $port_id));
            echo "$port_info_string deleted.\n";
        }
        continue;
    }

    echo $port_info_string;
    if ($port_stats[$ifIndex]) {
        // Check to make sure Port data is cached.
        $this_port = &$port_stats[$ifIndex];

        if ($device['os'] == 'vmware' && preg_match('/Device ([a-z0-9]+) at .*/', $this_port['ifDescr'], $matches)) {
            $this_port['ifDescr'] = $matches[1];
        }

        $polled_period = ($polled - $port['poll_time']);

        $port['update'] = array();
        $port['update_extended'] = array();
        $port['state']  = array();

        if ($config['slow_statistics'] == true) {
            $port['update']['poll_time']   = $polled;
            $port['update']['poll_prev']   = $port['poll_time'];
            $port['update']['poll_period'] = $polled_period;
        }

        // Copy ifHC[In|Out]Octets values to non-HC if they exist
        if ($this_port['ifHCInOctets'] > 0 && is_numeric($this_port['ifHCInOctets']) && $this_port['ifHCOutOctets'] > 0 && is_numeric($this_port['ifHCOutOctets'])) {
            echo 'HC ';
            $this_port['ifInOctets']  = $this_port['ifHCInOctets'];
            $this_port['ifOutOctets'] = $this_port['ifHCOutOctets'];
        }

        if ($device['os'] === 'airos-af' && $port['ifAlias'] === 'eth0') {
            $airos_stats = snmpwalk_cache_oid($device, '.1.3.6.1.4.1.41112.1.3.3.1', $airos_stats, 'UBNT-AirFIBER-MIB');
            $this_port['ifInOctets'] = $airos_stats[1]['rxOctetsOK'];
            $this_port['ifOutOctets'] = $airos_stats[1]['txOctetsOK'];
            $this_port['ifInErrors'] = $airos_stats[1]['rxErroredFrames'];
            $this_port['ifOutErrors'] = $airos_stats[1]['txErroredFrames'];
            $this_port['ifInBroadcastPkts'] = $airos_stats[1]['rxValidBroadcastFrames'];
            $this_port['ifOutBroadcastPkts'] = $airos_stats[1]['txValidBroadcastFrames'];
            $this_port['ifInMulticastPkts'] = $airos_stats[1]['rxValidMulticastFrames'];
            $this_port['ifOutMulticastPkts'] = $airos_stats[1]['txValidMulticastFrames'];
            $this_port['ifInUcastPkts'] = $airos_stats[1]['rxValidUnicastFrames'];
            $this_port['ifOutUcastPkts'] = $airos_stats[1]['txValidUnicastFrames'];
            $ports['update']['ifInOctets'] = $airos_stats[1]['rxOctetsOK'];
            $ports['update']['ifOutOctets'] = $airos_stats[1]['txOctetsOK'];
            $ports['update']['ifInErrors'] = $airos_stats[1]['rxErroredFrames'];
            $ports['update']['ifOutErrors'] = $airos_stats[1]['txErroredFrames'];
            $ports['update']['ifInBroadcastPkts'] = $airos_stats[1]['rxValidBroadcastFrames'];
            $ports['update']['ifOutBroadcastPkts'] = $airos_stats[1]['txValidBroadcastFrames'];
            $ports['update']['ifInMulticastPkts'] = $airos_stats[1]['rxValidMulticastFrames'];
            $ports['update']['ifOutMulticastPkts'] = $airos_stats[1]['txValidMulticastFrames'];
            $ports['update']['ifInUcastPkts'] = $airos_stats[1]['rxValidUnicastFrames'];
            $ports['update']['ifOutUcastPkts'] = $airos_stats[1]['txValidUnicastFrames'];
        }

        // rewrite the ifPhysAddress
        if (strpos($this_port['ifPhysAddress'], ':')) {
            list($a_a, $a_b, $a_c, $a_d, $a_e, $a_f) = explode(':', $this_port['ifPhysAddress']);
            $this_port['ifPhysAddress']              = zeropad($a_a).zeropad($a_b).zeropad($a_c).zeropad($a_d).zeropad($a_e).zeropad($a_f);
        }

        if (is_numeric($this_port['ifHCInBroadcastPkts']) && is_numeric($this_port['ifHCOutBroadcastPkts']) && is_numeric($this_port['ifHCInMulticastPkts']) && is_numeric($this_port['ifHCOutMulticastPkts']) && $device['os'] !== 'ciscosb') {
            echo 'HC ';
            $this_port['ifInBroadcastPkts']  = $this_port['ifHCInBroadcastPkts'];
            $this_port['ifOutBroadcastPkts'] = $this_port['ifHCOutBroadcastPkts'];
            $this_port['ifInMulticastPkts']  = $this_port['ifHCInMulticastPkts'];
            $this_port['ifOutMulticastPkts'] = $this_port['ifHCOutMulticastPkts'];
        }

        // Overwrite ifSpeed with ifHighSpeed if it's over 1G
        if (is_numeric($this_port['ifHighSpeed']) && ($this_port['ifSpeed'] > '1000000000' || $this_port['ifSpeed'] == 0)) {
            echo 'HighSpeed ';
            $this_port['ifSpeed'] = ($this_port['ifHighSpeed'] * 1000000);
        }

        // Overwrite ifDuplex with dot3StatsDuplexStatus if it exists
        if (isset($this_port['dot3StatsDuplexStatus'])) {
            echo 'dot3Duplex ';
            $this_port['ifDuplex'] = $this_port['dot3StatsDuplexStatus'];
        }

        // Set VLAN and Trunk from Cisco
        if (isset($this_port['vlanTrunkPortEncapsulationOperType']) && $this_port['vlanTrunkPortEncapsulationOperType'] != 'notApplicable') {
            $this_port['ifTrunk'] = $this_port['vlanTrunkPortEncapsulationOperType'];
            if (isset($this_port['vlanTrunkPortNativeVlan'])) {
                $this_port['ifVlan'] = $this_port['vlanTrunkPortNativeVlan'];
            }
        }

        if (isset($this_port['vmVlan'])) {
            $this_port['ifVlan'] = $this_port['vmVlan'];
        }

        // Set VLAN and Trunk from Q-BRIDGE-MIB
        if (!isset($this_port['ifVlan']) && isset($this_port['dot1qPvid'])) {
            $this_port['ifVlan'] = $this_port['dot1qPvid'];
        }

        // FIXME use $q_bridge_mib[$this_port['ifIndex']] to see if it is a trunk (>1 array count)
        echo 'VLAN == '.$this_port['ifVlan'];

	// When devices do not provide ifAlias data, populate with ifDescr data if configured
        if ($this_port['ifAlias'] == '' || $this_port['ifAlias'] == NULL) {
            $this_port['ifAlias'] = $this_port['ifDescr'];
            d_echo('Using ifDescr as ifAlias');
        }

        // Update IF-MIB data
        $tune_port = false;
        foreach ($data_oids as $oid) {

            if ($oid == 'ifAlias') {
                if (get_dev_attrib($device, 'ifName:'.$port['ifName'], 1)) {
                    $this_port['ifAlias'] = $port['ifAlias'];
                }
            }
            if ($oid == 'ifSpeed' || $oid == 'ifHighSpeed') {
                if (get_dev_attrib($device, 'ifSpeed:'.$port['ifName'], 1)) {
                    $this_port[$oid] = $port[$oid];
                }
            }

            if ($port[$oid] != $this_port[$oid] && !isset($this_port[$oid])) {
                $port['update'][$oid] = array('NULL');
                log_event($oid.': '.$port[$oid].' -> NULL', $device, 'interface', $port['port_id']);
                if ($debug) {
                    d_echo($oid.': '.$port[$oid].' -> NULL ');
                }
                else {
                    echo $oid.' ';
                }
            }
            else if ($port[$oid] != $this_port[$oid]) {
                $port_tune = get_dev_attrib($device, 'ifName_tune:'.$port['ifName']);
                $device_tune = get_dev_attrib($device,'override_rrdtool_tune');
                if ($port_tune == "true" ||
                    ($device_tune == "true" && $port_tune != 'false') || 
                    ($config['rrdtool_tune'] == "true" && $port_tune != 'false' && $device_tune != 'false')) {
                    if ($oid == 'ifSpeed') {
                        $tune_port = true;
                    }
                }
                $port['update'][$oid] = $this_port[$oid];
                if ($oid == 'ifOperStatus' || $oid == 'ifAdminStatus') {
                    $port['update'][$oid.'_prev'] = $port[$oid];
                }
                log_event($oid.': '.$port[$oid].' -> '.$this_port[$oid], $device, 'interface', $port['port_id']);
                if ($debug) {
                    d_echo($oid.': '.$port[$oid].' -> '.$this_port[$oid].' ');
                }
                else {
                    echo $oid.' ';
                }
            }
        }//end foreach

        // Parse description (usually ifAlias) if config option set
        if (isset($config['port_descr_parser']) && is_file($config['install_dir'].'/'.$config['port_descr_parser'])) {
            $port_attribs = array(
                'type',
                'descr',
                'circuit',
                'speed',
                'notes',
            );

            include $config['install_dir'].'/'.$config['port_descr_parser'];

            foreach ($port_attribs as $attrib) {
                $attrib_key = 'port_descr_'.$attrib;
                if ($port_ifAlias[$attrib] != $port[$attrib_key]) {
                    if (!isset($port_ifAlias[$attrib])) {
                        $port_ifAlias[$attrib] = array('NULL');
                        $log_port              = 'NULL';
                    }
                    else {
                        $log_port = $port_ifAlias[$attrib];
                    }

                    $port['update'][$attrib_key] = $port_ifAlias[$attrib];
                    log_event($attrib.': '.$port[$attrib_key].' -> '.$log_port, $device, 'interface', $port['port_id']);
                    unset($log_port);
                }
            }
        }//end if

        // End parse ifAlias
        // Update IF-MIB metrics
        $_stat_oids = array_merge($stat_oids_db, $stat_oids_db_extended);
        foreach ($_stat_oids as $oid) {
            $port_update = 'update';
            $extended_metric = !in_array($oid, $stat_oids_db, true);
            if ($extended_metric) {
                $port_update = 'update_extended';
            }

            if ($config['slow_statistics'] == true) {
                $port[$port_update][$oid]         = $this_port[$oid];
                $port[$port_update][$oid.'_prev'] = $port[$oid];
            }

            $oid_prev = $oid.'_prev';
            if (isset($port[$oid])) {
                $oid_diff = ($this_port[$oid] - $port[$oid]);
                $oid_rate = ($oid_diff / $polled_period);
                if ($oid_rate < 0) {
                    $oid_rate = '0';
                    echo "negative $oid";
                }

                $port['stats'][$oid.'_rate'] = $oid_rate;
                $port['stats'][$oid.'_diff'] = $oid_diff;

                if ($config['slow_statistics'] == true) {
                    $port[$port_update][$oid.'_rate']  = $oid_rate;
                    $port[$port_update][$oid.'_delta'] = $oid_diff;
                }

                d_echo("\n $oid ($oid_diff B) $oid_rate Bps $polled_period secs\n");
            }//end if
        }//end foreach

        if ($config['debug_port'][$port['port_id']]) {
            $port_debug  = $port['port_id'].'|'.$polled.'|'.$polled_period.'|'.$this_port['ifHCInOctets'].'|'.$this_port['ifHCOutOctets'];
            $port_debug .= '|'.$port['stats']['ifInOctets_rate'].'|'.$port['stats']['ifOutOctets_rate']."\n";
            file_put_contents('/tmp/port_debug.txt', $port_debug, FILE_APPEND);
            echo 'Wrote port debugging data';
        }

        $port['stats']['ifInBits_rate']  = round(($port['stats']['ifInOctets_rate'] * 8));
        $port['stats']['ifOutBits_rate'] = round(($port['stats']['ifOutOctets_rate'] * 8));

        // If we have a valid ifSpeed we should populate the stats for checking.
        if (is_numeric($this_port['ifSpeed'])) {
            $port['stats']['ifInBits_perc']  = round(($port['stats']['ifInBits_rate'] / $this_port['ifSpeed'] * 100));
            $port['stats']['ifOutBits_perc'] = round(($port['stats']['ifOutBits_rate'] / $this_port['ifSpeed'] * 100));
        }

        echo 'bps('.formatRates($port['stats']['ifInBits_rate']).'/'.formatRates($port['stats']['ifOutBits_rate']).')';
        echo 'bytes('.formatStorage($port['stats']['ifInOctets_diff']).'/'.formatStorage($port['stats']['ifOutOctets_diff']).')';
        echo 'pkts('.format_si($port['stats']['ifInUcastPkts_rate']).'pps/'.format_si($port['stats']['ifOutUcastPkts_rate']).'pps)';

        // Port utilisation % threshold alerting. // FIXME allow setting threshold per-port. probably 90% of ports we don't care about.
        if ($config['alerts']['port_util_alert'] && $port['ignore'] == '0') {
            // Check for port saturation of $config['alerts']['port_util_perc'] or higher.  Alert if we see this.
            // Check both inbound and outbound rates
            $saturation_threshold = ($this_port['ifSpeed'] * ( $config['alerts']['port_util_perc'] / 100 ));
            echo 'IN: '.$port['stats']['ifInBits_rate'].' OUT: '.$port['stats']['ifOutBits_rate'].' THRESH: '.$saturation_threshold;
            if (($port['stats']['ifInBits_rate'] >= $saturation_threshold || $port['stats']['ifOutBits_rate'] >= $saturation_threshold) && $saturation_threshold > 0) {
                log_event('Port reached saturation threshold: '.formatRates($port['stats']['ifInBits_rate']).'/'.formatRates($port['stats']['ifOutBits_rate']).' - ifspeed: '.formatRates($this_port['stats']['ifSpeed']), $device, 'interface', $port['port_id']);
            }
        }

        // Update RRDs
        $rrdfile = get_port_rrdfile_path ($device['hostname'], $port_id);
        if (!is_file($rrdfile)) {
            rrdtool_create(
                $rrdfile,
                ' --step 300 
                DS:INOCTETS:DERIVE:600:0:12500000000 
                DS:OUTOCTETS:DERIVE:600:0:12500000000 
                DS:INERRORS:DERIVE:600:0:12500000000 
                DS:OUTERRORS:DERIVE:600:0:12500000000 
                DS:INUCASTPKTS:DERIVE:600:0:12500000000 
                DS:OUTUCASTPKTS:DERIVE:600:0:12500000000 
                DS:INNUCASTPKTS:DERIVE:600:0:12500000000 
                DS:OUTNUCASTPKTS:DERIVE:600:0:12500000000 
                DS:INDISCARDS:DERIVE:600:0:12500000000 
                DS:OUTDISCARDS:DERIVE:600:0:12500000000 
                DS:INUNKNOWNPROTOS:DERIVE:600:0:12500000000 
                DS:INBROADCASTPKTS:DERIVE:600:0:12500000000 
                DS:OUTBROADCASTPKTS:DERIVE:600:0:12500000000 
                DS:INMULTICASTPKTS:DERIVE:600:0:12500000000 
                DS:OUTMULTICASTPKTS:DERIVE:600:0:12500000000 '.$config['rrd_rra']
            );
        }//end if

        $fields = array(
            'INOCTETS'         => $this_port['ifInOctets'],
            'OUTOCTETS'        => $this_port['ifOutOctets'],
            'INERRORS'         => $this_port['ifInErrors'],
            'OUTERRORS'        => $this_port['ifOutErrors'],
            'INUCASTPKTS'      => $this_port['ifInUcastPkts'],
            'OUTUCASTPKTS'     => $this_port['ifOutUcastPkts'],
            'INNUCASTPKTS'     => $this_port['ifInNUcastPkts'],
            'OUTNUCASTPKTS'    => $this_port['ifOutNUcastPkts'],
            'INDISCARDS'       => $this_port['ifInDiscards'],
            'OUTDISCARDS'      => $this_port['ifOutDiscards'],
            'INUNKNOWNPROTOS'  => $this_port['ifInUnknownProtos'],
            'INBROADCASTPKTS'  => $this_port['ifInBroadcastPkts'],
            'OUTBROADCASTPKTS' => $this_port['ifOutBroadcastPkts'],
            'INMULTICASTPKTS'  => $this_port['ifInMulticastPkts'],
            'OUTMULTICASTPKTS' => $this_port['ifOutMulticastPkts'],
        );

        if ($tune_port === true) {
            rrdtool_tune('port',$rrdfile,$this_port['ifSpeed']);
        }
        rrdtool_update("$rrdfile", $fields);

        $fields['ifInUcastPkts_rate'] = $port['ifInUcastPkts_rate'];
        $fields['ifOutUcastPkts_rate'] = $port['ifOutUcastPkts_rate'];
        $fields['ifInErrors_rate'] = $port['ifInErrors_rate'];
        $fields['ifOutErrors_rate'] = $port['ifOutErrors_rate'];
        $fields['ifInOctets_rate'] = $port['ifInOctets_rate'];
        $fields['ifOutOctets_rate'] = $port['ifOutOctets_rate'];

        $tags = array('ifName' => $port['ifName'], 'port_descr_type' => $port['port_descr_type']);
        influx_update($device,'ports',$tags,$fields);

        // End Update IF-MIB
        // Update PAgP
        if ($this_port['pagpOperationMode'] || $port['pagpOperationMode']) {
            foreach ($pagp_oids as $oid) {
                // Loop the OIDs
                if ($this_port[$oid] != $port[$oid]) {
                    // If data has changed, build a query
                    $port['update'][$oid] = $this_port[$oid];
                    echo 'PAgP ';
                    log_event("$oid -> ".$this_port[$oid], $device, 'interface', $port['port_id']);
                }
            }
        }

        // End Update PAgP
        // Do EtherLike-MIB
        if ($config['enable_ports_etherlike']) {
            include 'port-etherlike.inc.php';
        }

        // Do ADSL MIB
        if ($config['enable_ports_adsl']) {
            include 'port-adsl.inc.php';
        }

        // Do PoE MIBs
        if ($config['enable_ports_poe']) {
            include 'port-poe.inc.php';
        }

        // Do Alcatel Detailed Stats
        if ($device['os'] == 'aos') {
            include 'port-alcatel.inc.php';
        }

        foreach ($port['update'] as $key => $val_check) {
            if (!isset($val_check)) {
                unset($port['update'][$key]);
            }
        }

        // Update Database
        if (count($port['update'])) {
            $updated = dbUpdate($port['update'], 'ports', '`port_id` = ?', array($port_id));
            // do we want to do something else with this?
            $updated += dbUpdate($port['update_extended'], 'ports_statistics', '`port_id` = ?', array($port_id));
            d_echo("$updated updated");
        }
        // End Update Database
    }

    echo "\n";

    // Clear Per-Port Variables Here
    unset($this_port);
} //end port update

// Clear Variables Here
unset($port_stats);
unset($ports_found);
