diff --git a/doc/Support/Discovery Support.md b/doc/Support/Discovery Support.md
index 9d41002428..fb8caa538b 100644
--- a/doc/Support/Discovery Support.md
+++ b/doc/Support/Discovery Support.md
@@ -57,6 +57,7 @@ $config['discovery_modules']['os'] = 1;
$config['discovery_modules']['ports'] = 1;
$config['discovery_modules']['ports-stack'] = 1;
$config['discovery_modules']['entity-physical'] = 1;
+$config['discovery_modules']['entity-state'] = 0;
$config['discovery_modules']['processors'] = 1;
$config['discovery_modules']['mempools'] = 1;
$config['discovery_modules']['cisco-vrf-lite'] = 1;
diff --git a/doc/Support/Poller Support.md b/doc/Support/Poller Support.md
index f28f2a80e0..3862b913ae 100644
--- a/doc/Support/Poller Support.md
+++ b/doc/Support/Poller Support.md
@@ -76,6 +76,7 @@ $config['poller_modules']['cisco-vpdn'] = 0;
$config['poller_modules']['netscaler-vsvr'] = 0;
$config['poller_modules']['aruba-controller'] = 0;
$config['poller_modules']['entity-physical'] = 1;
+$config['poller_modules']['entity-state'] = 0;
$config['poller_modules']['applications'] = 1;
$config['poller_modules']['mib'] = 0;
$config['poller_modules']['stp'] = 1;
diff --git a/html/pages/device/entphysical.inc.php b/html/pages/device/entphysical.inc.php
index a3d485f7b7..a8a6138b35 100644
--- a/html/pages/device/entphysical.inc.php
+++ b/html/pages/device/entphysical.inc.php
@@ -6,51 +6,40 @@ function printEntPhysical($ent, $level, $class)
global $device;
$ents = dbFetchRows('SELECT * FROM `entPhysical` WHERE device_id = ? AND entPhysicalContainedIn = ? ORDER BY entPhysicalContainedIn,entPhysicalIndex', array($device['device_id'], $ent));
+
foreach ($ents as $ent) {
echo "
";
if ($ent['entPhysicalClass'] == 'chassis') {
- echo ' ';
- }
-
- if ($ent['entPhysicalClass'] == 'module') {
- echo ' ';
- }
-
- if ($ent['entPhysicalClass'] == 'port') {
- echo ' ';
- }
-
- if ($ent['entPhysicalClass'] == 'container') {
- echo ' ';
- }
-
- if ($ent['entPhysicalClass'] == 'sensor') {
- echo ' ';
+ echo ' ';
+ } elseif ($ent['entPhysicalClass'] == 'module') {
+ echo ' ';
+ } elseif ($ent['entPhysicalClass'] == 'port') {
+ echo ' ';
+ } elseif ($ent['entPhysicalClass'] == 'container') {
+ echo ' ';
+ } elseif ($ent['entPhysicalClass'] == 'sensor') {
+ echo ' ';
$sensor = dbFetchRow('SELECT * FROM `sensors` WHERE `device_id` = ? AND (`entPhysicalIndex` = ? OR `sensor_index` = ?)', array($device['device_id'], $ent['entPhysicalIndex'], $ent['entPhysicalIndex']));
if (count($sensor)) {
- $link = " href='device/device=".$device['device_id'].'/tab=health/metric='.$sensor['sensor_class']."/' onmouseover=\"return overlib('', LEFT,FGCOLOR,'#e5e5e5', BGCOLOR, '#c0c0c0', BORDER, 5, CELLPAD, 4, CAPCOLOR, '#050505');\" onmouseout=\"return nd();\"";
+ $link = "', LEFT,FGCOLOR,'#e5e5e5', BGCOLOR, '#c0c0c0', BORDER, 5, CELLPAD, 4, CAPCOLOR, '#050505');\" onmouseout=\"return nd();\">";
}
- } else {
- unset($link);
- }
-
- if ($ent['entPhysicalClass'] == 'backplane') {
- echo ' ';
+ } elseif ($ent['entPhysicalClass'] == 'backplane') {
+ echo ' ';
}
if ($ent['entPhysicalParentRelPos'] > '-1') {
echo ''.$ent['entPhysicalParentRelPos'].'. ';
}
- if ($link) {
- echo "";
+ if (isset($link)) {
+ echo $link;
}
if ($ent['ifIndex']) {
- $interface = dbFetchRow('SELECT * FROM `ports` WHERE ifIndex = ? AND device_id = ?', array($ent['ifIndex'], $device['device_id']));
- $interface = cleanPort($interface);
+ $interface = get_port_by_ifIndex($device['device_id'], $ent['ifIndex']);
+ $interface = cleanPort($interface);
$ent['entPhysicalName'] = generate_port_link($interface);
}
@@ -66,16 +55,50 @@ function printEntPhysical($ent, $level, $class)
echo ''.$ent['entPhysicalDescr'].'';
}
- if ($ent['entPhysicalClass'] == 'sensor') {
- echo ' ('.$ent['entSensorValue'].' '.$ent['entSensorType'].')';
+ if ($ent['entPhysicalClass'] == 'sensor' && isset($sensor)) {
+ echo ' ('.trim($sensor['sensor_current'] . ' ' . get_units_from_sensor($sensor)) . ')';
+ }
+
+ if (isset($link)) {
+ echo '';
+ unset($link);
+ }
+
+ // display entity state
+ $entState = dbFetchRow(
+ 'SELECT * FROM `entityState` WHERE `device_id`=? && `entPhysical_id`=?',
+ array($device['device_id'], $ent['entPhysical_id'])
+ );
+
+ if (!empty($entState)) {
+ $display_states = array(
+// 'entStateAdmin',
+ 'entStateOper',
+ 'entStateUsage',
+ 'entStateStandby'
+ );
+ foreach ($display_states as $state_name) {
+ $value = $entState[$state_name];
+ $display = parse_entity_state($state_name, $value);
+ echo " ";
+ echo $display['text'];
+ echo " ";
+ }
+
+ // ignore none and unavailable alarms
+ if ($entState['entStateAlarm'] != '00' && $entState['entStateAlarm'] != '80') {
+ $alarms = parse_entity_state_alarm($entState['entStateAlarm']);
+ echo '
';
+ echo "Alarms: ";
+ foreach ($alarms as $alarm) {
+ echo " {$alarm['text']}";
+ }
+ echo '';
+ }
}
echo "
".$ent['entPhysicalDescr'];
- if ($link) {
- echo '';
- }
-
if ($ent['entPhysicalSerialNum']) {
echo "
Serial No. ".$ent['entPhysicalSerialNum'].' ';
}
diff --git a/includes/dbFacile.php b/includes/dbFacile.php
index 850239b145..1d4e44d423 100644
--- a/includes/dbFacile.php
+++ b/includes/dbFacile.php
@@ -210,7 +210,11 @@ function dbBulkInsert($data, $table)
if ($rowvalues != '') {
$rowvalues .= ',';
}
- $rowvalues .= "'".mres($value)."'";
+ if (is_null($value)) {
+ $rowvalues .= 'NULL';
+ } else {
+ $rowvalues .= "'" . mres($value) . "'";
+ }
}
$values .= "(".$rowvalues.")";
}
diff --git a/includes/defaults.inc.php b/includes/defaults.inc.php
index e284b0f33e..b8a15f3c42 100644
--- a/includes/defaults.inc.php
+++ b/includes/defaults.inc.php
@@ -765,6 +765,7 @@ $config['poller_modules']['cisco-vpdn'] = 0;
$config['poller_modules']['netscaler-vsvr'] = 0;
$config['poller_modules']['aruba-controller'] = 0;
$config['poller_modules']['entity-physical'] = 1;
+$config['poller_modules']['entity-state'] = 0;
$config['poller_modules']['applications'] = 1;
$config['poller_modules']['mib'] = 0;
$config['poller_modules']['stp'] = 1;
@@ -779,6 +780,7 @@ $config['discovery_modules']['os'] = 1;
$config['discovery_modules']['ports'] = 1;
$config['discovery_modules']['ports-stack'] = 1;
$config['discovery_modules']['entity-physical'] = 1;
+$config['discovery_modules']['entity-state'] = 0;
$config['discovery_modules']['processors'] = 1;
$config['discovery_modules']['mempools'] = 1;
$config['discovery_modules']['cisco-vrf-lite'] = 1;
diff --git a/includes/discovery/entity-state.inc.php b/includes/discovery/entity-state.inc.php
new file mode 100644
index 0000000000..8034f560b5
--- /dev/null
+++ b/includes/discovery/entity-state.inc.php
@@ -0,0 +1,105 @@
+.
+ *
+ * @package LibreNMS
+ * @link http://librenms.org
+ * @copyright 2017 Tony Murray
+ * @author Tony Murray
+ */
+
+$entPhysical = dbFetchRows(
+ 'SELECT entPhysical_id, entPhysicalIndex FROM entPhysical WHERE device_id=?',
+ array($device['device_id'])
+);
+
+if (!empty($entPhysical)) {
+ echo "\nEntity States: ";
+
+ $entPhysical = array_combine(
+ array_column($entPhysical, 'entPhysicalIndex'),
+ array_column($entPhysical, 'entPhysical_id')
+ );
+ $state_data = snmpwalk_group($device, 'entStateTable', 'ENTITY-STATE-MIB');
+ $db_states = dbFetchRows('SELECT * FROM entityState WHERE device_id=?', array($device['device_id']));
+ $db_states = array_combine(array_column($db_states, 'entPhysical_id'), $db_states);
+
+ foreach ($state_data as $index => $state) {
+ if (isset($entPhysical[$index])) {
+ $id = $entPhysical[$index];
+
+ // format datetime
+ if (empty($state['entStateLastChanged'])) {
+ $state['entStateLastChanged'] = null;
+ } else {
+ list($date, $time, $tz) = explode(',', $state['entStateLastChanged']);
+ try {
+ $lastChanged = new DateTime("$date $time", new DateTimeZone($tz));
+ $state['entStateLastChanged'] = $lastChanged
+ ->setTimezone(new DateTimeZone(date_default_timezone_get()))
+ ->format('Y-m-d H:i:s');
+ } catch (Exception $e) {
+ // no update
+ }
+ }
+
+ if (isset($db_states[$id])) { // update the db
+ $db_state = $db_states[$id];
+ $update = array_diff($state, $db_state);
+
+ if (!empty($update)) {
+ if (array_key_exists('entStateLastChanged', $update) && is_null($update['entStateLastChanged'])) {
+ $update['entStateLastChanged'] = array('NULL');
+ }
+
+ dbUpdate($update, 'entityState', 'entity_state_id=?', array($db_state['entity_state_id']));
+ d_echo("Updating entity state: ", 'U');
+ d_echo($update);
+ } else {
+ echo '.';
+ }
+
+ unset($state_data[$index]); // remove so we don't insert later
+ unset($db_states[$id]); // remove so we don't delete later
+ } else {
+ // prep for insert later
+ $state_data[$index]['device_id'] = $device['device_id'];
+ $state_data[$index]['entPhysical_id'] = $id;
+ $state_data[$index]['entStateLastChanged'] = $state['entStateLastChanged'];
+ d_echo("Inserting entity state:: ", '+');
+ d_echo($state);
+ }
+ }
+ }
+
+ if (!empty($state_data)) {
+ dbBulkInsert($state_data, 'entityState');
+ }
+
+ if (!empty($db_states)) {
+ dbDelete(
+ 'entityState',
+ 'entity_state_id IN ' . dbGenPlaceholders(count($db_states)),
+ array_column($db_states, 'entity_state_id')
+ );
+ }
+}
+
+echo PHP_EOL;
+
+unset($entPhysical, $state_data, $db_states, $update);
diff --git a/includes/discovery/sensors.inc.php b/includes/discovery/sensors.inc.php
index 95095d4294..c12eb0f0d6 100644
--- a/includes/discovery/sensors.inc.php
+++ b/includes/discovery/sensors.inc.php
@@ -34,6 +34,7 @@ if (isset($device['dynamic_discovery']['modules']['sensors']) && $device['os'] !
// Run custom sensors
require 'includes/discovery/sensors/cisco-entity-sensor.inc.php';
require 'includes/discovery/sensors/entity-sensor.inc.php';
+require 'includes/discovery/sensors/entity-state.inc.php';
require 'includes/discovery/sensors/ipmi.inc.php';
if ($device['os'] == 'netscaler') {
diff --git a/includes/discovery/sensors/entity-sensor.inc.php b/includes/discovery/sensors/entity-sensor.inc.php
index 4f5ddfcbfa..9b9d6854e6 100644
--- a/includes/discovery/sensors/entity-sensor.inc.php
+++ b/includes/discovery/sensors/entity-sensor.inc.php
@@ -1,7 +1,7 @@
.
+ *
+ * @package LibreNMS
+ * @link http://librenms.org
+ * @copyright 2017 Tony Murray
+ * @author Tony Murray
+ */
+
+$entityStatesIndexes = dbFetchRows(
+ 'SELECT S.entity_state_id, S.entStateLastChanged, P.entPhysicalIndex FROM entityState AS S ' .
+ 'LEFT JOIN entPhysical AS P USING (entPhysical_id) WHERE S.device_id=?',
+ array($device['device_id'])
+);
+
+if (!empty($entityStatesIndexes)) {
+ echo "\nEntity States: ";
+
+ // index by entPhysicalIndex
+ $entityStatesIndexes = array_combine(array_column($entityStatesIndexes, 'entPhysicalIndex'), $entityStatesIndexes);
+
+ $entLC = snmpwalk_group($device, 'entStateLastChanged', 'ENTITY-STATE-MIB', 0);
+
+ foreach (current($entLC) as $index => $changed) {
+ if ($changed) { // skip empty entries
+ try {
+ list($date, $time, $tz) = explode(',', $changed);
+ $lastChanged = new DateTime("$date $time", new DateTimeZone($tz));
+ $dbLastChanged = new DateTime($entityStatesIndexes[$index]['entStateLastChanged']);
+ if ($lastChanged != $dbLastChanged) {
+ // data has changed, fetch it
+ $new_states = snmp_get_multi(
+ $device,
+ array(
+ "entStateAdmin.$index",
+ "entStateOper.$index",
+ "entStateUsage.$index",
+ "entStateAlarm.$index",
+ "entStateStandby.$index"
+ ),
+ '-OQUse',
+ 'ENTITY-STATE-MIB'
+ );
+ $new_states = $new_states[$index]; // just get values
+
+ // add entStateLastChanged and update
+ $new_states['entStateLastChanged'] = $lastChanged
+ ->setTimezone(new DateTimeZone(date_default_timezone_get()))
+ ->format('Y-m-d H:i:s');
+
+ // check if anything has changed
+ $update = array_diff(
+ $new_states,
+ dbFetchRow(
+ 'SELECT * FROM entityState WHERE entity_state_id=?',
+ array($entityStatesIndexes[$index]['entity_state_id'])
+ )
+ );
+
+ if (!empty($update)) {
+ dbUpdate(
+ $update,
+ 'entityState',
+ 'entity_state_id=?',
+ array($entityStatesIndexes[$index]['entity_state_id'])
+ );
+ d_echo("Updating $index: ", 'U');
+ d_echo($new_states[$index]);
+ continue;
+ }
+ }
+ } catch (Exception $e) {
+ // no update
+ d_echo("Error: " . $e->getMessage() . PHP_EOL);
+ }
+ }
+ echo '.';
+ }
+
+ echo PHP_EOL;
+}
+
+unset($entityStatesIndexes, $entLC, $lastChanged, $dbLastChanged, $new_states, $update);
diff --git a/includes/rewrites.php b/includes/rewrites.php
index b1e7c29bd7..734808fd04 100644
--- a/includes/rewrites.php
+++ b/includes/rewrites.php
@@ -1432,3 +1432,95 @@ function return_number($value)
}
return $value;
}
+
+function get_units_from_sensor($sensor)
+{
+ switch ($sensor['sensor_class']) {
+ case 'airflow':
+ return 'cfm';
+ case 'current':
+ return 'A';
+ case 'dbm':
+ case 'signal':
+ return 'dBm';
+ case 'fanspeed':
+ return 'rpm';
+ case 'frequency':
+ return 'Hz';
+ case 'charge':
+ case 'humidity':
+ case 'load':
+ return '%';
+ case 'cooling':
+ case 'power':
+ return 'W';
+ case 'pressure':
+ return 'kPa';
+ case 'runtime':
+ return 'Min';
+ case 'snr':
+ return 'SNR';
+ case 'state':
+ return '#';
+ case 'temperature':
+ return 'C';
+ case 'voltage':
+ return 'V';
+ default:
+ return '';
+ }
+}
+
+function parse_entity_state($state, $value)
+{
+ $data = array(
+ 'entStateOper' => array(
+ 1 => array('text' => 'unavailable', 'color' => 'default'),
+ 2 => array('text' => 'disabled', 'color' => 'danger'),
+ 3 => array('text' => 'enabled', 'color' => 'success'),
+ 4 => array('text' => 'testing', 'color' => 'warning'),
+ ),
+ 'entStateUsage' => array(
+ 1 => array('text' => 'unavailable', 'color' => 'default'),
+ 2 => array('text' => 'idle', 'color' => 'info'),
+ 3 => array('text' => 'active', 'color' => 'success'),
+ 4 => array('text' => 'busy', 'color' => 'success'),
+ ),
+ 'entStateStandby' => array(
+ 1 => array('text' => 'unavailable', 'color' => 'default'),
+ 2 => array('text' => 'hotStandby', 'color' => 'info'),
+ 3 => array('text' => 'coldStandby', 'color' => 'info'),
+ 4 => array('text' => 'providingService', 'color' => 'success'),
+ ),
+ 'entStateAdmin' => array(
+ 1 => array('text' => 'unknown', 'color' => 'default'),
+ 2 => array('text' => 'locked', 'color' => 'info'),
+ 3 => array('text' => 'shuttingDown', 'color' => 'warning'),
+ 4 => array('text' => 'unlocked', 'color' => 'success'),
+ ),
+ );
+
+ if (isset($data[$state][$value])) {
+ return $data[$state][$value];
+ }
+
+ return array('text'=>'na', 'color'=>'default');
+}
+
+function parse_entity_state_alarm($bits)
+{
+ // not sure if this is correct
+ $data = array(
+ 0 => array('text' => 'unavailable', 'color' => 'default'),
+ 1 => array('text' => 'underRepair', 'color' => 'warning'),
+ 2 => array('text' => 'critical', 'color' => 'danger'),
+ 3 => array('text' => 'major', 'color' => 'danger'),
+ 4 => array('text' => 'minor', 'color' => 'info'),
+ 5 => array('text' => 'warning', 'color' => 'warning'),
+ 6 => array('text' => 'indeterminate', 'color' => 'default'),
+ );
+
+ $alarms = str_split(base_convert($bits, 16, 2));
+ $active_alarms = array_filter($alarms);
+ return array_intersect_key($data, $active_alarms);
+}
diff --git a/misc/db_schema.yaml b/misc/db_schema.yaml
index 8bb45adde9..2c9e98d9e7 100644
--- a/misc/db_schema.yaml
+++ b/misc/db_schema.yaml
@@ -525,6 +525,20 @@ device_perf:
Indexes:
id: { Name: id, Columns: [id], Unique: false, Type: BTREE }
device_id: { Name: device_id, Columns: [device_id], Unique: false, Type: BTREE }
+entityState:
+ Columns:
+ - { Field: entity_state_id, Type: int(11), 'Null': false, Extra: auto_increment }
+ - { Field: device_id, Type: int(11), 'Null': true, Extra: '' }
+ - { Field: entPhysical_id, Type: int(11), 'Null': true, Extra: '' }
+ - { Field: entStateLastChanged, Type: datetime, 'Null': true, Extra: '' }
+ - { Field: entStateAdmin, Type: int(11), 'Null': true, Extra: '' }
+ - { Field: entStateOper, Type: int(11), 'Null': true, Extra: '' }
+ - { Field: entStateUsage, Type: int(11), 'Null': true, Extra: '' }
+ - { Field: entStateAlarm, Type: text, 'Null': true, Extra: '' }
+ - { Field: entStateStandby, Type: int(11), 'Null': true, Extra: '' }
+ Indexes:
+ PRIMARY: { Name: PRIMARY, Columns: [entity_state_id], Unique: true, Type: BTREE }
+ entityState_device_id_index: { Name: entityState_device_id_index, Columns: [device_id], Unique: false, Type: BTREE }
entPhysical:
Columns:
- { Field: entPhysical_id, Type: int(11), 'Null': false, Extra: auto_increment }
diff --git a/sql-schema/220.sql b/sql-schema/220.sql
new file mode 100644
index 0000000000..3a0e506409
--- /dev/null
+++ b/sql-schema/220.sql
@@ -0,0 +1,2 @@
+CREATE TABLE entityState (entity_state_id INT(11) PRIMARY KEY NOT NULL AUTO_INCREMENT, device_id INT(11), entPhysical_id INT(11), entStateLastChanged DATETIME, entStateAdmin INT(11), entStateOper INT(11), entStateUsage INT(11), entStateAlarm TEXT, entStateStandby INT(11));
+CREATE INDEX entityState_device_id_index ON entityState (device_id);