diff --git a/LibreNMS/Snmptrap/Handlers/AxisAlarmCleared.php b/LibreNMS/Snmptrap/Handlers/AxisAlarmCleared.php new file mode 100644 index 000000000000..2bb77b818769 --- /dev/null +++ b/LibreNMS/Snmptrap/Handlers/AxisAlarmCleared.php @@ -0,0 +1,59 @@ +. + * + * @link https://www.librenms.org + * + * @copyright 2024 Transitiv Technologies Ltd. + * @author Adam Sweet + */ + +namespace LibreNMS\Snmptrap\Handlers; + +use App\Models\Device; +use LibreNMS\Enum\Severity; +use LibreNMS\Interfaces\SnmptrapHandler; +use LibreNMS\Snmptrap\Trap; + +class AxisAlarmCleared implements SnmptrapHandler +{ + /** + * Handle snmptrap. + * Data is pre-parsed and delivered as a Trap. + * + * @param Device $device + * @param Trap $trap + * @return void + */ + public function handle(Device $device, Trap $trap) + { + $AlarmString = $trap->getOidData($trap->findOid('AXIS-VIDEO-MIB::alarmID')); + // Handle data type errors in translated trap + $AlarmID = preg_match('/^(?P.+?)?(\:\s)?(?P\d+)$/m', $AlarmString, $matches); + if (! empty($matches['value'])) { + $AlarmID = $matches['value']; + } else { + $AlarmID = $trap->getOidData($trap->findOid('AXIS-VIDEO-MIB::alarmID')); + } + + $AlarmName = $trap->getOidData($trap->findOid('AXIS-VIDEO-MIB::alarmName')); + $Message = $trap->getOidData($trap->findOid('AXIS-VIDEO-MIB::alarmText')); + + $trap->log("Axis Alarm Cleared Trap: Alarm ID $AlarmID for $AlarmName with text \"$Message\" has cleared", Severity::Ok); + } +} diff --git a/LibreNMS/Snmptrap/Handlers/AxisAlarmNew.php b/LibreNMS/Snmptrap/Handlers/AxisAlarmNew.php new file mode 100644 index 000000000000..6c86c5fe23f4 --- /dev/null +++ b/LibreNMS/Snmptrap/Handlers/AxisAlarmNew.php @@ -0,0 +1,59 @@ +. + * + * @link https://www.librenms.org + * + * @copyright 2024 Transitiv Technologies Ltd. + * @author Adam Sweet + */ + +namespace LibreNMS\Snmptrap\Handlers; + +use App\Models\Device; +use LibreNMS\Enum\Severity; +use LibreNMS\Interfaces\SnmptrapHandler; +use LibreNMS\Snmptrap\Trap; + +class AxisAlarmNew implements SnmptrapHandler +{ + /** + * Handle snmptrap. + * Data is pre-parsed and delivered as a Trap. + * + * @param Device $device + * @param Trap $trap + * @return void + */ + public function handle(Device $device, Trap $trap) + { + $AlarmString = $trap->getOidData($trap->findOid('AXIS-VIDEO-MIB::alarmID')); + // Handle data type errors in translated trap + $AlarmID = preg_match('/^(?P.+?)?(\:\s)?(?P\d+)$/m', $AlarmString, $matches); + if (! empty($matches['value'])) { + $AlarmID = $matches['value']; + } else { + $AlarmID = $trap->getOidData($trap->findOid('AXIS-VIDEO-MIB::alarmID')); + } + + $AlarmName = $trap->getOidData($trap->findOid('AXIS-VIDEO-MIB::alarmName')); + $Message = $trap->getOidData($trap->findOid('AXIS-VIDEO-MIB::alarmText')); + + $trap->log("Axis Alarm Trap: Alarm ID $AlarmID: $AlarmName: $Message", Severity::Warning); + } +} diff --git a/LibreNMS/Snmptrap/Handlers/AxisAlarmSingle.php b/LibreNMS/Snmptrap/Handlers/AxisAlarmSingle.php new file mode 100644 index 000000000000..4448bbd0f462 --- /dev/null +++ b/LibreNMS/Snmptrap/Handlers/AxisAlarmSingle.php @@ -0,0 +1,59 @@ +. + * + * @link https://www.librenms.org + * + * @copyright 2024 Transitiv Technologies Ltd. + * @author Adam Sweet + */ + +namespace LibreNMS\Snmptrap\Handlers; + +use App\Models\Device; +use LibreNMS\Enum\Severity; +use LibreNMS\Interfaces\SnmptrapHandler; +use LibreNMS\Snmptrap\Trap; + +class AxisAlarmSingle implements SnmptrapHandler +{ + /** + * Handle snmptrap. + * Data is pre-parsed and delivered as a Trap. + * + * @param Device $device + * @param Trap $trap + * @return void + */ + public function handle(Device $device, Trap $trap) + { + $AlarmString = $trap->getOidData($trap->findOid('AXIS-VIDEO-MIB::alarmID')); + // Handle data type errors in translated trap + $AlarmID = preg_match('/^(?P.+?)?(\:\s)?(?P\d+)$/m', $AlarmString, $matches); + if (! empty($matches['value'])) { + $AlarmID = $matches['value']; + } else { + $AlarmID = $trap->getOidData($trap->findOid('AXIS-VIDEO-MIB::alarmID')); + } + + $AlarmName = $trap->getOidData($trap->findOid('AXIS-VIDEO-MIB::alarmName')); + $Message = $trap->getOidData($trap->findOid('AXIS-VIDEO-MIB::alarmText')); + + $trap->log("Axis Alarm Trap: Alarm ID $AlarmID: $AlarmName: $Message", Severity::Warning); + } +} diff --git a/LibreNMS/Util/Graph.php b/LibreNMS/Util/Graph.php index bea92d7e1d00..ee56981684f0 100644 --- a/LibreNMS/Util/Graph.php +++ b/LibreNMS/Util/Graph.php @@ -157,6 +157,8 @@ public static function get($vars): GraphImage require base_path('/includes/html/graphs/customoid/customoid.inc.php'); } elseif (is_file(base_path("/includes/html/graphs/$type/$subtype.inc.php"))) { require base_path("/includes/html/graphs/$type/$subtype.inc.php"); + } elseif (is_file(base_path("/includes/html/graphs/$type/generic.inc.php"))) { + require base_path("/includes/html/graphs/$type/generic.inc.php"); } else { throw new RrdGraphException("{$type}_$subtype template missing", "{$type}_$subtype missing", $width, $height); } diff --git a/LibreNMS/Validations/WebServer.php b/LibreNMS/Validations/WebServer.php index 180c1dceee09..969ab162993e 100644 --- a/LibreNMS/Validations/WebServer.php +++ b/LibreNMS/Validations/WebServer.php @@ -45,22 +45,28 @@ public function validate(Validator $validator): void { if (! app()->runningInConsole()) { $url = $this->removeStandardPorts(request()->url()); - $expected = $this->removeStandardPorts(Str::finish(Config::get('base_url'), '/') . 'validate/results'); + $base_url = Config::get('base_url'); + $expected = $this->removeStandardPorts(Str::finish($base_url, '/') . 'validate/results'); + $correct_base = str_replace('/validate/results', '', $url); if ($url !== $expected) { preg_match($this->host_regex, $url, $actual_host_match); preg_match($this->host_regex, $expected, $expected_host_match); $actual_host = $actual_host_match[1] ?? ''; $expected_host = $expected_host_match[1] ?? "parse failure ($expected)"; - if ($actual_host != $expected_host) { + + if ($base_url == '/' && ! str_contains($actual_host, '/') || ! Str::startsWith($base_url, 'http')) { + $validator->warn('base_url could be more specific', "lnms config:set base_url $correct_base"); + } elseif ($actual_host != $expected_host) { $nginx = Str::startsWith(request()->server->get('SERVER_SOFTWARE'), 'nginx'); $server_name = $nginx ? 'server_name' : 'ServerName'; $fix = $nginx ? "server_name $actual_host;" : "ServerName $actual_host"; $validator->fail("$server_name is set incorrectly for your webserver, update your webserver config. $actual_host $expected_host", $fix); } else { - $correct_base = str_replace('validate/results', '', $url); $validator->fail('base_url is not set correctly', "lnms config:set base_url $correct_base"); } + } elseif (preg_replace('#/$#', '', \config('app.url')) !== $correct_base) { + $validator->fail("APP_URL is not set correctly. It should be set to $correct_base"); } if (request()->secure() && ! \config('session.secure')) { diff --git a/app/Jobs/PollDevice.php b/app/Jobs/PollDevice.php index 3633119dc468..559d2a2d1872 100644 --- a/app/Jobs/PollDevice.php +++ b/app/Jobs/PollDevice.php @@ -4,6 +4,7 @@ use App\Events\DevicePolled; use App\Events\PollingDevice; +use App\Facades\LibrenmsConfig; use App\Models\Eventlog; use App\Polling\Measure\Measurement; use App\Polling\Measure\MeasurementManager; @@ -229,11 +230,16 @@ private function recordPerformance(Measurement $measurement): void private function getModules(): array { - if (! empty($this->module_overrides)) { - return $this->module_overrides; + $default_modules = LibrenmsConfig::get('poller_modules', []); + + if (empty($this->module_overrides)) { + return $default_modules; } - return \LibreNMS\Config::get('poller_modules', []); + // ensure order of modules + $ordered_modules = array_fill_keys(array_keys(array_intersect_key($default_modules, $this->module_overrides)), true); + + return $ordered_modules; } private function isModuleManuallyEnabled(string $module): ?bool diff --git a/app/Logging/LogFileFormatter.php b/app/Logging/LogFileFormatter.php new file mode 100644 index 000000000000..fdab9466487b --- /dev/null +++ b/app/Logging/LogFileFormatter.php @@ -0,0 +1,21 @@ + [ 'driver' => 'single', 'path' => env('APP_LOG', base_path('logs/librenms.log')), - 'formatter' => \App\Logging\NoColorFormatter::class, - 'level' => env('LOG_LEVEL', 'error'), + 'formatter' => \App\Logging\LogFileFormatter::class, + 'level' => env('LOG_LEVEL', 'warning'), 'replace_placeholders' => true, ], diff --git a/config/snmptraps.php b/config/snmptraps.php index 1714d93b61ad..7e6354122341 100755 --- a/config/snmptraps.php +++ b/config/snmptraps.php @@ -22,6 +22,9 @@ 'ALCATEL-IND1-LBD-MIB::alaLbdStateChangeToShutdown' => LibreNMS\Snmptrap\Handlers\Aos6LbdStateChangeToShutdown::class, 'ALCATEL-IND1-LBD-MIB::alaLbdStateChangeForAutoRecovery' => LibreNMS\Snmptrap\Handlers\Aos6LbdStateChangeForAutoRecovery::class, 'ALCATEL-IND1-AAA-MIB::aaaHicServerTrap' => LibreNMS\Snmptrap\Handlers\Aos6HicServerTrap::class, + 'AXIS-VIDEO-MIB::alarmCleared' => LibreNMS\Snmptrap\Handlers\AxisAlarmCleared::class, + 'AXIS-VIDEO-MIB::alarmNew' => LibreNMS\Snmptrap\Handlers\AxisAlarmNew::class, + 'AXIS-VIDEO-MIB::alarmSingle' => LibreNMS\Snmptrap\Handlers\AxisAlarmSingle::class, 'BGP4-MIB::bgpBackwardTransition' => LibreNMS\Snmptrap\Handlers\BgpBackwardTransition::class, 'BGP4-MIB::bgpEstablished' => LibreNMS\Snmptrap\Handlers\BgpEstablished::class, 'BGP4-V2-MIB-JUNIPER::jnxBgpM2BackwardTransition' => LibreNMS\Snmptrap\Handlers\JnxBgpM2BackwardTransition::class, diff --git a/doc/Extensions/Dispatcher-Service.md b/doc/Extensions/Dispatcher-Service.md index 1baf0143237c..6d90b085580c 100644 --- a/doc/Extensions/Dispatcher-Service.md +++ b/doc/Extensions/Dispatcher-Service.md @@ -134,7 +134,7 @@ Optional Settings lnms config:set service_poller_frequency 300 lnms config:set service_services_frequency 300 lnms config:set service_discovery_frequency 21600 - lnms config:set schedule_type.alert dispatcher + lnms config:set schedule_type.alerting dispatcher lnms config:set schedule_type.billing dispatcher lnms config:set service_billing_frequency 300 lnms config:set service_billing_calculate_frequency 60 diff --git a/html/images/logos/seh.svg b/html/images/logos/seh.svg new file mode 100644 index 000000000000..2c9855c00f99 --- /dev/null +++ b/html/images/logos/seh.svg @@ -0,0 +1,48 @@ + + + + + + + + + + + + + + + + + diff --git a/html/images/os/seh.svg b/html/images/os/seh.svg new file mode 100644 index 000000000000..2c9855c00f99 --- /dev/null +++ b/html/images/os/seh.svg @@ -0,0 +1,48 @@ + + + + + + + + + + + + + + + + + diff --git a/includes/common.php b/includes/common.php index aa9ce917ce98..cc514ec5c87a 100644 --- a/includes/common.php +++ b/includes/common.php @@ -242,22 +242,6 @@ function gethostbyid($device_id) return DeviceCache::get((int) $device_id)->hostname; } -function strgen($length = 16) -{ - $entropy = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 'a', 'A', 'b', 'B', 'c', 'C', 'd', 'D', 'e', - 'E', 'f', 'F', 'g', 'G', 'h', 'H', 'i', 'I', 'j', 'J', 'k', 'K', 'l', 'L', 'm', 'M', 'n', - 'N', 'o', 'O', 'p', 'P', 'q', 'Q', 'r', 'R', 's', 'S', 't', 'T', 'u', 'U', 'v', 'V', 'w', - 'W', 'x', 'X', 'y', 'Y', 'z', 'Z', ]; - $string = ''; - - for ($i = 0; $i < $length; $i++) { - $key = mt_rand(0, 61); - $string .= $entropy[$key]; - } - - return $string; -} - function getifbyid($id) { return dbFetchRow('SELECT * FROM `ports` WHERE `port_id` = ?', [$id]); @@ -626,24 +610,6 @@ function ResolveGlues($tables, $target, $x = 0, $hist = [], $last = []) return false; } -/** - * Determine if a given string contains a given substring. - * - * @param string $haystack - * @param string|array $needles - * @return bool - */ -function str_i_contains($haystack, $needles) -{ - foreach ((array) $needles as $needle) { - if ($needle != '' && stripos($haystack, $needle) !== false) { - return true; - } - } - - return false; -} - /** * Get alert_rules sql filter by minimal severity * diff --git a/includes/definitions/arista-mos.yaml b/includes/definitions/arista-mos.yaml index a80781ccb0cc..01ad66f0ce4f 100644 --- a/includes/definitions/arista-mos.yaml +++ b/includes/definitions/arista-mos.yaml @@ -15,6 +15,7 @@ discovery: - .1.3.6.1.4.1.43191.1.6.6 - .1.3.6.1.4.1.43191.1.6.7 - .1.3.6.1.4.1.43191.1.6.9 + - .1.3.6.1.4.1.43191.1.6.10 - .1.3.6.1.4.1.43191.1.7.5 - .1.3.6.1.4.1.43191.1.7.6 - .1.3.6.1.4.1.43191.1.7.7 diff --git a/includes/definitions/discovery/drac.yaml b/includes/definitions/discovery/drac.yaml index 288a6eb82bac..5e5e513a17a0 100644 --- a/includes/definitions/discovery/drac.yaml +++ b/includes/definitions/discovery/drac.yaml @@ -2,19 +2,39 @@ mib: DELL-RAC-MIB:IDRAC-MIB-SMIv2 modules: os: hardware: - - .1.3.6.1.4.1.674.10892.5.1.3.12.0 - - .1.3.6.1.4.1.674.10892.2.1.1.2.0 - version: .1.3.6.1.4.1.674.10892.2.1.2.1.0 - serial: .1.3.6.1.4.1.674.10892.2.1.1.11.0 - location: .1.3.6.1.4.1.674.10892.5.1.3.8.0 + - IDRAC-MIB-SMIv2::systemModelName.0 + - DELL-RAC-MIB::drsProductShortName.0 + version: + - IDRAC-MIB-SMIv2::racFirmwareVersion.0 + - DELL-RAC-MIB::drsFirmwareVersion.0 + serial: + - IDRAC-MIB-SMIv2::systemServiceTag.0 + - DELL-RAC-MIB::drsSystemServiceTag.0 + location: IDRAC-MIB-SMIv2::systemDataCenterName.0 sensors: + fanspeed: + data: + - + oid: IDRAC-MIB-SMIv2::coolingDeviceTable + value: IDRAC-MIB-SMIv2::coolingDeviceReading + num_oid: '.1.3.6.1.4.1.674.10892.5.4.700.12.1.6.{{ $index }}' + descr: IDRAC-MIB-SMIv2::coolingDeviceLocationName + index: '{{ $index }}' + high_limit: IDRAC-MIB-SMIv2::coolingDeviceUpperCriticalThreshold + low_limit: IDRAC-MIB-SMIv2::coolingDeviceLowerCriticalThreshold + # Following can be enabled with new testfiles for Poweredge R640, iDRAC7, Poweredge M1000e + # skip_values: + # - + # oid: IDRAC-MIB-SMIv2::coolingDeviceSubType + # op: 'not_in_array' + # value: [ 3, 5 ] state: data: - oid: drsGlobalSystemStatus value: drsGlobalSystemStatus num_oid: '.1.3.6.1.4.1.674.10892.2.2.1.{{ $index }}' - descr: 'Global System Status' + descr: 'Global System' index: 'drsGlobalSystemStatus.{{ $index }}' states: - { value: 1, descr: other, graph: 1, generic: 3 } @@ -27,7 +47,7 @@ modules: oid: systemStateIDSDMCardUnitStatusCombined value: systemStateIDSDMCardUnitStatusCombined num_oid: '.1.3.6.1.4.1.674.10892.5.4.200.10.1.58.{{ $index }}' - descr: 'IDSDM Card Unit Combined Status' + descr: 'IDSDM Card Unit Combined' index: 'systemStateIDSDMCardUnitStatusCombined.{{ $index }}' states: - { value: 1, descr: other, graph: 1, generic: 3 } @@ -40,7 +60,7 @@ modules: oid: systemStateIDSDMCardDeviceStatusCombined value: systemStateIDSDMCardDeviceStatusCombined num_oid: '.1.3.6.1.4.1.674.10892.5.4.200.10.1.60.{{ $index }}' - descr: 'IDSDM Card Device Combined Status' + descr: 'IDSDM Card Device Combined' index: 'systemStateIDSDMCardDeviceStatusCombined.{{ $index }}' states: - { value: 1, descr: other, graph: 1, generic: 3 } @@ -53,7 +73,7 @@ modules: oid: intrusionStatus value: intrusionStatus num_oid: '.1.3.6.1.4.1.674.10892.5.4.300.70.1.5.{{ $index }}' - descr: 'Intrusion Status' + descr: 'Intrusion' index: 'intrusionStatus.{{ $index }}' states: - { value: 1, descr: other, graph: 1, generic: 3 } @@ -77,7 +97,7 @@ modules: oid: drsIOMCurrStatus value: drsIOMCurrStatus num_oid: '.1.3.6.1.4.1.674.10892.2.3.1.2.{{ $index }}' - descr: 'IOM Status' + descr: 'IOM' index: 'drsIOMCurrStatus.{{ $index }}' states: - { value: 1, descr: other, graph: 1, generic: 3 } @@ -87,10 +107,10 @@ modules: - { value: 5, descr: critical, graph: 1, generic: 2 } - { value: 6, descr: nonRecoverable, graph: 1, generic: 2 } - - oid: drsKVMCurrStatus - value: drsKVMCurrStatus + oid: DELL-RAC-MIB::drsKVMCurrStatus + value: DELL-RAC-MIB::drsKVMCurrStatus num_oid: '.1.3.6.1.4.1.674.10892.2.3.1.3.{{ $index }}' - descr: 'KVM Status' + descr: 'KVM' index: 'drsKVMCurrStatus.{{ $index }}' states: - { value: 1, descr: other, graph: 1, generic: 3 } @@ -103,7 +123,7 @@ modules: oid: drsRedCurrStatus value: drsRedCurrStatus num_oid: '.1.3.6.1.4.1.674.10892.2.3.1.4.{{ $index }}' - descr: 'Redundancy Status' + descr: 'Redundancy' index: 'drsRedCurrStatus.{{ $index }}' states: - { value: 1, descr: other, graph: 1, generic: 3 } @@ -113,10 +133,10 @@ modules: - { value: 5, descr: critical, graph: 1, generic: 2 } - { value: 6, descr: nonRecoverable, graph: 1, generic: 2 } - - oid: drsPowerCurrStatus - value: drsPowerCurrStatus + oid: DELL-RAC-MIB::drsPowerCurrStatus + value: DELL-RAC-MIB::drsPowerCurrStatus num_oid: '.1.3.6.1.4.1.674.10892.2.3.1.5.{{ $index }}' - descr: 'Power Status' + descr: 'Power' index: 'drsPowerCurrStatus.{{ $index }}' states: - { value: 1, descr: other, graph: 1, generic: 3 } @@ -126,10 +146,10 @@ modules: - { value: 5, descr: critical, graph: 1, generic: 2 } - { value: 6, descr: nonRecoverable, graph: 1, generic: 2 } - - oid: drsFanCurrStatus + oid: DELL-RAC-MIB::drsFanCurrStatus value: drsFanCurrStatus num_oid: '.1.3.6.1.4.1.674.10892.2.3.1.6.{{ $index }}' - descr: 'FAN Status' + descr: 'FAN' index: 'drsFanCurrStatus.{{ $index }}' states: - { value: 1, descr: other, graph: 1, generic: 3 } @@ -139,9 +159,9 @@ modules: - { value: 5, descr: critical, graph: 1, generic: 2 } - { value: 6, descr: nonRecoverable, graph: 1, generic: 2 } - - oid: drsBladeCurrStatus + oid: DELL-RAC-MIB::drsBladeCurrStatus num_oid: '.1.3.6.1.4.1.674.10892.2.3.1.7.{{ $index }}' - descr: 'Blade Status' + descr: 'Blade' index: 'drsBladeCurrStatus.{{ $index }}' states: - { value: 1, descr: other, graph: 1, generic: 3 } @@ -151,9 +171,9 @@ modules: - { value: 5, descr: critical, graph: 1, generic: 2 } - { value: 6, descr: nonRecoverable, graph: 1, generic: 2 } - - oid: drsTempCurrStatus + oid: DELL-RAC-MIB::drsTempCurrStatus num_oid: '.1.3.6.1.4.1.674.10892.2.3.1.8.{{ $index }}' - descr: 'Temperature Status' + descr: 'Temperature' index: 'drsTempCurrStatus.{{ $index }}' states: - { value: 1, descr: other, graph: 1, generic: 3 } @@ -163,9 +183,9 @@ modules: - { value: 5, descr: critical, graph: 1, generic: 2 } - { value: 6, descr: nonRecoverable, graph: 1, generic: 2 } - - oid: drsCMCCurrStatus + oid: DELL-RAC-MIB::drsCMCCurrStatus num_oid: '.1.3.6.1.4.1.674.10892.2.3.1.9.{{ $index }}' - descr: 'CMC Status' + descr: 'CMC' index: 'drsCMCCurrStatus.{{ $index }}' states: - { value: 1, descr: other, graph: 1, generic: 3 } @@ -178,9 +198,10 @@ modules: oid: DELL-RAC-MIB::physicalDiskTable value: DELL-RAC-MIB::physicalDiskState num_oid: '.1.3.6.1.4.1.674.10892.2.6.1.20.130.4.1.4.{{ $index }}' - descr: '{{ $DELL-RAC-MIB::physicalDiskName }} State' - index: 'physicalDiskStatus.{{ $index }}' - state_name: DELL-RAC-MIB::physicalDiskTable + descr: '{{ $DELL-RAC-MIB::physicalDiskName }}' + index: '{{ $index }}' + group: 'Storage' + state_name: physicalDiskState states: - { value: 1, descr: unknown, graph: 1, generic: 3 } - { value: 2, descr: ready, graph: 1, generic: 0 } @@ -190,15 +211,48 @@ modules: - { value: 6, descr: blocked, graph: 1, generic: 2} - { value: 7, descr: failed, graph: 1, generic: 2 } - { value: 8, descr: nonraid, graph: 1, generic: 0 } - - { value: 9, descr: nonrecoverable, graph: 1, generic: 2 } + - { value: 9, descr: removed, graph: 1, generic: 2 } - { value: 10, descr: readonly, graph: 1, generic: 0 } + - + oid: IDRAC-MIB-SMIv2::physicalDiskTable + value: IDRAC-MIB-SMIv2::physicalDiskState + num_oid: '.1.3.6.1.4.1.674.10892.5.5.1.20.130.4.1.4.{{ $index }}' + descr: '{{ $IDRAC-MIB-SMIv2::physicalDiskName }}' + index: '{{ $index }}' + group: 'Storage' + state_name: physicalDiskState + states: + - { value: 1, descr: unknown, graph: 1, generic: 3 } + - { value: 2, descr: ready, graph: 1, generic: 0 } + - { value: 3, descr: online, graph: 1, generic: 0 } + - { value: 4, descr: foreign, graph: 1, generic: 1 } + - { value: 5, descr: offline, graph: 1, generic: 2 } + - { value: 6, descr: blocked, graph: 1, generic: 2} + - { value: 7, descr: failed, graph: 1, generic: 2 } + - { value: 8, descr: nonraid, graph: 1, generic: 0 } + - { value: 9, descr: removed, graph: 1, generic: 2 } + - { value: 10, descr: readonly, graph: 1, generic: 0 } + - + oid: IDRAC-MIB-SMIv2::virtualDiskTable + value: IDRAC-MIB-SMIv2::virtualDiskState + num_oid: '.1.3.6.1.4.1.674.10892.5.5.1.20.140.1.1.4.{{ $index }}' + descr: '{{ $IDRAC-MIB-SMIv2::virtualDiskName }}' + index: '{{ $index }}' + group: 'Storage' + state_name: virtualDiskState + states: + - { value: 1, descr: unknown, graph: 0, generic: 3 } + - { value: 2, descr: online, graph: 0, generic: 0 } + - { value: 3, descr: failed, graph: 0, generic: 2 } + - { value: 4, descr: degraded, graph: 0, generic: 1 } - oid: DELL-RAC-MIB::controllerTable value: DELL-RAC-MIB::controllerComponentStatus num_oid: '.1.3.6.1.4.1.674.10892.2.6.1.20.130.1.1.38.{{ $index }}' - descr: '{{ $DELL-RAC-MIB::controllerName }} {{ $DELL-RAC-MIB::controllerDisplayName }} State' + descr: '{{ $DELL-RAC-MIB::controllerName }} {{ $DELL-RAC-MIB::controllerDisplayName }}' index: 'controllerComponentStatus.{{ $index }}' - state_name: DELL-RAC-MIB::controllerTable + group: 'Storage' + state_name: controllerState states: - { value: 1, descr: other, graph: 1, generic: 3 } - { value: 2, descr: unknown, graph: 1, generic: 3 } @@ -206,20 +260,119 @@ modules: - { value: 4, descr: nonCritical, graph: 1, generic: 1 } - { value: 5, descr: critical, graph: 1, generic: 2 } - { value: 6, descr: nonrecoverable, graph: 1, generic: 2} + - + oid: IDRAC-MIB-SMIv2::controllerTable + value: IDRAC-MIB-SMIv2::controllerComponentStatus + num_oid: '.1.3.6.1.4.1.674.10892.5.5.1.20.130.1.1.38.{{ $index }}' + descr: '{{ $IDRAC-MIB-SMIv2::controllerName }} {{ $IDRAC-MIB-SMIv2::controllerDisplayName }}' + index: 'controllerComponentStatus.{{ $index }}' + group: 'Storage' + state_name: controllerState + states: + - { value: 1, descr: other, graph: 0, generic: 3 } + - { value: 2, descr: unknown, graph: 0, generic: 3 } + - { value: 3, descr: ok, graph: 0, generic: 0 } + - { value: 4, descr: nonCritical, graph: 0, generic: 1 } + - { value: 5, descr: critical, graph: 0, generic: 2 } + - { value: 6, descr: nonRecoverable, graph: 0, generic: 2} + - + oid: IDRAC-MIB-SMIv2::processorDeviceTable + value: IDRAC-MIB-SMIv2::processorDeviceStatus + num_oid: '.1.3.6.1.4.1.674.10892.5.4.1100.30.1.5.{{ $index }}' + descr: '{{ $IDRAC-MIB-SMIv2::processorDeviceBrandName }}' + index: '{{ $index }}' + state_name: processorDeviceStatus + states: + - { value: 1, descr: other, graph: 0, generic: 3 } + - { value: 2, descr: unknown, graph: 0, generic: 3 } + - { value: 3, descr: ok, graph: 0, generic: 0 } + - { value: 4, descr: nonCritical, graph: 0, generic: 1 } + - { value: 5, descr: critical, graph: 0, generic: 2 } + - { value: 6, descr: nonRecoverable, graph: 0, generic: 2} + - + oid: IDRAC-MIB-SMIv2::memoryDeviceTable + value: IDRAC-MIB-SMIv2::memoryDeviceStatus + num_oid: '.1.3.6.1.4.1.674.10892.5.4.1100.50.1.5.{{ $index }}' + descr: '{{ $IDRAC-MIB-SMIv2::memoryDeviceLocationName }}' + index: '{{ $index }}' + group: 'Memory' + state_name: memoryDeviceStatus + states: + - { value: 1, descr: other, graph: 0, generic: 3 } + - { value: 2, descr: unknown, graph: 0, generic: 3 } + - { value: 3, descr: ok, graph: 0, generic: 0 } + - { value: 4, descr: nonCritical, graph: 0, generic: 1 } + - { value: 5, descr: critical, graph: 0, generic: 2 } + - { value: 6, descr: nonRecoverable, graph: 0, generic: 2} + - + oid: IDRAC-MIB-SMIv2::voltageProbeTable + value: IDRAC-MIB-SMIv2::voltageProbeStatus + num_oid: '.1.3.6.1.4.1.674.10892.5.4.600.20.1.5.{{ $index }}' + descr: '{{ $IDRAC-MIB-SMIv2::voltageProbeLocationName }}' + index: '{{ $index }}' + state_name: voltageProbeStatus + states: + - { value: 1, descr: other, graph: 0, generic: 3 } + - { value: 2, descr: unknown, graph: 0, generic: 3 } + - { value: 3, descr: ok, graph: 0, generic: 0 } + - { value: 4, descr: nonCriticalUpper, graph: 0, generic: 1 } + - { value: 5, descr: criticalUpper, graph: 0, generic: 2 } + - { value: 6, descr: nonRecoverableUpper, graph: 0, generic: 2 } + - { value: 7, descr: nonCriticalLower, graph: 0, generic: 1 } + - { value: 8, descr: criticalLower, graph: 0, generic: 2 } + - { value: 9, descr: nonRecoverableLower, graph: 0, generic: 2 } + - { value: 10, descr: failed, graph: 0, generic: 2 } + - + oid: IDRAC-MIB-SMIv2::amperageProbeTable + value: IDRAC-MIB-SMIv2::amperageProbeStatus + num_oid: '.1.3.6.1.4.1.674.10892.5.4.600.30.1.5.{{ $index }}' + descr: '{{ $IDRAC-MIB-SMIv2::amperageProbeLocationName }}' + index: '{{ $index }}' + state_name: amperageProbeStatus + states: + - { value: 1, descr: other, graph: 0, generic: 3 } + - { value: 2, descr: unknown, graph: 0, generic: 3 } + - { value: 3, descr: ok, graph: 0, generic: 0 } + - { value: 4, descr: nonCriticalUpper, graph: 0, generic: 1 } + - { value: 5, descr: criticalUpper, graph: 0, generic: 2 } + - { value: 6, descr: nonRecoverableUpper, graph: 0, generic: 2 } + - { value: 7, descr: nonCriticalLower, graph: 0, generic: 1 } + - { value: 8, descr: criticalLower, graph: 0, generic: 2 } + - { value: 9, descr: nonRecoverableLower, graph: 0, generic: 2 } + - { value: 10, descr: failed, graph: 0, generic: 2 } + - + oid: IDRAC-MIB-SMIv2::systemBatteryTable + value: IDRAC-MIB-SMIv2::systemBatteryStatus + num_oid: '.1.3.6.1.4.1.674.10892.5.4.600.50.1.5.{{ $index }}' + descr: '{{ $IDRAC-MIB-SMIv2::systemBatteryLocationName }}' + index: '{{ $index }}' + state_name: systemBatteryStatus + states: + - { value: 1, descr: other, graph: 0, generic: 3 } + - { value: 2, descr: unknown, graph: 0, generic: 3 } + - { value: 3, descr: ok, graph: 0, generic: 0 } + - { value: 4, descr: nonCriticalUpper, graph: 0, generic: 1 } + - { value: 5, descr: criticalUpper, graph: 0, generic: 2 } + - { value: 6, descr: nonRecoverableUpper, graph: 0, generic: 2 } + - { value: 7, descr: nonCriticalLower, graph: 0, generic: 1 } + - { value: 8, descr: criticalLower, graph: 0, generic: 2 } + - { value: 9, descr: nonRecoverableLower, graph: 0, generic: 2 } + - { value: 10, descr: failed, graph: 0, generic: 2 } - oid: drsCMCServerTable value: drsServerMonitoringCapable num_oid: '.1.3.6.1.4.1.674.10892.2.5.1.1.2.{{ $index }}' - descr: 'Slot {{ $drsServerSlotNumber }} {{ $drsServerSlotName }} {{ $drsServerModel }} State' - index: 'drsServerMonitoringCapable.{{ $index }}' + descr: 'Slot {{ $drsServerSlotNumber }} {{ $drsServerModel }}' + index: '{{ $index }}' + state_name: drsServerMonitoringCapable skip_values: - oid: drsServerSlotName op: '==' value: 'N/A' states: - - { value: 1, descr: absent, graph: 1, generic: 0 } - - { value: 2, descr: none, graph: 1, generic: 0 } + - { value: 1, descr: absent, graph: 1, generic: 3 } + - { value: 2, descr: none, graph: 1, generic: 3 } - { value: 3, descr: basic, graph: 1, generic: 0 } - { value: 4, descr: off l, graph: 1, generic: 1 } temperature: @@ -229,7 +382,7 @@ modules: value: drsChassisFrontPanelAmbientTemperature num_oid: '.1.3.6.1.4.1.674.10892.2.3.1.10.{{ $index }}' index: 'drsChassisFrontPanelAmbientTemperature.{{ $index }}' - descr: 'Chassis {{ $drsChassisIndex }} Front Panel Temperature' + descr: 'Chassis Front Panel Temperature' - oid: drsCMCAmbientTemperature value: drsCMCAmbientTemperature @@ -247,8 +400,6 @@ modules: value: temperatureProbeReading num_oid: '.1.3.6.1.4.1.674.10892.5.4.700.20.1.6.{{ $index }}' index: '{{ $index }}' - # Better but break graphs - #index: 'temperatureProbeReading.{{ $index }}' descr: temperatureProbeLocationName divisor: 10 high_limit: temperatureProbeUpperCriticalThreshold @@ -262,6 +413,13 @@ modules: value: drsPSUVoltsReading num_oid: '.1.3.6.1.4.1.674.10892.2.4.2.1.5.{{ $index }}' descr: 'Chassis {{ $drsPSUChassisIndex }} PS-{{ $drsPSUIndex }}' + - + oid: powerSupplyTable + value: powerSupplyCurrentInputVoltage + num_oid: '.1.3.6.1.4.1.674.10892.5.4.600.12.1.16.{{ $index }}' + index: 'powerSupplyCurrentInputVoltage.{{ $index }}' + descr: powerSupplyLocationName + high_limit: powerSupplyMaximumInputVoltage current: data: - @@ -285,7 +443,6 @@ modules: oid: amperageProbeType op: '!=' value: 23 - power: data: - @@ -334,3 +491,4 @@ modules: oid: amperageProbeType op: '!=' value: 26 + diff --git a/includes/definitions/discovery/exos.yaml b/includes/definitions/discovery/exos.yaml index 45ef0c173d79..cfefe9be3f5f 100644 --- a/includes/definitions/discovery/exos.yaml +++ b/includes/definitions/discovery/exos.yaml @@ -13,6 +13,7 @@ modules: descr: 'Connectivity Unit State' index: 'connUnitState.{{ $index }}' state_name: connUnitState + snmp_flags: ['-OteQUsab'] states: - { value: 2, generic: 0, graph: 0, descr: Online } - { value: 3, generic: 2, graph: 0, descr: Offline } @@ -24,6 +25,7 @@ modules: descr: 'Connectivity Unit Status' index: 'connUnitStatus.{{ $index }}' state_name: connUnitStatus + snmp_flags: ['-OteQUsab'] states: - { value: 1, generic: 3, graph: 0, descr: none } - { value: 2, generic: 3, graph: 0, descr: Unused } @@ -37,6 +39,12 @@ modules: descr: 'Principal Unit' index: 'connUnitPrincipal.{{ $index }}' state_name: connUnitPrincipal + snmp_flags: ['-OteQUsab'] + skip_values: + - + oid: connUnitPrincipal + op: '=' + value: 1 states: - { value: 1, generic: 3, graph: 0, descr: Unknown } - { value: 2, generic: 0, graph: 0, descr: No } @@ -48,6 +56,7 @@ modules: descr: '{{ $connUnitSensorName }}' index: 'connUnitSensorName.{{ $index }}' state_name: connUnitSensorName + snmp_flags: ['-OteQUsab'] states: - { value: 1, generic: 3, graph: 0, descr: Unknown } - { value: 2, generic: 3, graph: 0, descr: Other } diff --git a/includes/definitions/discovery/seh-uds.yaml b/includes/definitions/discovery/seh-uds.yaml new file mode 100644 index 000000000000..6c8c6ec49073 --- /dev/null +++ b/includes/definitions/discovery/seh-uds.yaml @@ -0,0 +1,30 @@ +mib: SEH-PSRV-MIB +modules: + os: + sysDescr_regex: '/SEH (?\S+); SN (?\S+), .* SW (?\S+)/' + sensors: + state: + data: + - + oid: utnPortTable + value: utnPortSlot + num_oid: '.1.3.6.1.4.1.1229.2.50.2.1.27.{{ $index }}' + descr: 'Port{{ utnPortIndex }}: {{ utnPortUsbOwn }}' + group: USB Port + index: 'utnPortIndex.{{ $index }}' + skip_values: + - + oid: utnPortTag + op: 'exists' + value: false + state_name: utnPortSlot + states: + - { value: 0, generic: 3, graph: 0, descr: disabled } + - { value: 1, generic: 0, graph: 0, descr: enabled } + - { value: 2, generic: 0, graph: 0, descr: enabled } + - { value: 3, generic: 0, graph: 0, descr: enabled } + - { value: 4, generic: 0, graph: 0, descr: enabled } + - { value: 5, generic: 0, graph: 0, descr: enabled } + - { value: 6, generic: 0, graph: 0, descr: enabled } + - { value: 7, generic: 0, graph: 0, descr: enabled } + - { value: 8, generic: 0, graph: 0, descr: enabled } diff --git a/includes/definitions/seh-uds.yaml b/includes/definitions/seh-uds.yaml new file mode 100644 index 000000000000..3994c9cc8e1b --- /dev/null +++ b/includes/definitions/seh-uds.yaml @@ -0,0 +1,8 @@ +os: seh-uds +text: 'SEH' +type: appliance +icon: seh +mib_dir: seh +discovery: + - sysObjectID: + - .1.3.6.1.4.1.1229 diff --git a/includes/discovery/sensors/current/exos.inc.php b/includes/discovery/sensors/current/exos.inc.php index ae3d565f3842..810cb9ee5ffa 100644 --- a/includes/discovery/sensors/current/exos.inc.php +++ b/includes/discovery/sensors/current/exos.inc.php @@ -6,7 +6,7 @@ foreach ($pre_cache['exos']['connUnitSensorTable'] as $index => $entry) { if (preg_match('/Current.* ([: 0-9\.]+A)/', $entry['connUnitSensorMessage'], $temp_value)) { $value = str_replace('A', '', $temp_value[1]); - discover_sensor($valid['sensor'], 'current', $device, ".1.3.6.1.3.94.1.8.1.6.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.{$entry['connUnitSensorIndex']}", $entry['connUnitSensorIndex'], 'exos', $entry['connUnitSensorName'], 1, '1', null, null, null, null, $value); + discover_sensor($valid['sensor'], 'current', $device, ".1.3.6.1.3.94.1.8.1.6.{$index}", $entry['connUnitSensorIndex'], 'exos', $entry['connUnitSensorName'], 1, '1', null, null, null, null, $value); } } } diff --git a/includes/discovery/sensors/fanspeed/drac.inc.php b/includes/discovery/sensors/fanspeed/drac.inc.php deleted file mode 100644 index f6cc8707f55d..000000000000 --- a/includes/discovery/sensors/fanspeed/drac.inc.php +++ /dev/null @@ -1,27 +0,0 @@ - - * This program is free software: you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation, either version 3 of the License, or (at your - * option) any later version. Please see LICENSE.txt at the top level of - * the source code distribution for details. - */ - -$tables = [ - ['physicalDiskTable', '.1.3.6.1.4.1.674.10892.5.5.1.20.130.4.1.4.', 'physicalDiskState', 'physicalDiskName'], - ['virtualDiskTable', '.1.3.6.1.4.1.674.10892.5.5.1.20.140.1.1.4.', 'virtualDiskState', 'virtualDiskName'], - ['processorDeviceTable', '.1.3.6.1.4.1.674.10892.5.4.1100.30.1.5.', 'processorDeviceStatus', 'processorDeviceBrandName'], - ['memoryDeviceTable', '.1.3.6.1.4.1.674.10892.5.4.1100.50.1.5.', 'memoryDeviceStatus', 'memoryDeviceLocationName'], - ['voltageProbeTable', '.1.3.6.1.4.1.674.10892.5.4.600.20.1.5.', 'voltageProbeStatus', 'voltageProbeLocationName'], - ['amperageProbeTable', '.1.3.6.1.4.1.674.10892.5.4.600.30.1.5.', 'amperageProbeStatus', 'amperageProbeLocationName'], - ['systemBatteryTable', '.1.3.6.1.4.1.674.10892.5.4.600.50.1.5.', 'systemBatteryStatus', 'systemBatteryLocationName'], -]; - -foreach ($tables as [$table_oid, $num_oid, $value_oid, $descr_oid]) { - $temp = snmpwalk_cache_multi_oid($device, $table_oid, [], 'IDRAC-MIB-SMIv2', null, '-OQUse'); - // '-OQUsetX' - - if (! empty($temp)) { - // Find the right states - if ($value_oid == 'physicalDiskTable') { - $states = [ - ['value' => 1, 'generic' => 3, 'graph' => 0, 'descr' => 'unknown'], - ['value' => 2, 'generic' => 0, 'graph' => 0, 'descr' => 'ready'], - ['value' => 3, 'generic' => 0, 'graph' => 0, 'descr' => 'online'], - ['value' => 4, 'generic' => 1, 'graph' => 0, 'descr' => 'foreign'], - ['value' => 5, 'generic' => 2, 'graph' => 0, 'descr' => 'offline'], - ['value' => 6, 'generic' => 2, 'graph' => 0, 'descr' => 'blocked'], - ['value' => 7, 'generic' => 2, 'graph' => 0, 'descr' => 'failed'], - ['value' => 8, 'generic' => 2, 'graph' => 0, 'descr' => 'noraid'], - ['value' => 9, 'generic' => 2, 'graph' => 0, 'descr' => 'removed'], - ['value' => 10, 'generic' => 2, 'graph' => 0, 'descr' => 'readonly'], - ]; - } elseif ($value_oid == 'virtualDiskState') { - $states = [ - ['value' => 1, 'generic' => 3, 'graph' => 0, 'descr' => 'unknown'], - ['value' => 2, 'generic' => 0, 'graph' => 0, 'descr' => 'online'], - ['value' => 3, 'generic' => 2, 'graph' => 0, 'descr' => 'failed'], - ['value' => 4, 'generic' => 1, 'graph' => 0, 'descr' => 'degraded'], - ]; - } elseif ($value_oid == 'processorDeviceStatus' || $value_oid == 'memoryDeviceStatus') { - $states = [ - ['value' => 1, 'generic' => 3, 'graph' => 0, 'descr' => 'other'], - ['value' => 2, 'generic' => 3, 'graph' => 0, 'descr' => 'unknown'], - ['value' => 3, 'generic' => 0, 'graph' => 0, 'descr' => 'ok'], - ['value' => 4, 'generic' => 1, 'graph' => 0, 'descr' => 'nonCritical'], - ['value' => 5, 'generic' => 2, 'graph' => 0, 'descr' => 'critical'], - ['value' => 6, 'generic' => 2, 'graph' => 0, 'descr' => 'nonRecoverable'], - ]; - } elseif ($value_oid == 'voltageProbeStatus' || $value_oid == 'amperageProbeStatus' || $value_oid == 'systemBatteryStatus') { - $states = [ - ['value' => 1, 'generic' => 3, 'graph' => 0, 'descr' => 'other'], - ['value' => 2, 'generic' => 3, 'graph' => 0, 'descr' => 'unknown'], - ['value' => 3, 'generic' => 0, 'graph' => 0, 'descr' => 'ok'], - ['value' => 4, 'generic' => 1, 'graph' => 0, 'descr' => 'nonCriticalUpper'], - ['value' => 5, 'generic' => 2, 'graph' => 0, 'descr' => 'criticalUpper'], - ['value' => 6, 'generic' => 2, 'graph' => 0, 'descr' => 'nonRecoverableUpper'], - ['value' => 7, 'generic' => 1, 'graph' => 0, 'descr' => 'nonCriticalLower'], - ['value' => 8, 'generic' => 2, 'graph' => 0, 'descr' => 'criticalLower'], - ['value' => 9, 'generic' => 2, 'graph' => 0, 'descr' => 'nonRecoverableLower'], - ['value' => 10, 'generic' => 2, 'graph' => 0, 'descr' => 'failed'], - ]; - } - - // Create State Index - create_state_index($value_oid, $states ?? []); - - foreach ($temp as $index => $entry) { - //Discover Sensors - if (isset($entry[$descr_oid])) { - if ($value_oid == 'memoryDeviceStatus') { - $descr = $entry[$descr_oid] . ', ' . $entry['memoryDeviceSize'] / 1024 . ' MB'; - } else { - $descr = $entry[$descr_oid]; - } - discover_sensor( - null, - 'state', - $device, - $num_oid . $index, - $index, - $value_oid, - $descr, - 1, - 1, - null, - null, - null, - null, - $entry[$value_oid], - 'snmp', - $index - ); - } - - //Create Sensor To State Index - create_sensor_to_state_index($device, $value_oid, $index); - } - } -} diff --git a/includes/discovery/sensors/temperature/exos.inc.php b/includes/discovery/sensors/temperature/exos.inc.php index c5d8632f1817..50ebf06cd127 100644 --- a/includes/discovery/sensors/temperature/exos.inc.php +++ b/includes/discovery/sensors/temperature/exos.inc.php @@ -6,7 +6,7 @@ foreach ($pre_cache['exos']['connUnitSensorTable'] as $index => $entry) { if (preg_match('/ Temp.* ([: 0-9]+ C)/', $entry['connUnitSensorMessage'], $temp_value)) { [$value, $dump] = explode(' ', $temp_value[1]); - discover_sensor($valid['sensor'], 'temperature', $device, ".1.3.6.1.3.94.1.8.1.6.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.{$entry['connUnitSensorIndex']}", $entry['connUnitSensorIndex'], 'exos', $entry['connUnitSensorName'], 1, '1', null, null, null, null, $value); + discover_sensor($valid['sensor'], 'temperature', $device, ".1.3.6.1.3.94.1.8.1.6.{$index}", $entry['connUnitSensorIndex'], 'exos', $entry['connUnitSensorName'], 1, '1', null, null, null, null, $value); } } } diff --git a/includes/discovery/sensors/voltage/drac.inc.php b/includes/discovery/sensors/voltage/drac.inc.php deleted file mode 100644 index 524d615ad814..000000000000 --- a/includes/discovery/sensors/voltage/drac.inc.php +++ /dev/null @@ -1,25 +0,0 @@ - $entry) { if (preg_match('/Voltage.* ([: 0-9\.]+V)/', $entry['connUnitSensorMessage'], $temp_value)) { $value = str_replace('V', '', $temp_value[1]); - discover_sensor($valid['sensor'], 'voltage', $device, ".1.3.6.1.3.94.1.8.1.6.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.{$entry['connUnitSensorIndex']}", $entry['connUnitSensorIndex'], 'exos', $entry['connUnitSensorName'], 1, '1', null, null, null, null, $value); + discover_sensor($valid['sensor'], 'voltage', $device, ".1.3.6.1.3.94.1.8.1.6.{$index}", $entry['connUnitSensorIndex'], 'exos', $entry['connUnitSensorName'], 1, '1', null, null, null, null, $value); } } } diff --git a/includes/functions.php b/includes/functions.php index b88b80f3196c..b429eb37b9ce 100755 --- a/includes/functions.php +++ b/includes/functions.php @@ -8,6 +8,7 @@ * @copyright (C) 2006 - 2012 Adam Armstrong */ +use App\Models\Device; use App\Models\StateTranslation; use Illuminate\Support\Str; use LibreNMS\Config; @@ -70,11 +71,6 @@ function logfile($string) fclose($fd); } -function percent_colour($perc) -{ - return \LibreNMS\Util\Color::percent(percent: $perc); -} - /** * @param $device * @return string the path to the icon image for this device. Close to square. @@ -286,12 +282,12 @@ function is_port_valid($port, $device) $ifType = $port['ifType']; $ifOperStatus = $port['ifOperStatus'] ?? ''; - if (str_i_contains($ifDescr, Config::getOsSetting($device['os'], 'good_if', Config::get('good_if')))) { + if (Str::contains($ifDescr, Config::getOsSetting($device['os'], 'good_if', Config::get('good_if')), ignoreCase: true)) { return true; } foreach (Config::getCombined($device['os'], 'bad_if') as $bi) { - if (str_i_contains($ifDescr, $bi)) { + if (Str::contains($ifDescr, $bi, ignoreCase: true)) { d_echo("ignored by ifDescr: $ifDescr (matched: $bi)\n"); return false; @@ -376,22 +372,6 @@ function port_fill_missing_and_trim(&$port, $device) } } -function validate_device_id($id) -{ - if (empty($id) || ! is_numeric($id)) { - $return = false; - } else { - $device_id = dbFetchCell('SELECT `device_id` FROM `devices` WHERE `device_id` = ?', [$id]); - if ($device_id == $id) { - $return = true; - } else { - $return = false; - } - } - - return $return; -} - function convert_delay($delay) { if (preg_match('/(\d+)([mhd]?)/', $delay, $matches)) { @@ -430,29 +410,14 @@ function fix_integer_value($value) /** * Checks if the $hostname provided exists in the DB already - * - * @param string $hostname The hostname to check for - * @param string $sysName The sysName to check - * @return bool true if hostname already exists - * false if hostname doesn't exist */ -function host_exists($hostname, $sysName = null) +function host_exists(string $hostname, ?string $sysName = null): bool { - $query = 'SELECT COUNT(*) FROM `devices` WHERE `hostname`=?'; - $params = [$hostname]; - - if (! empty($sysName) && ! Config::get('allow_duplicate_sysName')) { - $query .= ' OR `sysName`=?'; - $params[] = $sysName; - - if (! empty(Config::get('mydomain'))) { - $full_sysname = rtrim($sysName, '.') . '.' . Config::get('mydomain'); - $query .= ' OR `sysName`=?'; - $params[] = $full_sysname; - } - } - - return dbFetchCell($query, $params) > 0; + return Device::where('hostname', $hostname) + ->when(! empty($sysName), function ($query) use ($sysName) { + $query->when(! Config::get('allow_duplicate_sysName'), fn ($q) => $q->orWhere('sysName', $sysName)) + ->when(! empty(Config::get('mydomain')), fn ($q) => $q->orWhere('sysName', rtrim($sysName, '.') . '.' . Config::get('mydomain'))); + })->exists(); } /** diff --git a/includes/html/forms/application-update.inc.php b/includes/html/forms/application-update.inc.php index 8b13752761a3..dc8640be10ad 100644 --- a/includes/html/forms/application-update.inc.php +++ b/includes/html/forms/application-update.inc.php @@ -22,25 +22,27 @@ * @copyright 2017 Tony Murray * @author Tony Murray */ -use App\Models\Application; +use App\Models\Device; +use App\Models\Eventlog; +use LibreNMS\Enum\Severity; if (! Auth::user()->hasGlobalAdmin()) { $status = ['status' => 1, 'message' => 'You need to be admin']; } else { - $device_id = $_POST['device_id']; + $device = Device::find($_POST['device_id']); $app = $_POST['application']; - if (! isset($app) && validate_device_id($device_id) === false) { + if (! isset($app) && $device === null) { $status = ['status' => 1, 'message' => 'Error with data']; } else { $status = ['status' => 1, 'message' => 'Database update failed']; - $app = Application::withTrashed()->firstOrNew(['device_id' => $device_id, 'app_type' => $app]); + $app = $device->applications()->withTrashed()->firstOrNew(['app_type' => $app]); if ($_POST['state'] == 'true') { if ($app->trashed()) { $app->restore(); } if ($app->save()) { - log_event("Application enabled by user: $app", $device_id, 'application', 1); + Eventlog::log('Application enabled by user ' . Auth::user()->username . ': ' . $app, $device->device_id, 'application', Severity::Ok); $status = ['status' => 0, 'message' => 'Application enabled']; } else { $status = ['status' => 1, 'message' => 'Database update for enabling the application failed']; @@ -48,7 +50,7 @@ } else { $app->delete(); if ($app->save()) { - log_event("Application disabled by user: $app", $device_id, 'application', 3); + Eventlog::log('Application disabled by user ' . Auth::user()->username . ': ' . $app, $device->device_id, 'application', Severity::Notice); $status = ['status' => 0, 'message' => 'Application disabled']; } else { $status = ['status' => 1, 'message' => 'Database update for disabling the application failed']; diff --git a/includes/html/forms/create-alert-item.inc.php b/includes/html/forms/create-alert-item.inc.php index 833380a16848..7c691a3ad36a 100644 --- a/includes/html/forms/create-alert-item.inc.php +++ b/includes/html/forms/create-alert-item.inc.php @@ -12,6 +12,7 @@ * the source code distribution for details. */ +use App\Models\Device; use LibreNMS\Alert\AlertDB; if (! Auth::user()->hasGlobalAdmin()) { @@ -33,7 +34,7 @@ if (empty($rule)) { $update_message = 'ERROR: No rule was generated - did you forget to click and / or?'; -} elseif (validate_device_id($_POST['device_id']) || $_POST['device_id'] == '-1' || $_POST['device_id'][0] == ':') { +} elseif (Device::where('device_id', $_POST['device_id'])->exists() || $_POST['device_id'] == '-1' || $_POST['device_id'][0] == ':') { $device_id = $_POST['device_id']; if (! is_numeric($count)) { $count = '-1'; diff --git a/includes/html/forms/customoid.inc.php b/includes/html/forms/customoid.inc.php index 56cd45b99241..91731afdd6d3 100644 --- a/includes/html/forms/customoid.inc.php +++ b/includes/html/forms/customoid.inc.php @@ -39,7 +39,7 @@ $oid_value = $rawdata; } elseif ( ! empty($_POST['unit']) && - str_i_contains($rawdata, $unit) && + Str::contains($rawdata, $unit, ignoreCase: true) && is_numeric(trim(str_replace($unit, '', $rawdata))) ) { $oid_value = trim(str_replace($unit, '', $rawdata)); diff --git a/includes/html/functions.inc.php b/includes/html/functions.inc.php index ff95156da188..903cb6bdee31 100644 --- a/includes/html/functions.inc.php +++ b/includes/html/functions.inc.php @@ -754,7 +754,7 @@ function alert_details($details) if (in_array($k, ['device_id', 'sysObjectID', 'sysDescr', 'location_id'])) { continue; } - if (! empty($v) && str_i_contains($k, ['id', 'desc', 'msg', 'last'])) { + if (! empty($v) && Str::contains($k, ['id', 'desc', 'msg', 'last'], ignoreCase: true)) { $fault_detail_data[] = "$k => '$v'"; } } diff --git a/includes/html/graphs/graph.inc.php b/includes/html/graphs/graph.inc.php index 1b7f74dfd68a..9a8d9efe396f 100644 --- a/includes/html/graphs/graph.inc.php +++ b/includes/html/graphs/graph.inc.php @@ -1,5 +1,6 @@ "; echo "

RRDTool Command

"; echo "
";
-        echo escapeshellcmd('rrdtool ' . Rrd::buildCommand('graph', Config::get('temp_dir') . '/' . strgen(), $rrd_options));
+        echo escapeshellcmd('rrdtool ' . Rrd::buildCommand('graph', Config::get('temp_dir') . '/' . Str::random(), $rrd_options));
         echo '
'; try { Rrd::graph($rrd_options, $env); diff --git a/includes/polling/customoid.inc.php b/includes/polling/customoid.inc.php index 84414dc76c7f..75c5401455dd 100644 --- a/includes/polling/customoid.inc.php +++ b/includes/polling/customoid.inc.php @@ -24,7 +24,7 @@ $oid_value = $rawdata; } elseif ( $unit && - str_i_contains($rawdata, $unit) && + Str::contains($rawdata, $unit, ignoreCase: true) && is_numeric(trim(str_replace($unit, '', $rawdata))) ) { $os->enableGraph('customoid'); diff --git a/lang/zh-TW.json b/lang/zh-TW.json index ea27d64001a7..eac031cacbbf 100644 --- a/lang/zh-TW.json +++ b/lang/zh-TW.json @@ -1,465 +1,659 @@ { - "Server Name": "伺æœå™¨å稱", - "Power Status": "é›»æºç‹€æ…‹", - "Services Templates": "æœå‹™ç¯„本", - "New Service Template": "新增æœå‹™ç¯„本", + " (Read)": " (讀å–)", + "(Un) Acknowledgement note:": "èªå¯/å–消èªå¯ 備註說明", + "(Un)Acknowledgement note:": "確èª/å–消警報 說明:", + "About :project_name": "關於 :project_name", + "Abuse Contact Finder": "濫用通報è¯çµ¡äººæŸ¥æ‰¾", + "Access": "å­˜å–權é™", + "Ack alert":"", + "Acknowledge Alert": "確èªè­¦å ±", + "Acknowledge until alert clears": "確èªï¼Œç›´åˆ°è­¦å ±è§£é™¤", + "Acknowledge until clear:": "確èªç›´åˆ°è§£é™¤:", + "Acknowledgements": "特別感è¬", + "Action": "動作", + "Actions": "動作", + "Add a message to the acknowledgement": "å°è­¦å ±ç¢ºèªæ–°å¢žèªªæ˜Žè³‡è¨Š", + "Add Device": "新增è£ç½®", + "Add Poller Group": "新增輪詢器群組", + "Add schedule notes to devices notes": "新增排程備註至è£ç½®å‚™è¨»", + "Add Service": "新增æœå‹™", + "Add User": "新增使用者", + "Add Widgets": "新增å°å·¥å…·", + "Add": "新增", + "Admin": "Admin", + "Alert disabled": "å·²åœç”¨è­¦å ±", + "Alert history stats": "警報歷程統計", + "Alert History Stats": "警報歷程統計", + "Alert history": "警報歷程", + "Alert History": "警報歷程", + "Alert Log entries": "警報記錄項目", + "Alert Log": "警報記錄", + "Alert notes": "警報說明", + "Alert rule": "警報è¦å‰‡", + "Alert Rules": "警報è¦å‰‡", + "Alert Templates": "警報範本", + "Alert Transports": "警報傳é€", + "alert-disabled": "警報已åœç”¨", + "Alert": "警報", + "Alerted": "已警報", + "Alerts": "警報", + "All alerts": "所有警報", + "All Device Types": "所有è£ç½®é¡žåž‹", + "All devices": "所有è£ç½®", + "All Devices": "所有è£ç½®", + "All Featuresets": "所有功能集", + "All Locations": "所有ä½ç½®", + "All Messages": "所有訊æ¯", + "All OS": "所有作業系統", + "All Platforms": "所有平å°", + "All Ports": "所有連接埠", + "All Priorities": "所有優先順åº", + "All Programs": "所有程å¼", + "All Services": "所有æœå‹™", + "All types": "所有類型", + "All Types": "所有類型", + "All Versions": "所有版本", + "All widgets removed": "已移除所有å°å·¥å…·", + "All": "全部", + "and click Save": "並點按儲存", + "any severity": "任何嚴é‡ç¨‹åº¦", + "any state": "任何狀態", + "Any": "任何", + "API Access": "API å­˜å–", + "API Docs": "API 文件", + "API Settings": "API 設定", + "App does not exist": "應用ä¸å­˜åœ¨", + "Application monitors for PowerDNS, Shoutcast, NTPD (Client, Server).": "PowerDNSã€Shoutcastã€NTPD (用戶端ã€ä¼ºæœå™¨) 的應用程å¼ç›£æŽ§", + "Application": "應用", + "Applications": "應用", "Apply Service Templates": "套用æœå‹™ç¯„本", - "Device Type": "é¡žåž‹", - "Device Group Type": "è£ç½®ç¾¤çµ„é¡žåž‹", - "Device Rules": "è£ç½®è¦å‰‡", - "Device Group Rules": "è£ç½®ç¾¤çµ„è¦å‰‡", "Apply Services for this Service Template": "套用æœå‹™åˆ°æ­¤æœå‹™ç¯„本", - "Remove Services for this Service Template": "移除此æœå‹™ç¯„本的æœå‹™", - "Edit Service Template": "編輯æœå‹™ç¯„本", - "Delete Service Template": "刪除æœå‹™ç¯„本", - "Check Type": "檢查類型", - "Parameters": "åƒæ•¸", - "Remote Host": "é ç«¯ä¸»æ©Ÿ", + "Apps": "應用程å¼", "Are you sure you want to Apply All Service Templates?": "您確定è¦å¥—用至所有的æœå‹™ç¯„本?", - "Are you sure you want to remove all Services created by ": "您確定è¦ç§»é™¤ä»¥ä¸‹å»ºç«‹çš„所有æœå‹™å—Žï¼Ÿ", - "No Services for this Service Template were removed": "未移除這個æœå‹™ç¯„本中的æœå‹™", + "Are you sure you want to delete ": "您確定è¦åˆªé™¤ ", "Are you sure you want to delete AND remove all Services created by ": "您確定è¦åˆªé™¤ä¸”移除以下建立的所有æœå‹™å—Žï¼Ÿ", - "The Service Template could not be deleted": "未刪除這個æœå‹™ç¯„本", - "Ignore tag": "已忽略標記", - "Device Groups Dependencies": "è£ç½®ç¾¤çµ„相ä¾æ€§", - "Outages": "中斷", - "Device Status": "è£ç½®ç‹€æ…‹", - "alert-disabled": "警報已åœç”¨", - "maintenance": "維護", - "Ignored tag": "已忽略標記", - "Alert disabled": "å·²åœç”¨è­¦å ±", - "Node ID": "節點 ID", - "Last Checkin": "最後一次簽入", + "Are you sure you want to remove all Services created by ": "您確定è¦ç§»é™¤ä»¥ä¸‹å»ºç«‹çš„所有æœå‹™å—Žï¼Ÿ", + "ARP Table": "ARP å°ç…§è¡¨", + "ARP Tables": "ARP å°ç…§è¡¨", + "Ascending": "å‡å†ª", + "Auth History": "驗證歷程", + "Auth": "é©—è­‰", + "Authlog": "驗證歷程", + "Automatic Title": "自動產生標題", + "Availability map for": "å¯ç”¨æ€§åœ°åœ– - ", + "Availability Map": "å¯ç”¨æ€§åœ°åœ–", + "Availability": "å¯ç”¨æ€§", + "Basic": "基本", + "Better": "較好", + "BGP Down": "BGP 離線", + "Bill does not exist": "帳單ä¸å­˜åœ¨", + "Bill": "帳單", + "Bits": "Bits", + "boxes": "å€å¡Š", + "Bug tracker": "å•é¡Œè¿½è¹¤", + "Bulk Add": "批次新增", + "Can Modify Password": "å…許修改密碼", + "Cancel": "å–消", + "Cannot delete locations used by devices": "無法刪除已有è£ç½®ä½¿ç”¨çš„ä½ç½®", + "Change Password": "變更密碼", + "Changelog": "變更記錄", + "Check Type": "檢查類型", + "Check your log for more details.": "查閱您的記錄檔以å–得更詳細的資訊。", + "Child Hosts:": "å­ä¸»æ©Ÿ:", + "Clear All": "全部清除", + "Clear remote stats": "清除é ç«¯çµ±è¨ˆè³‡æ–™", + "Clear reporting data": "清除報表資料", + "Clear": "清除", + "Click on the Edit Dashboard button (next to the list of dashboards) to add widgets": "點擊儀表æ¿ç·¨è¼¯æŒ‰éˆ• (在儀表æ¿åˆ—表æ—) 以新增å°å·¥å…·", + "Close": "關閉", "Cluster Master": "å¢é›†ä¸»è¦ç¯€é»ž", - "Job": "作業", - "Poller Name": "輪詢器å稱", - "Devices Polled": "已輪詢è£ç½®", - "Total Poll Time": "總輪詢時間", - "Last Run": "上次執行", - "Poller Cluster Health": "輪詢器å¢é›†å¥åº·ç¨‹åº¦", - "Seconds": "秒", - "Polling Duration": "輪詢æŒçºŒæ™‚é–“", - "Last Polled": "最後一次輪詢", - "Poller Groups": "輪詢器群組", - "Add Poller Group": "新增輪詢器群組", - "Poller Group deleted": "輪詢器群組已刪除", - "Failed to delete Poller Group": "刪除輪詢器群組失敗", + "Codebase for fork.": "分å‰çš„程å¼ç¢¼åŸºç¤Ž", + "Collaboration": "å”作", + "Collectd code": "Collectd 程å¼ç¢¼", + "Columns": "欄ä½", + "Community Forum": "社群論壇", + "compact": "精簡", + "Component Status": "元件狀態", + "Configuration": "組態", "Confirm Delete": "確èªåˆªé™¤", - "If you would like to remove the Poller Group then please click Delete.": "若您確èªè¦åˆªé™¤è¼ªè©¢å™¨ç¾¤çµ„,請按下刪除。", + "Confirm Password": "確èªæ–°å¯†ç¢¼", + "Contributors": "è²¢ç»è€…", + "Coordinates": "座標", + "Copy Dashboard to": "複製資訊看æ¿è‡³", + "Copy Dashboard": "複製資訊看æ¿", + "Core": "核心", + "Count": "計數", + "Countries": "國家", + "CPU Usage": "CPU 使用é‡", + "CPU": "CPU", + "Create Device Group": "建立è£ç½®ç¾¤çµ„", + "Create new poller group": "建立新的輪詢群組", + "Create User": "建立使用者", + "Critical :service_count": "åš´é‡ :service_count", + "Critical": "åš´é‡", + "CSS Style": "CSS 樣å¼", + "Ctrl-F to focus the global search bar": "按 Ctrl-F èšç„¦è‡³å…¨åŸŸæœå°‹åˆ—", + "Current Password": "ç›®å‰å¯†ç¢¼", + "Custom Aggregator(s)": "Custom Aggregator(s)", + "Custom Image": "自訂圖片", + "Custom Map Editor": "自訂地圖編輯器", + "Custom title for widget": "自訂å°å·¥å…·æ¨™é¡Œ", + "Custom title": "自訂標題", + "Customers": "客戶", + "Dashboard Name": "儀表æ¿å稱", + "Dashboard": "資訊看版", + "Dashboards": "資訊看æ¿", + "Database Schema": "資料庫綱è¦", + "Database": "資料庫", + "Date range": "日期範åœ", + "default 80": "é è¨­ 80", + "Default Title": "é è¨­æ¨™é¡Œ", "default": "é è¨­", - "Group Name": "群組å稱", - "Action": "動作", + "Default": "é è¨­", + "Define Rules": "é è¨­è¦å‰‡", + "Delete Device": "刪除è£ç½®", + "Delete Service Template": "刪除æœå‹™ç¯„本", + "Delete": "刪除", + "Deleted :port_count": "已刪除 :port_count", + "Deleted User": "已刪除使用者", + "Deleted": "已刪除", + "Demo": "展示", + "Dependencies": "相ä¾æ€§", + "Descending": "é™å†ª", + "Description": "æè¿°", + "Detail": "詳細資訊", + "Details": "詳細資料", + "Device dependencies could not be retrieved from the database": "無法從資料庫擷å–è£ç½®ç›¸ä¾æ€§", + "Device dependencies saved successfully": "è£ç½®ç›¸ä¾æ€§å„²å­˜æˆåŠŸ", + "Device Dependencies": "è£ç½®ç›¸ä¾æ€§", + "Device Dependency for Multiple Devices": "多è£ç½®çš„è£ç½®ç›¸ä¾æ€§", + "Device Dependency": "è£ç½®ç›¸ä¾æ€§", + "Device does not exist": "è£ç½®ä¸å­˜åœ¨", "Device Down": "è£ç½®é›¢ç·š", - "Port Down": "連接埠離線", - "Service Down": "æœå‹™é›¢ç·š", - "BGP Down": "BGP 離線", - "Device Rebooted": "è£ç½®å·²é‡å•Ÿ", - "Highlight Dependencies to Root Device": "çªé¡¯å°è·Ÿè£ç½®çš„相ä¾æ€§", - "Isolated Devices": "孤立è£ç½®", - "No devices found": "找ä¸åˆ°è£ç½®", - "Poller": "輪詢器", - "Toggle navigation": "切æ›å°Žè¦½åˆ—", - "Alert notes": "警報說明", - "Save notes": "儲存說明", - "Acknowledge Alert": "確èªè­¦å ±", - "Add a message to the acknowledgement": "å°è­¦å ±ç¢ºèªæ–°å¢žèªªæ˜Žè³‡è¨Š", - "(Un)Acknowledgement note:": "確èª/å–消警報 說明:", - "Acknowledge until alert clears": "確èªï¼Œç›´åˆ°è­¦å ±è§£é™¤", - "Acknowledge until clear:": "確èªç›´åˆ°è§£é™¤:", - "Ack alert":"", - "From": "èµ·", - "To": "è¿„", - "VM Host": "VM Host", "Device Group Dependencies": "è£ç½®ç¾¤çµ„相ä¾æ€§", - "Device Dependency": "è£ç½®ç›¸ä¾æ€§", - "The device group could not be deleted": "無法刪除è£ç½®ç¾¤çµ„", - "Operating System": "作業系統", - "Vendor": "廠商", - "Ungrouped Devices": "尚無群組è£ç½®", - "Enabled": "啟用", - "Priority": "é‡è¦æ€§", - "Program": "程å¼", - "Authlog": "驗證歷程", - "User": "使用者", - "IP Address": "IP ä½å€", - "Result": "çµæžœ", - "Translation not fully supported": "多國語系功能尚未完備", - "Warning :service_count": "警告 :service_count", - "Critical :service_count": "åš´é‡ :service_count", + "Device Group Rules": "è£ç½®ç¾¤çµ„è¦å‰‡", + "Device Group Type": "è£ç½®ç¾¤çµ„é¡žåž‹", + "Device group": "è£ç½®ç¾¤çµ„", + "Device Groups Dependencies": "è£ç½®ç¾¤çµ„相ä¾æ€§", + "Device Groups Maps": "è£ç½®ç¾¤çµ„地圖", + "Device Groups": "è£ç½®ç¾¤çµ„", + "Device Permissions": "è£ç½®æ¬Šé™", + "Device Rebooted": "è£ç½®å·²é‡å•Ÿ", + "Device Rules": "è£ç½®è¦å‰‡", + "Device Status": "è£ç½®ç‹€æ…‹", + "Device Summary": "è£ç½®æ‘˜è¦", + "Device Type": "é¡žåž‹", + "Device Types": "è£ç½®é¡žåž‹", + "Device": "è£ç½®", + "devices and services": "è£ç½®èˆ‡æœå‹™", + "Devices Polled": "已輪詢è£ç½®", + "Devices": "è£ç½®", + "Disable TwoFactor": "å–消雙因素驗證", "Disabled :port_count": "å·²åœç”¨ :port_count", - "Errored :port_count": "發生錯誤 :port_count", - "Ignored :port_count": "已忽略 :port_count", - "Down :port_count": "已關閉 :port_count", - "Deleted :port_count": "已刪除 :port_count", - "About :project_name": "關於 :project_name", - "See the list of contributors on GitHub.": "至 GitHub 觀看貢ç»è€…å單。", - "Docs": "文件", - "Close": "關閉", - "LibreNMS is an autodiscovering PHP/MySQL-based network monitoring system": "LibreNMS 是個以 PHP/MySQL 為基底的自動探索網路監控系統", - "Packages": "軟體包", - "Version": "版本", - "Database Schema": "資料庫綱è¦", - "Web Server": "Web 伺æœå™¨", - "LibreNMS is a community-based project": "LibreNMS 是建基於社群開發的專案", - "Please feel free to join us and contribute code, documentation, and bug reports:": "您隨時都å¯ä»¥åŠ å…¥ç¤¾ç¾¤è²¢ç»æ‚¨çš„程å¼ç¢¼ã€æ–‡ä»¶ä»¥åŠå•é¡Œå›žå ±ï¼š", - "Web site": "官方網站", - "Bug tracker": "å•é¡Œè¿½è¹¤", - "Community Forum": "社群論壇", - "Changelog": "變更記錄", - "Local git log": "本機 Git 記錄", - "Contributors": "è²¢ç»è€…", - "Acknowledgements": "特別感è¬", - "Opt in to send anonymous usage statistics to LibreNMS?": "您è¦é¸æ“‡ä»¥åŒ¿åæ–¹å¼æ供使用統計資料給 LibreNMS 嗎?", - "Online stats:": "線上統計資料", - "Clear remote stats": "清除é ç«¯çµ±è¨ˆè³‡æ–™", - "IPv4 Addresses": "IPv4 ä½å€", - "IPv4 Networks": "IPv4 網路", - "IPv6 Addresses": "IPv6 ä½å€", - "IPv6 Networks": "IPv6 網路", - "Processors": "處ç†å™¨", - "Applications": "應用", + "Disabled polling/alerting": "å·²åœç”¨è¼ªè©¢/警報", + "disabled": "å·²å–消", + "Disabled": "å·²åœç”¨", + "Disabled\/ignored": "Disabled\/ignored", "Disk I/O": "ç£ç¢Ÿ I/O", - "Syslog Entries": "Syslog é …ç›®", - "Eventlog Entries": "事件記錄項目", - "Sensors": "感測器", - "Wireless Sensors": "無線感測器", - "Toner": "碳粉", - "License": "授權", - "Shutdown": "關機", - "Select Devices": "é¸æ“‡è£ç½®", + "Disk usage": "ç£ç¢Ÿä½¿ç”¨é‡", + "Disk": "ç£ç¢Ÿ", + "Display Name": "顯示å稱", + "Display type": "顯示類型", + "Displayed severity": "顯示嚴é‡ç¨‹åº¦", + "Distributed poller": "分散å¼è¼ªè©¢å™¨", + "Distributedpoller": "分散å¼è¼ªè©¢å™¨", + "Docs": "文件", + "Down :port_count": "已關閉 :port_count", + "down": "離線", + "Down": "離線", + "Duration": "æŒçºŒæ™‚é–“", "Dynamic": "å‹•æ…‹", - "Static": "éœæ…‹", - "Define Rules": "é è¨­è¦å‰‡", - "Create Device Group": "建立è£ç½®ç¾¤çµ„", + "Edit Dashboard": "編輯資訊看æ¿", + "Edit Dependency": "編輯相ä¾æ€§", "Edit Device Group": "編輯è£ç½®ç¾¤çµ„", - "New Device Group": "新增è£ç½®ç¾¤çµ„", - "Pattern": "模å¼", - "Type": "é¡žåž‹", - "Name": "å稱", - "User Preferences": "使用者喜好設定", - "Global Administrative Access": "全域管ç†å­˜å–", - "Device Permissions": "è£ç½®æ¬Šé™", - "Preferences": "喜好設定", - "Language": "語言", - "Change Password": "變更密碼", - "Verify New Password": "確èªæ–°å¯†ç¢¼", - "Peering + Transit": "互連 + 轉é€", + "Edit Mode": "編輯模å¼", + "Edit Service Template": "編輯æœå‹™ç¯„本", + "Edit User": "編輯使用者", + "Edit": "編輯", + "Email": "郵件", + "empty": "空白", + "Enable": "啟用", + "Enabled": "啟用", + "End": "çµæŸ", + "Error Rate": "錯誤率", + "Error reporting:": "錯誤回報:", + "ERROR: Could not update": "錯誤: 無法更新", + "ERROR: You have no write-access to this dashboard": "錯誤: 您沒有此儀表æ¿çš„寫入權é™", + "ERROR: You need to be admin": "錯誤: 您需è¦ç®¡ç†å“¡æ¬Šé™", + "Errored :port_count": "發生錯誤 :port_count", + "Errored": "已錯誤", + "Event type": "事件類型", + "Eventlog Entries": "事件記錄項目", + "Eventlog": "事件記錄", + "Existing password did not match": "與既有的密碼ä¸ç¬¦", + "Export to PDF": "åŒ¯å‡ºæˆ PDF", + "Facility": "設備", + "Failed to create user": "建立使用者失敗", + "Failed to delete location": "刪除ä½ç½®å¤±æ•—", + "Failed to delete Poller Group": "刪除輪詢器群組失敗", + "Failed to remove Two Factor": "移除雙因素驗證失敗", + "Failed to remove Two-Factor.": "雙因素驗證移除失敗。", + "Failed to unlock Two Factor": "雙因素驗證解除鎖定失敗", + "Failed to unlock Two-Factor.": "雙因素驗證解鎖失敗。", + "Failed to update user :username": "更新使用者 :username 失敗", + "Failure": "失敗", + "FDB Table": "FDB å°ç…§è¡¨", "FDB Tables": "FDB å°ç…§è¡¨", - "ARP Tables": "ARP å°ç…§è¡¨", - "MAC Address": "MAC ä½å€", - "IPv6 Address": "IPv6 ä½å€", - "IPv4 Address": "IPv4 ä½å€", - "Package": "軟體包", - "Virtual Machines": "虛擬機器", - "Device Groups": "è£ç½®ç¾¤çµ„", - "Register": "註冊", - "Overview": "概觀", - "Maps": "地圖", - "Availability": "å¯ç”¨æ€§", - "Device Groups Maps": "è£ç½®ç¾¤çµ„地圖", + "Filter": "篩é¸", + "Firewalls": "防ç«ç‰†", + "Four Hours": "4 å°æ™‚", + "From": "èµ·", + "Geo Locations": "地ç†ä½ç½®", "Geographical": "地ç†", - "Plugins": "外掛", - "Plugin Admin": "外掛管ç†", - "Tools": "工具", - "Eventlog": "事件記錄", + "Global Administrative Access": "全域管ç†å­˜å–", + "Global Read": "全域讀å–", + "Global Search": "全域æœå°‹", + "Global Settings": "全域設定", + "Globe": "å…¨çƒ", + "Graph type": "圖表類型", + "Graphs": "圖表", + "Group Name": "群組å稱", + "Grouping radius": "Grouping radius", + "Groups": "群組", + "Health": "å¥åº·æƒ…æ³", + "Help": "說明", + "Here you can modify multiple device dependencies. Setting the parent device to \"None\" will clear the dependency.": "您å¯ä»¥åœ¨é€™è£¡ä¿®æ”¹å¤šå€‹è£ç½®ç›¸ä¾æ€§ã€‚將上層è£ç½®è¨­ç‚º \"ç„¡\" 將清除相ä¾æ€§ã€‚", + "hide acknowledged": "éš±è—已通知", + "Hide Dashboard Editor": "éš±è—儀表æ¿ç·¨è¼¯å™¨", + "Hide Navigation": "éš±è—導航", + "hide": "éš±è—", + "Hide": "éš±è—", + "Highlight Dependencies to Root Device": "çªé¡¯å°è·Ÿè£ç½®çš„相ä¾æ€§", + "History": "歷程", + "Hostname": "主機å稱", + "ID": "ID", + "ie. 0 for Greenwich": "例如 0 為格林å¨æ²»", + "ie. 51.4800 for Greenwich": "例如 51.4800 為格林å¨æ²»", + "If you need additional help, you can find how to get help at": "若您需è¦æ›´å¤šçš„說明,您å¯ä»¥åœ¨é€™è£¡æ‰¾åˆ°æ›´å¤šç›¸é—œè³‡è¨Šã€‚", + "If you want just text then wrap in
": "如果您åªæƒ³è¦ç´”文字,請用 
 包起來",
+    "If you would like to remove the Poller Group then please click Delete.": "若您確èªè¦åˆªé™¤è¼ªè©¢å™¨ç¾¤çµ„,請按下刪除。",
+    "Ignore tag": "已忽略標記",
+    "Ignored :port_count": "已忽略 :port_count",
+    "Ignored tag": "已忽略標記",
+    "ignored": "已忽略",
+    "Ignored": "已忽略",
+    "Image URL": "圖片 URL",
+    "Initial Latitude": "åˆå§‹ç·¯åº¦",
+    "Initial Longitude": "åˆå§‹ç¶“度",
+    "Initial Zoom": "åˆå§‹ Zoom 縮放等級",
+    "Interface type": "介é¢é¡žåž‹",
+    "Interface": "介é¢",
     "Inventory": "設備",
-    "MIB definitions": "MIB 定義",
-    "No devices": "沒有è£ç½®",
-    "MIB associations": "MIB é—œè¯",
+    "IP Address": "IP ä½å€",
+    "IP, ASN etc.": "IPã€ASN ç­‰",
+    "IPv4 Address": "IPv4 ä½å€",
+    "IPv4 Addresses": "IPv4 ä½å€",
+    "IPv4 Networks": "IPv4 網路",
+    "IPv6 Address": "IPv6 ä½å€",
+    "IPv6 Addresses": "IPv6 ä½å€",
+    "IPv6 Networks": "IPv6 網路",
+    "Isolated Devices": "孤立è£ç½®",
+    "Job": "作業",
+    "Label": "標籤",
+    "Language": "語言",
+    "Last Checkin": "最後一次簽入",
+    "Last days": "最近幾天",
+    "Last polled (minutes)": "最後一次輪詢 (分é˜)",
+    "Last Polled (minutes)": "最後一次輪詢 (分é˜)",
+    "Last Polled": "最後一次輪詢",
+    "Last Run": "上次執行",
+    "Level": "等級",
+    "LibreNMS is a community-based project": "LibreNMS 是建基於社群開發的專案",
+    "LibreNMS is an autodiscovering PHP/MySQL-based network monitoring system": "LibreNMS 是個以 PHP/MySQL 為基底的自動探索網路監控系統",
+    "License": "授權",
+    "Lists": "列表",
+    "Load": "負載",
+    "Local git log": "本機 Git 記錄",
+    "Location deleted": "ä½ç½®å·²åˆªé™¤",
+    "Location must have devices to show graphs": "æ­¤ä½ç½®å¿…需有è£ç½®æ‰èƒ½é¡¯ç¤ºåœ–表",
+    "Location updated": "ä½ç½®å·²æ›´æ–°",
+    "Location": "ä½ç½®",
+    "Locations": "ä½ç½®",
+    "Log Level": "記錄層級",
+    "Login": "登入",
+    "Logon": "登入",
+    "Logout": "登出",
+    "MAC Address": "MAC ä½å€",
+    "Mail": "郵件",
+    "maintenance": "維護",
+    "Maintenance": "維護",
+    "Manage Access": "管ç†å­˜å–權é™",
+    "Manage Device Dependencies": "管ç†è£ç½®ç›¸ä¾æ€§",
     "Manage Groups": "群組管ç†",
-    "Device Dependencies": "è£ç½®ç›¸ä¾æ€§",
-    "Add Device": "新增è£ç½®",
-    "Delete Device": "刪除è£ç½®",
-    "All Services": "所有æœå‹™",
-    "Add Service": "新增æœå‹™",
-    "Traffic Bills": "æµé‡å¸³å–®",
-    "Pseudowires": "虛擬線路",
-    "Customers": "客戶",
-    "Transit": "轉é€",
-    "Core": "核心",
-    "Alerts": "警報",
-    "Deleted": "已刪除",
-    "Health": "å¥åº·æƒ…æ³",
+    "Manage Users": "管ç†ä½¿ç”¨è€…",
+    "Maps": "地圖",
+    "Markers": "標記",
+    "Memory usage": "記憶體使用é‡",
     "Memory": "記憶體",
-    "Processor": "處ç†å™¨",
-    "Storage": "儲存",
-    "Wireless": "ç„¡ç·š",
-    "Apps": "應用程å¼",
-    "Routing": "路由",
-    "Alerted": "已警報",
-    "Notifications": "通知",
-    "Alert History": "警報歷程",
-    "Statistics": "統計資料",
-    "Alert Rules": "警報è¦å‰‡",
-    "Scheduled Maintenance": "定期維護",
-    "Alert Templates": "警報範本",
-    "Alert Transports": "警報傳é€",
+    "Message": "訊æ¯",
+    "Metrics": "指標",
+    "Metros": "Metros",
+    "MIB associations": "MIB é—œè¯",
+    "MIB definitions": "MIB 定義",
+    "Minimum log Level": "最å°è¨˜éŒ„層級",
+    "Mode select": "模å¼é¸æ“‡",
+    "Munin plugin does not exist": "Munin 外掛程å¼ä¸å­˜åœ¨",
+    "Munin plugin": "Munin 外掛程å¼",
     "My Settings": "我的設定",
-    "Settings": "設定",
-    "Global Settings": "全域設定",
-    "Global Search": "全域æœå°‹",
-    "Validate Config": "組態驗證",
-    "Auth History": "驗證歷程",
-    "Peering": "互連",
-    "API Settings": "API 設定",
-    "API Docs": "API 文件",
-    "The :attribute must a valid IP address\/network or hostname.": "這個 :attribute 必需為有效的 IP ä½å€\/網路或主機å稱。",
+    "N\/A": "ç„¡",
+    "Name": "å稱", 
+    "Network": "網路",
     "Never polled": "從未輪詢",
-    "This indicates the most likely endpoint switchport": "這將表示為最有å¯èƒ½çš„交æ›å™¨é€£æŽ¥åŸ ç«¯é»ž",
-    "Two-Factor unlocked.": "雙因素驗證已解鎖。",
-    "Failed to unlock Two-Factor.": "雙因素驗證解鎖失敗。",
-    "Two-Factor removed.": "雙因素驗證已移除。",
-    "Failed to remove Two-Factor.": "雙因素驗證移除失敗。",
-    "TwoFactor auth removed.": "雙因素驗證已移除。",
-    "Too many two-factor failures, please contact administrator.": "雙因素驗證失敗次數太多,請年繫您的系統管ç†å“¡ï¼Œ",
-    "Too many two-factor failures, please wait :time seconds": "雙因素驗證失敗次數太多,請等候 :time 秒å†è©¦",
-    "No Two-Factor Token entered.": "沒有輸入雙因素驗證權仗。",
+    "New Dashboard": "新增資訊看æ¿",
+    "New Device Group": "新增è£ç½®ç¾¤çµ„",
+    "New Password": "新密碼",
+    "New Service Template": "新增æœå‹™ç¯„本",
+    "No devices found within interval.": "在最近一次輪詢間隔內尚未找到è£ç½®ã€‚",
+    "No devices found": "找ä¸åˆ°è£ç½®",
+    "No devices": "沒有è£ç½®",
+    "No other Dashboards": "沒有其他資訊看æ¿",
+    "No results found!": "找ä¸åˆ°çµæžœï¼",
+    "No Services for this Service Template were removed": "未移除這個æœå‹™ç¯„本中的æœå‹™",
     "No Two-Factor settings, how did you get here?": "沒有設定雙因素驗證,您è¦å¦‚何使用呢?",
-    "Wrong Two-Factor Token.": "錯誤的雙因素驗證權仗。",
-    "TwoFactor auth added.": "雙因素驗證已加入。",
-    "User :username created": "使用者 :username 已建立",
-    "Failed to create user": "建立使用者失敗",
-    "Updated dashboard for :username": "已更新 :username 的資訊看版",
-    "User :username updated": "使用者 :username 已更新",
-    "Failed to update user :username": "更新使用者 :username 失敗",
-    "User :username deleted.": "使用者 :username 已刪除。",
-    "Device does not exist": "è£ç½®ä¸å­˜åœ¨",
-    "Port does not exist": "連接埠ä¸å­˜åœ¨",
-    "App does not exist": "應用ä¸å­˜åœ¨",
-    "Bill does not exist": "帳單ä¸å­˜åœ¨",
-    "Munin plugin does not exist": "Munin 外掛程å¼ä¸å­˜åœ¨",
-    "Ok": "確èª",
-    "Warning": "警告",
-    "Critical": "åš´é‡",
-    "Existing password did not match": "與既有的密碼ä¸ç¬¦",
-    "The :attribute field is required.": ":attribute 是必è¦æ¬„ä½ã€‚",
-    "Edit User": "編輯使用者",
-    "Unlock": "解除鎖定",
-    "User exceeded failures": "使用者é”到失敗上é™",
-    "Disable TwoFactor": "å–消雙因素驗證",
+    "No Two-Factor Token entered.": "沒有輸入雙因素驗證權仗。",
     "No TwoFactor key generated for this user, Nothing to do.": "沒有為這個使用者產生雙因素驗證金鑰,暫ä¸å‹•ä½œã€‚",
-    "Save": "儲存",
-    "Cancel": "å–消",
-    "Unlocked Two Factor.": "解儲雙因素驗證鎖定。",
-    "Failed to unlock Two Factor": "雙因素驗證解除鎖定失敗",
-    "Removed Two Factor.": "雙因素驗證已經移除。",
-    "Failed to remove Two Factor": "移除雙因素驗證失敗",
-    "Real Name": "真實姓å",
-    "Email": "郵件",
-    "Description": "æè¿°",
-    "Level": "等級",
+    "no": "å¦",
+    "Node ID": "節點 ID",
+    "None": "ç„¡",
     "Normal": "正常",
-    "Global Read": "全域讀å–",
-    "Admin": "Admin",
-    "Demo": "Demo",
-    "Dashboard": "資訊看版",
+    "not filtered": "ä¸è¦ç¯©é¸",
+    "Notes": "附註",
+    "Notifications": "通知",
+    "Number of Devices": "è£ç½®æ•¸é‡",
+    "Number of interfaces": "介é¢æ•¸é‡",
+    "Ok (recovered)": "正常 (已復原)",
+    "Ok, warning and critical": "正常ã€è­¦å‘ŠåŠåš´é‡",
+    "Ok": "確èª",
+    "One Day": "1 天",
+    "One Hour": "1 å°æ™‚",
+    "One Month": "1 個月",
+    "One Week": "1 週",
+    "One Year": "1 å¹´",
+    "Online stats:": "線上統計資料",
+    "only devices": "僅è£ç½®",
+    "only services": "僅æœå‹™",
+    "Operating System": "作業系統",
+    "Opt in to send anonymous reports to LibreNMS?": "是å¦åŒæ„傳é€åŒ¿å報告給 LibreNMS?",
+    "Opt in to send anonymous usage statistics to LibreNMS?": "您è¦é¸æ“‡ä»¥åŒ¿åæ–¹å¼æ供使用統計資料給 LibreNMS 嗎?",
+    "or higher": "或更高者",
+    "Order By": "排åº",
+    "Origin": "來æº",
+    "Outages": "中斷",
+    "Overlib Library.": "Overlib 函å¼åº«",
+    "Overview": "概觀",
+    "Package": "軟體包",
+    "Packages": "軟體包",
+    "Page Size": "é é¢å¤§å°",
+    "Parameters": "åƒæ•¸",
+    "Parent Device(s)": "上層è£ç½®",
+    "Parent Host:": "上層主機:",
+    "Parent ID": "上層 ID",
     "Password": "密碼",
-    "Current Password": "ç›®å‰å¯†ç¢¼",
-    "New Password": "新密碼",
-    "Confirm Password": "確èªæ–°å¯†ç¢¼",
-    "Can Modify Password": "å…許修改密碼",
-    "Create User": "建立使用者",
-    "Username": "使用者å稱",
-    "Manage Users": "管ç†ä½¿ç”¨è€…",
-    "ID": "ID",
-    "Access": "å­˜å–權é™",
-    "Auth": "é©—è­‰",
-    "Actions": "動作",
-    "Edit": "編輯",
-    "Delete": "刪除",
-    "Manage Access": "管ç†å­˜å–權é™",
-    "Add User": "新增使用者",
-    "Are you sure you want to delete ": "您確定è¦åˆªé™¤ ",
-    "The user could not be deleted": "這個使用者無法刪除",
-    "Whoops, the web server could not write required files to the filesystem.": "噢,Web Server 無法寫入檔案到檔案系統。",
-    "Running the following commands will fix the issue most of the time:": "Running the following commands will fix the issue most of the time:",
-    "Whoops, looks like something went wrong. Check your librenms.log.": "噢,看起來發生了一些錯誤。請您查閱 librenms.log。",
-    "Public Devices": "公開è£ç½®",
-    "System Status": "系統è£æ…‹",
-    "Logon": "登入",
-    "Device": "è£ç½®",
+    "Pattern": "模å¼",
+    "Peering + Transit": "互連 + 轉é€",
+    "Peering": "互連",
+    "Performance": "效能",
+    "Php": "Php",
+    "Placeholder": " ",
     "Platform": "å¹³å°",
-    "Uptime": "é‹ä½œæ™‚é–“",
-    "Location": "ä½ç½®",
-    "Status": "狀態",
-    "Remember Me": "記ä½æˆ‘",
-    "Login": "登入",
+    "Please choose a parent device for": "è«‹é¸æ“‡æ­¤è£ç½®çš„上層è£ç½®:",
     "Please enter auth token": "請輸入驗證權仗",
-    "Submit": "æ交",
-    "Logout": "登出",
-    "Locations": "ä½ç½®",
-    "Coordinates": "座標",
-    "Devices": "è£ç½®",
-    "Network": "網路",
-    "Servers": "伺æœå™¨",
-    "Firewalls": "防ç«ç‰†",
-    "Down": "離線",
-    "Save changes": "儲存變更",
-    "N\/A": "ç„¡",
-    "Location must have devices to show graphs": "æ­¤ä½ç½®å¿…需有è£ç½®æ‰èƒ½é¡¯ç¤ºåœ–表",
-    "Traffic": "æµé‡",
-    "Cannot delete locations used by devices": "無法刪除已有è£ç½®ä½¿ç”¨çš„ä½ç½®",
-    "Location deleted": "ä½ç½®å·²åˆªé™¤",
-    "Failed to delete location": "刪除ä½ç½®å¤±æ•—",
-    "Timestamp": "時間戳記",
-    "Source": "來æº",
-    "Message": "訊æ¯",
-    "Facility": "設備",
-    "Total hosts": "所有主機",
-    "ignored": "已忽略",
-    "disabled": "å·²å–消",
-    "up": "上線",
-    "warn": "警告",
-    "down": "離線",
-    "Total services": "所有æœå‹™",
-    "Widget title": "å°å·¥å…·æ¨™é¡Œ",
-    "Default Title": "é è¨­æ¨™é¡Œ",
-    "Columns": "欄ä½",
-    "Markers": "標記",
+    "Please feel free to join us and contribute code, documentation, and bug reports:": "您隨時都å¯ä»¥åŠ å…¥ç¤¾ç¾¤è²¢ç»æ‚¨çš„程å¼ç¢¼ã€æ–‡ä»¶ä»¥åŠå•é¡Œå›žå ±ï¼š",
+    "Plugin Admin": "外掛管ç†",
+    "Plugins": "外掛",
+    "Poller Cluster Health": "輪詢器å¢é›†å¥åº·ç¨‹åº¦",
+    "Poller duration": "輪詢器花費時間",
+    "Poller Group deleted": "輪詢器群組已刪除",
+    "Poller Groups": "輪詢器群組",
+    "Poller Name": "輪詢器å稱",
+    "Poller": "輪詢器",
+    "Pollers": "輪詢器",
+    "Polling Duration": "輪詢æŒçºŒæ™‚é–“",
+    "Port does not exist": "連接埠ä¸å­˜åœ¨",
+    "Port Down": "連接埠離線",     
+    "Port group": "連接埠群組",
+    "Port Groups": "連接埠群組",
+    "Port": "連接埠",
     "Ports": "連接埠",
-    "Resolution": "解æžåº¦",
-    "Countries": "國家",
+    "Power Status": "é›»æºç‹€æ…‹", 
+    "Preferences": "喜好設定",
+    "Priority": "é‡è¦æ€§",
+    "Private": "ç§äºº",
+    "Problem with backend": "後端發生å•é¡Œ",
+    "Processor load": "處ç†å™¨è² è¼‰",
+    "Processor": "處ç†å™¨",
+    "Processors": "處ç†å™¨",
+    "Program": "程å¼",
+    "Programs": "程å¼",
     "Provinces": "çœä»½",
-    "Metros": "Metros",
+    "Pseudowires": "虛擬線路",
+    "Public Devices": "公開è£ç½®",
+    "Push Notifications": "推播通知",
+    "Query": "查詢",
+    "Real Name": "真實姓å",
+    "recovered": "å·²æ¢å¾©",
     "Region": "地å€",
-    "Help": "說明",
-    "Stream": "串æµ",
-    "All Messages": "所有訊æ¯",
-    "All Devices": "所有è£ç½®",
-    "Page Size": "é é¢å¤§å°",
-    "Time Range": "時間範åœ",
+    "Register": "註冊",
+    "Remember Me": "記ä½æˆ‘",
+    "Remember:": "請記ä½:",
+    "Remote Host": "é ç«¯ä¸»æ©Ÿ",
+    "Remove Dashboard": "移除資訊看æ¿",
+    "Remove Header": "移除標題",
+    "Remove Search": "移除æœå°‹",
+    "Remove Services for this Service Template": "移除此æœå‹™ç¯„本的æœå‹™",
+    "Remove": "移除",
+    "Removed Two Factor.": "雙因素驗證已經移除。",
+    "Reporting & Statistics": "報表與統計",
+    "Reset criteria to default.": "é‡è¨­æ¢ä»¶ç‚ºé è¨­å€¼",
+    "Reset": "é‡è¨­",
+    "Resolution": "解æžåº¦",
+    "Response time": "回應時間",
+    "Restore Header": "還原標題",
+    "Restore Search": "還原æœå°‹",
+    "Result": "çµæžœ",
+    "RIPE NCC - API Tools": "RIPE NCC - API 工具",
+    "RIPE NCC API Tools": "RIPE NCC API 工具",
+    "Roles": "角色",
+    "Routing": "路由",
+    "Rrd": "Rrd",
+    "Rrdcheck": "Rrdcheck",
+    "Rule": "è¦å‰‡",
+    "Running the following commands will fix the issue most of the time:": "Running the following commands will fix the issue most of the time:",
+    "Save changes": "儲存變更",
+    "Save notes": "儲存說明",
+    "Save": "儲存",
+    "Scheduled Maintenance": "定期維護",
+    "Scheduler": "排程器",
     "Search all time": "æœå°‹æ‰€æœ‰æ™‚é–“",
-    "Search last 5 minutes": "æœå°‹æœ€è¿‘ 5 分é˜å…§",
-    "Search last 15 minutes": "æœå°‹æœ€è¿‘ 15 分é˜å…§",
-    "Search last 30 minutes": "æœå°‹æœ€è¿‘ 30 分é˜å…§",
-    "Search last 1 hour": "æœå°‹æœ€è¿‘ 1 å°æ™‚å…§",
-    "Search last 2 hours": "æœå°‹æœ€è¿‘ 2 å°æ™‚å…§",
-    "Search last 8 hours": "æœå°‹æœ€è¿‘ 8 å°æ™‚å…§",
     "Search last 1 day": "æœå°‹æœ€è¿‘ 1 天內",
+    "Search last 1 hour": "æœå°‹æœ€è¿‘ 1 å°æ™‚å…§",
+    "Search last 14 days": "æœå°‹æœ€è¿‘ 14 天內",
+    "Search last 15 minutes": "æœå°‹æœ€è¿‘ 15 分é˜å…§",
     "Search last 2 days": "æœå°‹æœ€è¿‘ 2 天內",
+    "Search last 2 hours": "æœå°‹æœ€è¿‘ 2 å°æ™‚å…§",
+    "Search last 30 days": "æœå°‹æœ€è¿‘ 30 天內",
+    "Search last 30 minutes": "æœå°‹æœ€è¿‘ 30 分é˜å…§",
     "Search last 5 days": "æœå°‹æœ€è¿‘ 5 天內",
+    "Search last 5 minutes": "æœå°‹æœ€è¿‘ 5 分é˜å…§",
     "Search last 7 days": "æœå°‹æœ€è¿‘ 7 天內",
-    "Search last 14 days": "æœå°‹æœ€è¿‘ 14 天內",
-    "Search last 30 days": "æœå°‹æœ€è¿‘ 30 天內",
-    "Custom title": "自訂標題",
-    "Initial Latitude": "åˆå§‹ç·¯åº¦",
-    "ie. 51.4800 for Greenwich": "例如 51.4800 為格林å¨æ²»",
-    "Initial Longitude": "åˆå§‹ç¶“度",
-    "ie. 0 for Greenwich": "例如 0 為格林å¨æ²»",
-    "Initial Zoom": "åˆå§‹ Zoom 縮放等級",
-    "Grouping radius": "Grouping radius",
-    "default 80": "é è¨­ 80",
-    "Show devices": "顯示è£ç½®",
-    "Up + Down": "上線 + 離線",
-    "Up": "上線",
-    "Show Services": "顯示æœå‹™",
-    "no": "å¦",
-    "yes": "是",
-    "Show Port Errors": "顯示連接埠錯誤",
-    "Notes": "附註",
-    "Custom title for widget": "自訂å°å·¥å…·æ¨™é¡Œ",
-    "Display type": "顯示類型",
-    "boxes": "å€å¡Š",
-    "compact": "精簡",
-    "Uniform Tiles": "Uniform Tiles",
-    "Tile size": "Tile size",
-    "Disabled\/ignored": "Disabled\/ignored",
-    "Show": "顯示",
-    "Hide": "éš±è—",
-    "Mode select": "模å¼é¸æ“‡",
-    "only devices": "僅è£ç½®",
-    "only services": "僅æœå‹™",
-    "devices and services": "è£ç½®èˆ‡æœå‹™",
-    "Order By": "排åº",
-    "Hostname": "主機å稱",
-    "Device group": "è£ç½®ç¾¤çµ„",
-    "Automatic Title": "自動產生標題",
-    "Graph type": "圖表類型",
-    "Select a graph": "é¸æ“‡åœ–表",
-    "Show legend": "顯示圖例",
-    "Date range": "日期範åœ",
-    "One Hour": "1 å°æ™‚",
-    "Four Hours": "4 å°æ™‚",
-    "Six Hours": "6 å°æ™‚",
-    "Twelve Hours": "12 å°æ™‚",
-    "One Day": "1 天",
-    "One Week": "1 週",
-    "Two Weeks": "2 週",
-    "One Month": "1 個月",
-    "Two Months": "2 個月",
-    "Three Months": "3 個月",
-    "One Year": "1 å¹´",
-    "Two Years": "2 å¹´",
+    "Search last 8 hours": "æœå°‹æœ€è¿‘ 8 å°æ™‚å…§",
+    "Search": "æœå°‹",
+    "Seconds": "秒",
+    "See the list of contributors on GitHub.": "至 GitHub 觀看貢ç»è€…å單。",
+    "Select a bill": "é¸æ“‡å¸³å–®",
     "Select a device": "é¸æ“‡è£ç½®",
-    "Port": "連接埠",
+    "Select a graph": "é¸æ“‡åœ–表",
+    "Select a Munin plugin": "é¸æ“‡ Munin 外掛程å¼",
     "Select a port": "é¸æ“‡é€£æŽ¥åŸ ",
-    "Application": "應用",
     "Select an application": "é¸æ“‡æ‡‰ç”¨",
-    "Munin plugin": "Munin 外掛程å¼",
-    "Select a Munin plugin": "é¸æ“‡ Munin 外掛程å¼",
-    "Bill": "帳單",
-    "Select a bill": "é¸æ“‡å¸³å–®",
-    "Custom Aggregator(s)": "Custom Aggregator(s)",
-    "Select or add one or more": "é¸æ“‡æˆ–加入一或多個",
+    "Select Devices": "é¸æ“‡è£ç½®",
     "Select one or more": "é¸æ“‡ä¸€æˆ–多個",
-    "Top query": "排行榜查詢",
-    "Response time": "回應時間",
-    "Poller duration": "輪詢器花費時間",
-    "Processor load": "處ç†å™¨è² è¼‰",
-    "Memory usage": "記憶體使用é‡",
-    "Disk usage": "ç£ç¢Ÿä½¿ç”¨é‡",
-    "Sort order": "排åº",
-    "Ascending": "å‡å†ª",
-    "Descending": "é™å†ª",
-    "Number of Devices": "è£ç½®æ•¸é‡",
-    "Last Polled (minutes)": "最後一次輪詢 (分é˜)",
-    "Image URL": "圖片 URL",
-    "Target URL": "連çµç›®æ¨™ URL",
+    "Select or add one or more": "é¸æ“‡æˆ–加入一或多個",
+    "Select the parent device to delete its child devices": "é¸æ“‡ä¸Šå±¤è£ç½®ä»¥åˆªé™¤å…¶å­è£ç½®",
+    "Select Widget": "é¸æ“‡å°å·¥å…·",
+    "Sensors": "感測器",
+    "Server Name": "伺æœå™¨å稱",
+    "Server Stats": "伺æœå™¨çµ±è¨ˆ",
+    "Servers": "伺æœå™¨",
+    "Service Down": "æœå‹™é›¢ç·š", 
+    "Services Templates": "æœå‹™ç¯„本",
+    "Services": "æœå‹™",
+    "Settings": "設定",
+    "severity, descending": "åš´é‡ç¨‹åº¦ã€é™å†ª",
+    "Severity": "åš´é‡åº¦",
+    "Shared (Read)": "共用 (唯讀)",
+    "Shared Dashboards": "共用儀表æ¿",
+    "Shared": "共用",
     "Show acknowledged": "顯示已通知",
-    "not filtered": "ä¸è¦ç¯©é¸",
+    "Show Dashboard Editor": "顯示儀表æ¿ç·¨è¼¯å™¨",
+    "Show devices": "顯示è£ç½®",
+    "Show legend": "顯示圖例",
     "show only acknowledged": "僅顯示已通知",
-    "hide acknowledged": "éš±è—已通知",
-    "Show only fired": "僅顯示已觸發",
     "show only fired alerts": "僅顯示已觸發警報",
-    "Displayed severity": "顯示嚴é‡ç¨‹åº¦",
-    "any severity": "任何嚴é‡ç¨‹åº¦",
-    "or higher": "或更高者",
-    "State": "狀態",
-    "any state": "任何狀態",
-    "All alerts": "所有警報",
+    "Show only fired": "僅顯示已觸發",
+    "Show Port Errors": "顯示連接埠錯誤",
     "Show Procedure field": "顯示處ç†æ¬„ä½",
+    "Show Services": "顯示æœå‹™",
     "show": "顯示",
-    "hide": "éš±è—",
+    "Show": "顯示",
+    "Shutdown": "關機",
+    "Six Hours": "6 å°æ™‚",
+    "SNMP code improvements.": "SNMP 程å¼ç¢¼æ”¹é€²",
+    "SNMP sysName": "SNMP 系統å稱",
     "Sort alerts by": "排åºè­¦å ±ä¾æ“š",
-    "timestamp, descending": "時間戳記ã€é™å†ª",
-    "severity, descending": "åš´é‡ç¨‹åº¦ã€é™å†ª",
-    "All devices": "所有è£ç½®",
-    "Event type": "事件類型",
-    "All types": "所有類型",
-    "Number of interfaces": "介é¢æ•¸é‡",
-    "Last polled (minutes)": "最後一次輪詢 (分é˜)",
-    "Interface type": "介é¢é¡žåž‹",
-    "All Ports": "所有連接埠",
-    "Total": "總計",
-    "Ignored": "已忽略",
-    "Disabled": "å·²åœç”¨",
-    "Errored": "已錯誤",
-    "Services": "æœå‹™",
-    "No devices found within interval.": "在最近一次輪詢間隔內尚未找到è£ç½®ã€‚",
+    "Sort order": "排åº",
+    "Sort Order": "排åºé †åº",
+    "Source": "來æº",
+    "Standard Pollers": "標準輪詢器",
+    "Start": "開始",
+    "State": "狀態",
+    "Static": "éœæ…‹",
+    "Statistics": "統計資料",
+    "Status": "狀態",
+    "Storage": "儲存",
+    "Stream": "串æµ",
+    "Submit": "æ交",
     "Summary": "摘è¦",
-    "Interface": "介é¢",
+    "Syslog Entries": "Syslog é …ç›®",
+    "Sysname": "系統å稱",
+    "System Status": "系統狀態",
+    "System": "系統",
+    "Target URL": "連çµç›®æ¨™ URL",
+    "Temperature": "溫度",
+    "The :attribute field is required.": ":attribute 是必è¦æ¬„ä½ã€‚",
+    "The :attribute must a valid IP address\/network or hostname.": "這個 :attribute 必需為有效的 IP ä½å€\/網路或主機å稱。",
+    "The device dependency could not be deleted.": "無法刪除è£ç½®ç›¸ä¾æ€§",
+    "The device dependency could not be fetched.": "無法擷å–è£ç½®ç›¸ä¾æ€§",
+    "The device dependency could not be saved.": "無法儲存è£ç½®ç›¸ä¾æ€§",
+    "The device group could not be deleted": "無法刪除è£ç½®ç¾¤çµ„",
+    "The following html tags are supported: ,