From 595484192f18bb2c8a66a29fd892459fa78f4edc Mon Sep 17 00:00:00 2001 From: Nassim Boutekedjiret Date: Tue, 12 Dec 2023 12:08:23 +0100 Subject: [PATCH 01/13] [#4] Develop Connector Reference Report Goal * Developed ReferenceReport that defines the `metricshub-connector-reference` Mojo. * Developed ConnectorLibraryParser to read and parse the connectors. * Developed ConnectorPageReferenceProducer to produce the connector page. * Developed MainPageReferenceProducer to produce the main connector page. * Developed PlatformReport that defines the `metricshub-connector-platforms` Mojo. * Developed unit tests and ITs. --- .prettierrc.yaml | 11 + README.md | 106 +- checkstyle.xml | 395 +++-- pom.xml | 207 ++- src/it/metricshub-connectors/pom.xml | 85 + .../connector/hardware/DiskPart/DiskPart.yaml | 111 ++ .../hardware/DiskPart/embeddedFile-1 | 2 + .../hardware/DiskPart/embeddedFile-2 | 97 ++ .../hardware/DiskPart/embeddedFile-3 | 1 + .../hardware/GenBatteryNT/GenBatteryNT.yaml | 141 ++ .../GenericSwitchEnclosure.yaml | 67 + .../hardware/GenericUPS/GenericUPS.yaml | 349 +++++ .../hardware/GenericUPS/embeddedFile-1 | 12 + .../hardware/GenericUPS/embeddedFile-2 | 12 + .../connector/hardware/Hardware/Hardware.yaml | 322 ++++ .../connector/hardware/HyperV/HyperV.yaml | 89 ++ .../connector/hardware/IpmiTool/IpmiTool.yaml | 808 ++++++++++ .../hardware/IpmiTool/embeddedFile-1 | 1225 +++++++++++++++ .../LibreHardwareMonitor.yaml | 564 +++++++ .../LibreHardwareMonitor/embeddedFile-1 | 18 + .../LibreHardwareMonitor/embeddedFile-2 | 87 ++ .../LibreHardwareMonitor/embeddedFile-3 | 21 + .../LinuxIPNetwork/LinuxIPNetwork.yaml | 21 + .../hardware/LinuxIPNetwork/embeddedFile-1 | 25 + .../hardware/LinuxIPNetwork/embeddedFile-2 | 28 + .../LinuxIfConfigNetwork.yaml | 19 + .../LinuxIfConfigNetwork/embeddedFile-1 | 28 + .../LinuxIfConfigNetwork/embeddedFile-2 | 33 + .../LinuxMultipath/LinuxMultipath.yaml | 79 + .../hardware/LinuxMultipath/embeddedFile-1 | 49 + .../LinuxNetwork-header.yaml | 91 ++ .../LinuxNetwork-header/embeddedFile-3 | 44 + .../hardware/LinuxNetwork/LinuxNetwork.yaml | 1 + .../hardware/MIB2-header/MIB2-header.yaml | 268 ++++ .../main/connector/hardware/MIB2/MIB2.yaml | 55 + .../MIB2EntitySensor-header.yaml | 262 ++++ .../MIB2EntitySensor-header/embeddedFile-1 | 6 + .../hardware/MIB2Linux/MIB2Linux.yaml | 36 + .../connector/hardware/MIB2NT/MIB2NT.yaml | 64 + .../hardware/MIB2Solaris/MIB2Solaris.yaml | 55 + .../hardware/MIB2Solaris/embeddedFile-1 | 16 + .../hardware/MIB2Switch/MIB2Switch.yaml | 57 + .../hardware/NvidiaSmi/NvidiaSmi.yaml | 209 +++ .../hardware/NvidiaSmi/embeddedFile-1 | 64 + .../SunEthernetDriver-header.yaml | 31 + .../hardware/VMwareESX4i/VMwareESX4i.yaml | 26 + .../VMwareESX5iLUN-header.yaml | 138 ++ .../VMwareESX5iLUN-header/embeddedFile-1 | 27 + .../VMwareESX5iLUN-header/embeddedFile-2 | 15 + .../VMwareESX5iLUNSSH/VMwareESX5iLUNSSH.yaml | 20 + .../VMwareESX5iLUNesxcli.yaml | 23 + .../VMwareESXVMSNMP/VMwareESXVMSNMP.yaml | 71 + .../VMwareESXi-header/VMwareESXi-header.yaml | 1362 +++++++++++++++++ .../hardware/VMwareESXi-header/embeddedFile-1 | 42 + .../hardware/VMwareESXi-header/embeddedFile-2 | 16 + .../hardware/VMwareESXi-header/embeddedFile-3 | 28 + .../hardware/VMwareESXi-header/embeddedFile-4 | 37 + .../hardware/VMwareESXi/VMwareESXi.yaml | 25 + .../VMwareESXiDisksIPMI.yaml | 130 ++ .../VMwareESXiDisksIPMI/embeddedFile-1 | 16 + .../VMwareESXiDisksStorage.yaml | 239 +++ .../main/connector/hardware/Virsh/Virsh.yaml | 69 + .../connector/hardware/Virsh/embeddedFile-1 | 10 + .../hardware/WBEMGenDiskNT/WBEMGenDiskNT.yaml | 236 +++ .../hardware/WBEMGenHBA/WBEMGenHBA.yaml | 247 +++ .../hardware/WBEMGenHBA/embeddedFile-1 | 20 + .../hardware/WBEMGenLUN/WBEMGenLUN.yaml | 201 +++ .../hardware/WBEMGenLUN/embeddedFile-1 | 9 + .../hardware/WBEMGenLUN/embeddedFile-2 | 10 + .../hardware/WBEMGenLUN/embeddedFile-3 | 2 + .../WBEMGenNetwork/WBEMGenNetwork.yaml | 314 ++++ .../WBEMGenericDiskNT/WBEMGenericDiskNT.yaml | 1 + .../hardware/WBEMGenericNT/WBEMGenericNT.yaml | 1 + .../WinStorageSpaces/WinStorageSpaces.yaml | 544 +++++++ .../hardware/WinStorageSpaces/embeddedFile-1 | 36 + .../hardware/WinStorageSpaces/embeddedFile-2 | 22 + .../hardware/WinStorageSpaces/embeddedFile-3 | 12 + .../hardware/WinStorageSpaces/embeddedFile-4 | 16 + .../hardware/lmsensors/embeddedFile-1 | 8 + .../hardware/lmsensors/embeddedFile-2 | 8 + .../hardware/lmsensors/embeddedFile-3 | 24 + .../hardware/lmsensors/embeddedFile-4 | 12 + .../hardware/lmsensors/embeddedFile-5 | 16 + .../hardware/lmsensors/lmsensors.yaml | 211 +++ .../src/site/markdown/index.md | 3 + .../resources/images/sentry-logo-179x75px.png | Bin 0 -> 4321 bytes .../metricshub-connectors/src/site/site.xml | 44 + src/it/metricshub-connectors/verify.groovy | 262 ++++ src/it/settings.xml | 40 + src/main/java/lombok.config | 2 + .../connector/AbstractConnectorReport.java | 125 ++ .../maven/metricshub/connector/Constants.java | 49 + .../metricshub/connector/PlatformReport.java | 162 ++ .../metricshub/connector/ReferenceReport.java | 175 +++ .../metricshub/connector/package-info.java | 25 + .../parser/AbstractNodeProcessor.java | 64 + .../parser/ConnectorLibraryParser.java | 116 ++ .../connector/parser/ConnectorParser.java | 66 + .../connector/parser/ConstantsProcessor.java | 94 ++ .../connector/parser/ExtendsProcessor.java | 171 +++ .../connector/parser/JsonNodeUpdater.java | 113 ++ .../connector/parser/NodeProcessorHelper.java | 55 + .../connector/parser/package-info.java | 25 + .../producer/ConnectorJsonNodeReader.java | 595 +++++++ .../ConnectorPageReferenceProducer.java | 651 ++++++++ .../connector/producer/JsonNodeHelper.java | 145 ++ .../producer/MainPageReferenceProducer.java | 158 ++ .../producer/PlatformReportProducer.java | 223 +++ .../connector/producer/SinkHelper.java | 156 ++ .../common/OpenTelemetryHardwareType.java | 140 ++ .../producer/model/common/OsType.java | 156 ++ .../producer/model/common/TechnologyType.java | 103 ++ .../producer/model/common/package-info.java} | 23 +- .../model/criteria/AbstractCriterion.java | 59 + .../model/criteria/AbstractSnmpCriterion.java | 64 + .../model/criteria/AbstractWqlCriterion.java | 69 + .../model/criteria/CriterionFactory.java | 198 +++ .../criteria/CriterionSinkProduceVisitor.java | 302 ++++ .../model/criteria/DeviceTypeCriterion.java | 82 + .../model/criteria/HttpCriterion.java | 96 ++ .../model/criteria/ICriterionVisitor.java | 107 ++ .../model/criteria/IpmiCriterion.java | 47 + .../model/criteria/OsCommandCriterion.java | 70 + .../model/criteria/ProcessCriterion.java | 58 + .../ProductRequirementsCriterion.java | 58 + .../model/criteria/ServiceCriterion.java | 58 + .../model/criteria/SnmpGetCriterion.java | 54 + .../model/criteria/SnmpGetNextCriterion.java | 54 + .../model/criteria/WbemCriterion.java | 54 + .../producer/model/criteria/WmiCriterion.java | 54 + .../producer/model/criteria/package-info.java | 26 + .../producer/model/platform/Platform.java | 187 +++ .../producer/model/platform/package-info.java | 23 + .../connector/producer/package-info.java | 26 + src/site/markdown/index.md | 86 +- .../resources/images/sentry-logo-179x75px.png | Bin 6001 -> 4321 bytes src/site/site.xml | 5 +- .../parser/ConnectorLibraryParserTest.java | 108 ++ .../connector/Hardware/Hardware.yaml | 322 ++++ .../connector/MIB2-header/MIB2-header.yaml | 272 ++++ src/test/resources/connector/MIB2/MIB2.yaml | 55 + 141 files changed, 16748 insertions(+), 302 deletions(-) create mode 100644 .prettierrc.yaml create mode 100644 src/it/metricshub-connectors/pom.xml create mode 100644 src/it/metricshub-connectors/src/main/connector/hardware/DiskPart/DiskPart.yaml create mode 100644 src/it/metricshub-connectors/src/main/connector/hardware/DiskPart/embeddedFile-1 create mode 100644 src/it/metricshub-connectors/src/main/connector/hardware/DiskPart/embeddedFile-2 create mode 100644 src/it/metricshub-connectors/src/main/connector/hardware/DiskPart/embeddedFile-3 create mode 100644 src/it/metricshub-connectors/src/main/connector/hardware/GenBatteryNT/GenBatteryNT.yaml create mode 100644 src/it/metricshub-connectors/src/main/connector/hardware/GenericSwitchEnclosure/GenericSwitchEnclosure.yaml create mode 100644 src/it/metricshub-connectors/src/main/connector/hardware/GenericUPS/GenericUPS.yaml create mode 100644 src/it/metricshub-connectors/src/main/connector/hardware/GenericUPS/embeddedFile-1 create mode 100644 src/it/metricshub-connectors/src/main/connector/hardware/GenericUPS/embeddedFile-2 create mode 100644 src/it/metricshub-connectors/src/main/connector/hardware/Hardware/Hardware.yaml create mode 100644 src/it/metricshub-connectors/src/main/connector/hardware/HyperV/HyperV.yaml create mode 100644 src/it/metricshub-connectors/src/main/connector/hardware/IpmiTool/IpmiTool.yaml create mode 100644 src/it/metricshub-connectors/src/main/connector/hardware/IpmiTool/embeddedFile-1 create mode 100644 src/it/metricshub-connectors/src/main/connector/hardware/LibreHardwareMonitor/LibreHardwareMonitor.yaml create mode 100644 src/it/metricshub-connectors/src/main/connector/hardware/LibreHardwareMonitor/embeddedFile-1 create mode 100644 src/it/metricshub-connectors/src/main/connector/hardware/LibreHardwareMonitor/embeddedFile-2 create mode 100644 src/it/metricshub-connectors/src/main/connector/hardware/LibreHardwareMonitor/embeddedFile-3 create mode 100644 src/it/metricshub-connectors/src/main/connector/hardware/LinuxIPNetwork/LinuxIPNetwork.yaml create mode 100644 src/it/metricshub-connectors/src/main/connector/hardware/LinuxIPNetwork/embeddedFile-1 create mode 100644 src/it/metricshub-connectors/src/main/connector/hardware/LinuxIPNetwork/embeddedFile-2 create mode 100644 src/it/metricshub-connectors/src/main/connector/hardware/LinuxIfConfigNetwork/LinuxIfConfigNetwork.yaml create mode 100644 src/it/metricshub-connectors/src/main/connector/hardware/LinuxIfConfigNetwork/embeddedFile-1 create mode 100644 src/it/metricshub-connectors/src/main/connector/hardware/LinuxIfConfigNetwork/embeddedFile-2 create mode 100644 src/it/metricshub-connectors/src/main/connector/hardware/LinuxMultipath/LinuxMultipath.yaml create mode 100644 src/it/metricshub-connectors/src/main/connector/hardware/LinuxMultipath/embeddedFile-1 create mode 100644 src/it/metricshub-connectors/src/main/connector/hardware/LinuxNetwork-header/LinuxNetwork-header.yaml create mode 100644 src/it/metricshub-connectors/src/main/connector/hardware/LinuxNetwork-header/embeddedFile-3 create mode 100644 src/it/metricshub-connectors/src/main/connector/hardware/LinuxNetwork/LinuxNetwork.yaml create mode 100644 src/it/metricshub-connectors/src/main/connector/hardware/MIB2-header/MIB2-header.yaml create mode 100644 src/it/metricshub-connectors/src/main/connector/hardware/MIB2/MIB2.yaml create mode 100644 src/it/metricshub-connectors/src/main/connector/hardware/MIB2EntitySensor-header/MIB2EntitySensor-header.yaml create mode 100644 src/it/metricshub-connectors/src/main/connector/hardware/MIB2EntitySensor-header/embeddedFile-1 create mode 100644 src/it/metricshub-connectors/src/main/connector/hardware/MIB2Linux/MIB2Linux.yaml create mode 100644 src/it/metricshub-connectors/src/main/connector/hardware/MIB2NT/MIB2NT.yaml create mode 100644 src/it/metricshub-connectors/src/main/connector/hardware/MIB2Solaris/MIB2Solaris.yaml create mode 100644 src/it/metricshub-connectors/src/main/connector/hardware/MIB2Solaris/embeddedFile-1 create mode 100644 src/it/metricshub-connectors/src/main/connector/hardware/MIB2Switch/MIB2Switch.yaml create mode 100644 src/it/metricshub-connectors/src/main/connector/hardware/NvidiaSmi/NvidiaSmi.yaml create mode 100644 src/it/metricshub-connectors/src/main/connector/hardware/NvidiaSmi/embeddedFile-1 create mode 100644 src/it/metricshub-connectors/src/main/connector/hardware/SunEthernetDriver-header/SunEthernetDriver-header.yaml create mode 100644 src/it/metricshub-connectors/src/main/connector/hardware/VMwareESX4i/VMwareESX4i.yaml create mode 100644 src/it/metricshub-connectors/src/main/connector/hardware/VMwareESX5iLUN-header/VMwareESX5iLUN-header.yaml create mode 100644 src/it/metricshub-connectors/src/main/connector/hardware/VMwareESX5iLUN-header/embeddedFile-1 create mode 100644 src/it/metricshub-connectors/src/main/connector/hardware/VMwareESX5iLUN-header/embeddedFile-2 create mode 100644 src/it/metricshub-connectors/src/main/connector/hardware/VMwareESX5iLUNSSH/VMwareESX5iLUNSSH.yaml create mode 100644 src/it/metricshub-connectors/src/main/connector/hardware/VMwareESX5iLUNesxcli/VMwareESX5iLUNesxcli.yaml create mode 100644 src/it/metricshub-connectors/src/main/connector/hardware/VMwareESXVMSNMP/VMwareESXVMSNMP.yaml create mode 100644 src/it/metricshub-connectors/src/main/connector/hardware/VMwareESXi-header/VMwareESXi-header.yaml create mode 100644 src/it/metricshub-connectors/src/main/connector/hardware/VMwareESXi-header/embeddedFile-1 create mode 100644 src/it/metricshub-connectors/src/main/connector/hardware/VMwareESXi-header/embeddedFile-2 create mode 100644 src/it/metricshub-connectors/src/main/connector/hardware/VMwareESXi-header/embeddedFile-3 create mode 100644 src/it/metricshub-connectors/src/main/connector/hardware/VMwareESXi-header/embeddedFile-4 create mode 100644 src/it/metricshub-connectors/src/main/connector/hardware/VMwareESXi/VMwareESXi.yaml create mode 100644 src/it/metricshub-connectors/src/main/connector/hardware/VMwareESXiDisksIPMI/VMwareESXiDisksIPMI.yaml create mode 100644 src/it/metricshub-connectors/src/main/connector/hardware/VMwareESXiDisksIPMI/embeddedFile-1 create mode 100644 src/it/metricshub-connectors/src/main/connector/hardware/VMwareESXiDisksStorage/VMwareESXiDisksStorage.yaml create mode 100644 src/it/metricshub-connectors/src/main/connector/hardware/Virsh/Virsh.yaml create mode 100644 src/it/metricshub-connectors/src/main/connector/hardware/Virsh/embeddedFile-1 create mode 100644 src/it/metricshub-connectors/src/main/connector/hardware/WBEMGenDiskNT/WBEMGenDiskNT.yaml create mode 100644 src/it/metricshub-connectors/src/main/connector/hardware/WBEMGenHBA/WBEMGenHBA.yaml create mode 100644 src/it/metricshub-connectors/src/main/connector/hardware/WBEMGenHBA/embeddedFile-1 create mode 100644 src/it/metricshub-connectors/src/main/connector/hardware/WBEMGenLUN/WBEMGenLUN.yaml create mode 100644 src/it/metricshub-connectors/src/main/connector/hardware/WBEMGenLUN/embeddedFile-1 create mode 100644 src/it/metricshub-connectors/src/main/connector/hardware/WBEMGenLUN/embeddedFile-2 create mode 100644 src/it/metricshub-connectors/src/main/connector/hardware/WBEMGenLUN/embeddedFile-3 create mode 100644 src/it/metricshub-connectors/src/main/connector/hardware/WBEMGenNetwork/WBEMGenNetwork.yaml create mode 100644 src/it/metricshub-connectors/src/main/connector/hardware/WBEMGenericDiskNT/WBEMGenericDiskNT.yaml create mode 100644 src/it/metricshub-connectors/src/main/connector/hardware/WBEMGenericNT/WBEMGenericNT.yaml create mode 100644 src/it/metricshub-connectors/src/main/connector/hardware/WinStorageSpaces/WinStorageSpaces.yaml create mode 100644 src/it/metricshub-connectors/src/main/connector/hardware/WinStorageSpaces/embeddedFile-1 create mode 100644 src/it/metricshub-connectors/src/main/connector/hardware/WinStorageSpaces/embeddedFile-2 create mode 100644 src/it/metricshub-connectors/src/main/connector/hardware/WinStorageSpaces/embeddedFile-3 create mode 100644 src/it/metricshub-connectors/src/main/connector/hardware/WinStorageSpaces/embeddedFile-4 create mode 100644 src/it/metricshub-connectors/src/main/connector/hardware/lmsensors/embeddedFile-1 create mode 100644 src/it/metricshub-connectors/src/main/connector/hardware/lmsensors/embeddedFile-2 create mode 100644 src/it/metricshub-connectors/src/main/connector/hardware/lmsensors/embeddedFile-3 create mode 100644 src/it/metricshub-connectors/src/main/connector/hardware/lmsensors/embeddedFile-4 create mode 100644 src/it/metricshub-connectors/src/main/connector/hardware/lmsensors/embeddedFile-5 create mode 100644 src/it/metricshub-connectors/src/main/connector/hardware/lmsensors/lmsensors.yaml create mode 100644 src/it/metricshub-connectors/src/site/markdown/index.md create mode 100644 src/it/metricshub-connectors/src/site/resources/images/sentry-logo-179x75px.png create mode 100644 src/it/metricshub-connectors/src/site/site.xml create mode 100644 src/it/metricshub-connectors/verify.groovy create mode 100644 src/it/settings.xml create mode 100644 src/main/java/lombok.config create mode 100644 src/main/java/org/sentrysoftware/maven/metricshub/connector/AbstractConnectorReport.java create mode 100644 src/main/java/org/sentrysoftware/maven/metricshub/connector/Constants.java create mode 100644 src/main/java/org/sentrysoftware/maven/metricshub/connector/PlatformReport.java create mode 100644 src/main/java/org/sentrysoftware/maven/metricshub/connector/ReferenceReport.java create mode 100644 src/main/java/org/sentrysoftware/maven/metricshub/connector/package-info.java create mode 100644 src/main/java/org/sentrysoftware/maven/metricshub/connector/parser/AbstractNodeProcessor.java create mode 100644 src/main/java/org/sentrysoftware/maven/metricshub/connector/parser/ConnectorLibraryParser.java create mode 100644 src/main/java/org/sentrysoftware/maven/metricshub/connector/parser/ConnectorParser.java create mode 100644 src/main/java/org/sentrysoftware/maven/metricshub/connector/parser/ConstantsProcessor.java create mode 100644 src/main/java/org/sentrysoftware/maven/metricshub/connector/parser/ExtendsProcessor.java create mode 100644 src/main/java/org/sentrysoftware/maven/metricshub/connector/parser/JsonNodeUpdater.java create mode 100644 src/main/java/org/sentrysoftware/maven/metricshub/connector/parser/NodeProcessorHelper.java create mode 100644 src/main/java/org/sentrysoftware/maven/metricshub/connector/parser/package-info.java create mode 100644 src/main/java/org/sentrysoftware/maven/metricshub/connector/producer/ConnectorJsonNodeReader.java create mode 100644 src/main/java/org/sentrysoftware/maven/metricshub/connector/producer/ConnectorPageReferenceProducer.java create mode 100644 src/main/java/org/sentrysoftware/maven/metricshub/connector/producer/JsonNodeHelper.java create mode 100644 src/main/java/org/sentrysoftware/maven/metricshub/connector/producer/MainPageReferenceProducer.java create mode 100644 src/main/java/org/sentrysoftware/maven/metricshub/connector/producer/PlatformReportProducer.java create mode 100644 src/main/java/org/sentrysoftware/maven/metricshub/connector/producer/SinkHelper.java create mode 100644 src/main/java/org/sentrysoftware/maven/metricshub/connector/producer/model/common/OpenTelemetryHardwareType.java create mode 100644 src/main/java/org/sentrysoftware/maven/metricshub/connector/producer/model/common/OsType.java create mode 100644 src/main/java/org/sentrysoftware/maven/metricshub/connector/producer/model/common/TechnologyType.java rename src/main/java/org/sentrysoftware/{example/Example.java => maven/metricshub/connector/producer/model/common/package-info.java} (77%) create mode 100644 src/main/java/org/sentrysoftware/maven/metricshub/connector/producer/model/criteria/AbstractCriterion.java create mode 100644 src/main/java/org/sentrysoftware/maven/metricshub/connector/producer/model/criteria/AbstractSnmpCriterion.java create mode 100644 src/main/java/org/sentrysoftware/maven/metricshub/connector/producer/model/criteria/AbstractWqlCriterion.java create mode 100644 src/main/java/org/sentrysoftware/maven/metricshub/connector/producer/model/criteria/CriterionFactory.java create mode 100644 src/main/java/org/sentrysoftware/maven/metricshub/connector/producer/model/criteria/CriterionSinkProduceVisitor.java create mode 100644 src/main/java/org/sentrysoftware/maven/metricshub/connector/producer/model/criteria/DeviceTypeCriterion.java create mode 100644 src/main/java/org/sentrysoftware/maven/metricshub/connector/producer/model/criteria/HttpCriterion.java create mode 100644 src/main/java/org/sentrysoftware/maven/metricshub/connector/producer/model/criteria/ICriterionVisitor.java create mode 100644 src/main/java/org/sentrysoftware/maven/metricshub/connector/producer/model/criteria/IpmiCriterion.java create mode 100644 src/main/java/org/sentrysoftware/maven/metricshub/connector/producer/model/criteria/OsCommandCriterion.java create mode 100644 src/main/java/org/sentrysoftware/maven/metricshub/connector/producer/model/criteria/ProcessCriterion.java create mode 100644 src/main/java/org/sentrysoftware/maven/metricshub/connector/producer/model/criteria/ProductRequirementsCriterion.java create mode 100644 src/main/java/org/sentrysoftware/maven/metricshub/connector/producer/model/criteria/ServiceCriterion.java create mode 100644 src/main/java/org/sentrysoftware/maven/metricshub/connector/producer/model/criteria/SnmpGetCriterion.java create mode 100644 src/main/java/org/sentrysoftware/maven/metricshub/connector/producer/model/criteria/SnmpGetNextCriterion.java create mode 100644 src/main/java/org/sentrysoftware/maven/metricshub/connector/producer/model/criteria/WbemCriterion.java create mode 100644 src/main/java/org/sentrysoftware/maven/metricshub/connector/producer/model/criteria/WmiCriterion.java create mode 100644 src/main/java/org/sentrysoftware/maven/metricshub/connector/producer/model/criteria/package-info.java create mode 100644 src/main/java/org/sentrysoftware/maven/metricshub/connector/producer/model/platform/Platform.java create mode 100644 src/main/java/org/sentrysoftware/maven/metricshub/connector/producer/model/platform/package-info.java create mode 100644 src/main/java/org/sentrysoftware/maven/metricshub/connector/producer/package-info.java create mode 100644 src/test/java/org/sentrysoftware/maven/metricshub/connector/parser/ConnectorLibraryParserTest.java create mode 100644 src/test/resources/connector/Hardware/Hardware.yaml create mode 100644 src/test/resources/connector/MIB2-header/MIB2-header.yaml create mode 100644 src/test/resources/connector/MIB2/MIB2.yaml diff --git a/.prettierrc.yaml b/.prettierrc.yaml new file mode 100644 index 0000000..45cefe1 --- /dev/null +++ b/.prettierrc.yaml @@ -0,0 +1,11 @@ +overrides: + - files: + - "**/*.java" + options: + trailingComma: none + useTabs: true + tabWidth: 2 + semi: false + singleQuote: false + printWidth: 120 + arrowParens: avoid diff --git a/README.md b/README.md index 9721f79..d98d906 100644 --- a/README.md +++ b/README.md @@ -1,39 +1,67 @@ -# oss-maven-template - -![GitHub release (with filter)](https://img.shields.io/github/v/release/sentrysoftware/oss-maven-template) -![Build](https://img.shields.io/github/actions/workflow/status/sentrysoftware/oss-maven-template/deploy.yml) -![GitHub top language](https://img.shields.io/github/languages/top/sentrysoftware/oss-maven-template) -![License](https://img.shields.io/github/license/sentrysoftware/oss-maven-template) - -Repository template for all Sentry open-source Java projects, published on Maven Central. - -## Build instructions - -This is a simple Maven project. Build with: - -```bash -mvn verify -``` - -## Release instructions - -The artifact is deployed to Sonatype's [Maven Central](https://central.sonatype.com/). - -The actual repository URL is https://s01.oss.sonatype.org/, with server Id `ossrh` and requires credentials to deploy -artifacts manually. - -But it is strongly recommended to only use [GitHub Actions "Release to Maven Central"](actions/workflows/release.yml) to perform a release: - -* Manually trigger the "Release" workflow -* Specify the version being released and the next version number (SNAPSHOT) -* Release the corresponding staging repository on [Sonatype's Nexus server](https://s01.oss.sonatype.org/) -* Merge the PR that has been created to prepare the next version - -## License - -License is Apache-2. Each source file must include the Apache-2 header (build will fail otherwise). -To update source files with the proper header, simply execute the below command: - -```bash -mvn license:update-file-header -``` +# MetricsHub Connector Maven Plugin + +This is a Maven Report plugin, which is invoked by Maven's site plugin in the `site` lifecycle. + +This plugin is designed to be used with the [MetricsHub Community Connectors](https://github.com/sentrysoftware/metricshub-community-connectors). + +It reads the connector files from a source directory (E.g. `./src/main/connector`), parses the `.yaml` files and produces the corresponding Reference Guide, as a set of HTML documents (through Doxia's Sink API), which is integrated into the project's documentation. + +See **[Project Documentation](https://sentrysoftware.github.io/metricshub-connector-maven-plugin)** for more information on how to use this plugin in your maven project. + +## Build instructions + +This is a simple Maven Plugin project. Build with: + +```bash +mvn verify +``` + +## Integration Tests + +While modifying the *MetricsHub Connector Maven Plugin*, you will want to see how your changes are reflected in a *test* documentation project. + +Conveniently, the project comes with integration tests, i.e. a documentation project that is automatically built with the skin as it is in the workspace. +The integration test is run with the below command: + +```bash +mvn verify +``` + +This command builds the skin and run it against a documentation project. The result can be seen in `./metricshub-connector-maven-plugin/target/it/metricshub-connectors/site/*.html`. + +We recommend running [http-server](https://github.com/http-party/http-server#readme) to browse the result. Install with: + +```bash +npm install --global http-server +``` + +Launch a Web server with the generated test documentation with: + +```bash +http-server metricshub-connector-maven-plugin/target/it/metricshub-connectors/target/site +``` + +In case of a build failure, the output of the build is stored in `./metricshub-connector-maven-plugin/target/it/metricshub-connectors/build.log`. + +## Release instructions + +The artifact is deployed to Sonatype's [Maven Central](https://central.sonatype.com/). + +The actual repository URL is https://s01.oss.sonatype.org/, with server Id `ossrh` and requires credentials to deploy +artifacts manually. + +But it is strongly recommended to only use [GitHub Actions "Release to Maven Central"](actions/workflows/release.yml) to perform a release: + +* Manually trigger the "Release" workflow +* Specify the version being released and the next version number (SNAPSHOT) +* Release the corresponding staging repository on [Sonatype's Nexus server](https://s01.oss.sonatype.org/) +* Merge the PR that has been created to prepare the next version + +## License + +License is Apache-2. Each source file must include the Apache-2 header (build will fail otherwise). +To update source files with the proper header, simply execute the below command: + +```bash +mvn license:update-file-header +``` \ No newline at end of file diff --git a/checkstyle.xml b/checkstyle.xml index 2098fe6..889d74d 100644 --- a/checkstyle.xml +++ b/checkstyle.xml @@ -1,222 +1,179 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + "-//Checkstyle//DTD Checkstyle Configuration 1.3//EN" + "https://checkstyle.org/dtds/configuration_1_3.dtd"> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/pom.xml b/pom.xml index 6494976..2e19886 100644 --- a/pom.xml +++ b/pom.xml @@ -1,19 +1,29 @@ - + 4.0.0 - org.sentrysoftware - MYARTIFACT - MY PROJECT - 0.1.00-SNAPSHOT - SOME DESCRIPTION - jar + org.sentrysoftware.maven + metricshub-connector-maven-plugin + MetricsHub Connector Maven Plugin + 1.0.00-SNAPSHOT + + The MetricsHub Connector Maven Plugin is a Maven report plugin that + helps generate documentation for MetricsHub connectors. + It is invoked during the Maven site generation process, and it takes the + source code of the connectors as input. + The plugin then generates a reference guide that describes the + connectors in detail. + + + maven-plugin Sentry Software https://sentrysoftware.com - https://github.com/sentrysoftware/MYREPO + https://sentrysoftware.github.io/metricshub-connector-maven-plugin 2023 @@ -23,7 +33,8 @@ ossrh - https://s01.oss.sonatype.org/service/local/staging/deploy/maven2/ + + https://s01.oss.sonatype.org/service/local/staging/deploy/maven2/ @@ -37,29 +48,34 @@ GitHub - https://github.com/sentrysoftware/MYREPO/issues/ + https://github.com/sentrysoftware/metricshub-connector-maven-plugin/issues/ - scm:git:https://github.com/sentrysoftware/MYREPO.git - https://github.com/sentrysoftware/MYREPO + scm:git:https://github.com/sentrysoftware/metricshub-connector-maven-plugin.git + https://github.com/sentrysoftware/metricshub-connector-maven-plugin HEAD - MY NAME (@MY_ID) - MYNAME@sentrysoftware.com + Bertrand Martin (@bertysentry) + bertrand@sentrysoftware.com - Project founder + maintainer + + + + Nassim Boutekedjiret (@NassimBtk) + nassim@sentrysoftware.com + + maintainer - 11 - 11 11 @@ -85,16 +101,94 @@ + + + + org.projectlombok + lombok + 1.18.30 + compile + true + + + + + org.apache.maven.reporting + maven-reporting-impl + 4.0.0-M13 + + + org.apache.maven + maven-core + + + org.apache.maven + maven-artifact + + + + + + + org.apache.maven.reporting + maven-reporting-api + 4.0.0-M9 + + + + + org.apache.maven + maven-plugin-api + 3.9.6 + provided + + + + + org.apache.maven.plugin-tools + maven-plugin-annotations + 3.10.2 + provided + + + + + org.apache.maven + maven-project + 3.0-alpha-2 + provided + + + + + com.fasterxml.jackson.core + jackson-databind + 2.16.0 + + + com.fasterxml.jackson.core + jackson-core + 2.16.0 + + + com.fasterxml.jackson.dataformat + jackson-dataformat-yaml + 2.16.0 + + org.junit.platform junit-platform-launcher test + + org.junit.jupiter junit-jupiter-engine test + @@ -134,9 +228,9 @@ attach-sources - - jar - + + jar + @@ -185,7 +279,7 @@ maven-javadoc-plugin - attach-javadocs + attach-javadocs jar @@ -224,6 +318,75 @@ 3.1.1 + + + org.apache.maven.plugins + maven-plugin-plugin + 3.10.2 + + metricshub-connector + true + + + + mojo-descriptor + + descriptor + + + + help-goal + + helpmojo + + + + + + + + com.hubspot.maven.plugins + prettier-maven-plugin + 0.21 + + 2.5.0 + false + false + + src/main/java/**/*.java + src/test/java/**/*.java + + + + + + + maven-invoker-plugin + 3.6.0 + + true + ${project.build.directory}/it + + **/pom.xml + + verify + ${project.build.directory}/local-repo + src/it/settings.xml + + clean + site + + + + + integration-test + + install + run + + + + @@ -304,7 +467,7 @@ - + release diff --git a/src/it/metricshub-connectors/pom.xml b/src/it/metricshub-connectors/pom.xml new file mode 100644 index 0000000..3229298 --- /dev/null +++ b/src/it/metricshub-connectors/pom.xml @@ -0,0 +1,85 @@ + + + 4.0.0 + + org.sentrysoftware.maven + metricshub-connectors + 1.0.00-SNAPSHOT + pom + + A full documentation project + + 1975 + + + The Organization + https://the.org + + + + scm:git:https://git.sentrysoftware.integration-test.org/sentrysoftware/metricshub-connectors.git + https://git.sentrysoftware.integration-test.org/sentrysoftware/metricshub-connectors + HEAD + + + + @project.version@ + 1975-03-24 19:30:00 + + + + 2023-11-09T14:52:23Z + + + + + + + + + org.apache.maven.plugins + maven-site-plugin + 3.12.1 + + true + true + UTF-8 + UTF-8 + + + + org.sentrysoftware.maven + maven-skin-tools + 1.2.00 + + + + + + + + + + + + + + @project.groupId@ + @project.artifactId@ + @project.version@ + + + + + org.apache.maven.plugins + maven-project-info-reports-plugin + 3.4.5 + + + + + + \ No newline at end of file diff --git a/src/it/metricshub-connectors/src/main/connector/hardware/DiskPart/DiskPart.yaml b/src/it/metricshub-connectors/src/main/connector/hardware/DiskPart/DiskPart.yaml new file mode 100644 index 0000000..c544405 --- /dev/null +++ b/src/it/metricshub-connectors/src/main/connector/hardware/DiskPart/DiskPart.yaml @@ -0,0 +1,111 @@ +--- +extends: +- ../Hardware/Hardware +connector: + displayName: Windows - DiskPart + platforms: Any system + reliesOn: The DISKPART.EXE command-line utility + information: "Discovers and monitors the logical disks in a Microsoft Windows system through the DISKPART.EXE utility, notably the software RAID volumes." + version: 1.0 + detection: + appliesTo: + - NT + criteria: + # OS should be Windows + - type: deviceType + keep: + - NT + # Diskpart must be int he path and return something meaningful. Please note that we're using CHCP 437 to make sure the locale is English. + - type: osCommand + commandLine: "CHCP 437&&DISKPART.EXE /S ${file::embeddedFile-3}" + expectedResult: Leaving DiskPart + timeout: 120 +monitors: + logical_disk: + discovery: + sources: + source(1): + # Source(1) = DISKPART.EXE running "list volume" and "exit + type: osCommand + commandLine: "CHCP 437&&DISKPART.EXE /S ${file::embeddedFile-1}" + timeout: 120 + computes: + # Run Source(1) through an AWK script + # ID;Label;Letter;VolumeType;FileSystem;Size;Status;MoreInformation + - type: awk + script: "${file::embeddedFile-2}" + separators: ; + keep: ^MSHW; + selectColumns: "2,3,4,5,6,7,8,9" + # Exclude DVDs and stupid stuff + # ID;Label;Letter;VolumeType;FileSystem;Size;Status;MoreInformation + - type: excludeMatchingLines + column: 4 + valueList: "CD-ROM,DVD-ROM,Removable" + - type: leftConcat + column: 5 + value: "File System: " + - type: leftConcat + column: 8 + value: "Volume Information: " + mapping: + # The InstanceTable + source: "${source::monitors.logical_disk.discovery.sources.source(1)}" + attributes: + id: $1 + __display_id: $2 + raid_level: $4 + info: "${awk::join(\" \", $5, $8)}" + hw.parent.type: enclosure + name: "${awk::sprintf(\"%s (%s - %s)\", $2, $4, bytes2HumanFormatBase2($6))}" + metrics: + hw.logical_disk.limit: $6 + collect: + # Collection is multi-instance + type: multiInstance + keys: + - id + sources: + source(1): + # Source(1) = DISKPART.EXE running "list volume" and "exit + type: osCommand + commandLine: "CHCP 437&&DISKPART.EXE /S ${file::embeddedFile-1}" + timeout: 120 + computes: + # Run Source(1) through an AWK script + # ID;Status; + - type: awk + script: "${file::embeddedFile-2}" + separators: ; + keep: ^MSHW; + selectColumns: "2,8" + # Duplicate the status column + # ID;Status;Status + - type: duplicateColumn + column: 2 + # Now translate Status to Patrol Status + # status + - type: translate + column: 2 + translationTable: "${translation::LogicalDiskTranslationTable}" + mapping: + # ValueTable = Source(1) + source: "${source::monitors.logical_disk.collect.sources.source(1)}" + attributes: + id: $1 + metrics: + hw.status{hw.type="logical_disk"}: $2 + legacyTextParameters: + StatusInformation: $3 +translations: + LogicalDiskTranslationTable: + Resynching: degraded + Unknown: failed + Failed: failed + Formatting: ok + Failed Rd: degraded + Failed Redundancy: degraded + Default: UNKNOWN + Healthy: ok + Regenerating: degraded + Rebuild: degraded diff --git a/src/it/metricshub-connectors/src/main/connector/hardware/DiskPart/embeddedFile-1 b/src/it/metricshub-connectors/src/main/connector/hardware/DiskPart/embeddedFile-1 new file mode 100644 index 0000000..7862852 --- /dev/null +++ b/src/it/metricshub-connectors/src/main/connector/hardware/DiskPart/embeddedFile-1 @@ -0,0 +1,2 @@ +list volume +exit \ No newline at end of file diff --git a/src/it/metricshub-connectors/src/main/connector/hardware/DiskPart/embeddedFile-2 b/src/it/metricshub-connectors/src/main/connector/hardware/DiskPart/embeddedFile-2 new file mode 100644 index 0000000..d717c26 --- /dev/null +++ b/src/it/metricshub-connectors/src/main/connector/hardware/DiskPart/embeddedFile-2 @@ -0,0 +1,97 @@ +BEGIN { + foundHeader = 0; +} +($1 == "Volume" && $2 == "###" && $3 == "Ltr" && $4 == "Label" && $5 == "Fs" && $6 == "Type" && $7 == "Size" && $8 == "Status" && $9 == "Info") { + ltrIndex = index($0, "Ltr") + labelIndex = index($0, "Label") + fsIndex = index($0, "Fs") + typeIndex = index($0, "Type") + sizeIndex = index($0, "Size") + statusIndex = index($0, "Status") + infoIndex = index($0, "Info") + foundHeader = 1; +} +($1 == "Volume" && $2 ~ /^[0-9]+$/ && foundHeader == 1) { + + # Get the fields + volumeID = $2; + letter = substr($0, ltrIndex, 3); + label = substr($0, labelIndex, fsIndex - labelIndex); + fs = substr($0, fsIndex, typeIndex - fsIndex); + type = substr($0, typeIndex, sizeIndex - typeIndex); + sizeT = substr($0, sizeIndex, statusIndex - sizeIndex); + status = substr($0, statusIndex, infoIndex - statusIndex); + info = substr($0, infoIndex, length($0) - infoIndex + 1); + + # Do some processing, remove unnecessary white spaces + gsub(" ", "", letter); + sub("^ +", "", label); sub(" +$", "", label); + sub("^ +", "", fs); sub(" +$", "", fs); + sub("^ +", "", type); sub(" +$", "", type); + gsub(" ", "", sizeT); + sub("^ +", "", status); sub(" +$", "", status); + sub("^ +", "", info); sub(" +$", "", info); + + # Convert size to bytes + size = ""; + if (substr(sizeT, length(sizeT), 1) == "B") + { + size = substr(sizeT, 1, length(sizeT) - 1); + + # Handle unit multipliers + if (substr(size, length(size), 1) == "K") + { + size = substr(size, 1, length(size) - 1) * 1024; + } + else if (substr(size, length(size), 1) == "M") + { + size = substr(size, 1, length(size) - 1) * 1024 * 1024; + } + else if (substr(size, length(size), 1) == "G") + { + size = substr(size, 1, length(size) - 1) * 1024 * 1024 * 1024; + } + else if (substr(size, length(size), 1) == "T") + { + size = substr(size, 1, length(size) - 1) * 1024 * 1024 * 1024 * 1024; + } + + # Make sure we got a number + if (size !~ /^[0-9]+$/) + { + size = ""; + } + } + + # Add a colon to the drive letter, if any + if (letter ~ /^[A-Z]$/) + { + letter = letter ":" + } + + # Build the displayID from label and letter + if (letter != "" && label != "") + { + displayID = letter " - " label; + } + else if (letter != "" && label == "") + { + displayID = letter; + } + else if (letter == "" && label != "") + { + displayID = label; + } + else + { + displayID = ""; + } + + # Replace "Partition" type with nothing + if (type == "Partition") + { + type = ""; + } + + print "MSHW;" volumeID ";" displayID ";" letter ";" type ";" fs ";" size ";" status ";" info ";" +} \ No newline at end of file diff --git a/src/it/metricshub-connectors/src/main/connector/hardware/DiskPart/embeddedFile-3 b/src/it/metricshub-connectors/src/main/connector/hardware/DiskPart/embeddedFile-3 new file mode 100644 index 0000000..ae3bc0a --- /dev/null +++ b/src/it/metricshub-connectors/src/main/connector/hardware/DiskPart/embeddedFile-3 @@ -0,0 +1 @@ +exit \ No newline at end of file diff --git a/src/it/metricshub-connectors/src/main/connector/hardware/GenBatteryNT/GenBatteryNT.yaml b/src/it/metricshub-connectors/src/main/connector/hardware/GenBatteryNT/GenBatteryNT.yaml new file mode 100644 index 0000000..a90e58a --- /dev/null +++ b/src/it/metricshub-connectors/src/main/connector/hardware/GenBatteryNT/GenBatteryNT.yaml @@ -0,0 +1,141 @@ +--- +extends: +- ../Hardware/Hardware +connector: + displayName: WMI - Battery + platforms: Any system + reliesOn: WMI + version: 1.0 + information: This connector provides battery monitoring for Windows computers. + detection: + connectionTypes: + - remote + - local + appliesTo: + - NT + criteria: + - type: deviceType + keep: + - NT + - type: service + name: WINMGMT + - type: wmi + namespace: root\cimv2 + query: SELECT DeviceID FROM Win32_Battery +monitors: + battery: + discovery: + sources: + source(1): + # Discovery + type: wmi + namespace: root\cimv2 + query: "SELECT Chemistry, DeviceID, Name FROM Win32_Battery" + computes: + # Translate Chemistry + - type: translate + column: 1 + translationTable: "${translation::ChemistryTranslationTable}" + mapping: + # + source: "${source::monitors.battery.discovery.sources.source(1)}" + attributes: + id: $2 + chemistry: $1 + vendor: "" + model: $3 + hw.parent.type: enclosure + name: "${awk::sprintf(\"%s (%s %s)\", $2, \"\", $3)}" + collect: + # COLLECT + type: multiInstance + keys: + - id + sources: + source(1): + # BatteryStatus, DeviceID, Charge remaining, time left and status + type: wmi + namespace: root\cimv2 + query: "SELECT BatteryStatus, DeviceID, EstimatedChargeRemaining, EstimatedRunTime, Status FROM Win32_Battery" + computes: + # Duplicate Status to translate it + - type: duplicateColumn + column: 5 + # Translate Status in an OK, WARN, ALARM status + - type: translate + column: 5 + translationTable: "${translation::StatusTranslationTable}" + # Translate Status column 3 into a StatusInformation + - type: translate + column: 6 + translationTable: "${translation::StatusInformationTranslationTable}" + # Translate BatteryStatus into a StatusInformation + - type: translate + column: 1 + translationTable: "${translation::BatteryStatusTranslationTable}" + # Merge Status StatusInformation and BatteryStatus StatusInformation + - type: rightConcat + column: 6 + value: $1 + # A time left of 71582788 indicates that it is charging. Ignore it. + - type: replace + column: 4 + existingValue: 71582788 + newValue: "" + mapping: + # Battery StatusInformation (not used anymore at this point), DeviceID, Charge, Time Left (mins), Status, StatusInformation + source: "${source::monitors.battery.collect.sources.source(1)}" + attributes: + id: $2 + metrics: + hw.status{hw.type="battery"}: $5 + hw.battery.charge: percent2Ratio($3) + hw.battery.time_left: $4 + legacyTextParameters: + StatusInformation: $6 +translations: + BatteryStatusTranslationTable: + "11": Partially Charged + "1": Discharging + "2": On AC + "3": Fully Charged + "4": Low Battery + "5": Critically Low Battery + "6": Charging + "7": Charging and High + "8": Charging and Low + "9": Charging and Critical + "10": "" + ChemistryTranslationTable: + "1": "" + "2": "" + "3": Lead Acid + "4": Nickel Cadmium + "5": Nickel Metal Hydride + "6": Lithium-ion + "7": Zinc Air + "8": Lithium Polymer + StatusInformationTranslationTable: + No Contact: 'No Contact ' + Stressed: 'Stressed ' + Degraded: 'Degraded ' + Starting: "" + Error: 'Error ' + Stopping: "" + Service: 'Service ' + NonRecover: 'Non Recoverable Error ' + OK: "" + Lost Comm: 'Lost Communication ' + Default: 'Unknown ' + StatusTranslationTable: + No Contact: degraded + Stressed: degraded + Degraded: degraded + Starting: ok + Error: failed + Stopping: ok + Service: degraded + NonRecover: failed + OK: ok + Lost Comm: degraded + Default: UNKNOWN diff --git a/src/it/metricshub-connectors/src/main/connector/hardware/GenericSwitchEnclosure/GenericSwitchEnclosure.yaml b/src/it/metricshub-connectors/src/main/connector/hardware/GenericSwitchEnclosure/GenericSwitchEnclosure.yaml new file mode 100644 index 0000000..aa33bc2 --- /dev/null +++ b/src/it/metricshub-connectors/src/main/connector/hardware/GenericSwitchEnclosure/GenericSwitchEnclosure.yaml @@ -0,0 +1,67 @@ +--- +extends: +- ../Hardware/Hardware +- ../MIB2-header/MIB2-header +connector: + displayName: Generic Ethernet Switch + platforms: MIB-2 Supported Switch + reliesOn: MIB-2 SNMP + version: 1.0 + information: This connector provides an enclosure for Ethernet switches that do not provide one of their own. + detection: + connectionTypes: + - remote + - local + appliesTo: + - Network + supersedes: + - MIB2 +monitors: + enclosure: + discovery: + sources: + source(1): + # sysdecr + type: snmpTable + oid: 1.3.6.1.2.1.1 + selectColumns: 1 + mapping: + source: "${source::monitors.enclosure.discovery.sources.source(1)}" + attributes: + id: EthernetSwitch + __display_id: $1 + type: Switch + name: "${awk::sprintf(\"Switch: %s\", $1)}" + network: + discovery: + sources: + source(4): + # Source(4) = ifTable SNMP Table + # PortID;OperationalStatus + type: snmpTable + oid: 1.3.6.1.2.1.2.2.1 + selectColumns: "ID,8" + computes: + # Exclude OperationalStatus 6 = Component Not Present + - type: excludeMatchingLines + column: 2 + valueList: 6 + source(5): + # PortID;OperationalStatus;PortID;Description;TypeCode;MacAddress;AdminStatus;ID;Name;Alias; + type: tableJoin + leftTable: "${source::monitors.network.discovery.sources.source(4)}" + rightTable: "${source::monitors.network.discovery.sources.source(3)}" + leftKeyColumn: 1 + rightKeyColumn: 1 + mapping: + # PortID;OperationalStatus;PortID;Description;TypeCode;MacAddress;AdminStatus;ID;Name;Alias; + source: "${source::monitors.network.discovery.sources.source(5)}" + attributes: + id: $1 + __display_id: $9 + physical_address: $6 + physical_address_type: MAC + device_type: $5 + hw.parent.type: enclosure + hw.parent.id: EthernetSwitch + name: "${awk::sprintf(\"%s (%s)\", $9, $5)}" diff --git a/src/it/metricshub-connectors/src/main/connector/hardware/GenericUPS/GenericUPS.yaml b/src/it/metricshub-connectors/src/main/connector/hardware/GenericUPS/GenericUPS.yaml new file mode 100644 index 0000000..1d040af --- /dev/null +++ b/src/it/metricshub-connectors/src/main/connector/hardware/GenericUPS/GenericUPS.yaml @@ -0,0 +1,349 @@ +--- +extends: +- ../Hardware/Hardware +connector: + displayName: Generic UPS + platforms: Any UPS which supports UPS-MIB (RFC1628) + version: 1.0 + information: This connector provides hardware monitoring of MIB-2 Standard UPS through an SNMP interface. + detection: + connectionTypes: + - remote + appliesTo: + - OOB + criteria: + - type: snmpGetNext + oid: 1.3.6.1.2.1.33 +monitors: + enclosure: + discovery: + sources: + source(1): + type: snmpTable + oid: 1.3.6.1.2.1.33.1.1 + selectColumns: "1,2,3,5" + mapping: + source: "${source::monitors.enclosure.discovery.sources.source(1)}" + attributes: + id: $4 + vendor: $1 + model: $2 + type: UPS + info: $3 + name: "${awk::sprintf(\"Enclosure: (%s %s)\", $1, $2)}" + collect: + sources: + source(1): + type: snmpGet + oid: 1.3.6.1.2.1.33.1.6.1.0 + computes: + - type: leftConcat + column: 1 + value: MSHW; + source(2): + type: snmpTable + oid: 1.3.6.1.2.1.33.1.4.4.1 + selectColumns: 4 + computes: + - type: awk + script: "${file::embeddedFile-1}" + source(3): + type: tableJoin + leftTable: "${source::monitors.enclosure.collect.sources.source(1)}" + rightTable: "${source::monitors.enclosure.collect.sources.source(2)}" + leftKeyColumn: 1 + rightKeyColumn: 1 + defaultRightLine: ; + computes: + - type: translate + column: 2 + translationTable: "${translation::enclosureStatusTranslationTable}" + source(4): + type: snmpTable + oid: 1.3.6.1.2.1.33.1.6.2.1 + selectColumns: 2 + computes: + - type: leftConcat + column: 1 + value: MSHW; + source(5): + type: tableJoin + leftTable: "${source::monitors.enclosure.collect.sources.source(3)}" + rightTable: "${source::monitors.enclosure.collect.sources.source(4)}" + leftKeyColumn: 1 + rightKeyColumn: 1 + defaultRightLine: ; + mapping: + source: "${source::monitors.enclosure.collect.sources.source(5)}" + metrics: + hw.status{hw.type="enclosure"}: $2 + hw.enclosure.power: $4 + hw.enclosure.energy: fakeCounter($4) + legacyTextParameters: + StatusInformation: $6 + battery: + discovery: + sources: + source(1): + type: snmpGet + oid: 1.3.6.1.2.1.33.1.2.1.0 + mapping: + source: "${source::monitors.battery.discovery.sources.source(1)}" + attributes: + id: UPS Battery + __display_id: UPS Battery + type: UPS + hw.parent.type: enclosure + name: "${awk::sprintf(\"%s (%s)\", \"UPS Battery\", \"UPS\")}" + collect: + sources: + source(1): + type: snmpGet + oid: 1.3.6.1.2.1.33.1.2.1.0 + computes: + - type: leftConcat + column: 1 + value: MSHW; + source(2): + type: snmpGet + oid: 1.3.6.1.2.1.33.1.2.4.0 + computes: + - type: leftConcat + column: 1 + value: MSHW; + source(3): + type: tableJoin + leftTable: "${source::monitors.battery.collect.sources.source(1)}" + rightTable: "${source::monitors.battery.collect.sources.source(2)}" + leftKeyColumn: 1 + rightKeyColumn: 1 + defaultRightLine: ; + computes: + - type: duplicateColumn + column: 2 + - type: translate + column: 2 + translationTable: "${translation::BatteryStatusTranslationTable}" + - type: translate + column: 3 + translationTable: "${translation::BatteryStatusInformationTranslationTable}" + source(4): + type: snmpGet + oid: 1.3.6.1.2.1.33.1.2.3.0 + computes: + - type: leftConcat + column: 1 + value: MSHW; + source(5): + type: tableJoin + leftTable: "${source::monitors.battery.collect.sources.source(3)}" + rightTable: "${source::monitors.battery.collect.sources.source(4)}" + leftKeyColumn: 1 + rightKeyColumn: 1 + defaultRightLine: ; + mapping: + source: "${source::monitors.battery.collect.sources.source(5)}" + attributes: + id: UPS Battery + metrics: + hw.status{hw.type="battery"}: $2 + hw.battery.charge: percent2Ratio($5) + hw.battery.time_left: $7 + legacyTextParameters: + StatusInformation: $3 + voltage: + discovery: + sources: + source(1): + type: snmpGet + oid: 1.3.6.1.2.1.33.1.9.1.0 + computes: + - type: multiply + column: 1 + value: 1000 + source(2): + type: snmpTable + oid: 1.3.6.1.2.1.33.1.3.3.1 + selectColumns: ID + computes: + - type: leftConcat + column: 1 + value: 'MSHW;Input ' + - type: rightConcat + column: 2 + value: ";${source::monitors.voltage.discovery.sources.source(1)}" + # Create Thresholds + # MSHW;ID;UPSConfigInputVoltage;UpperThreshold;LowerThreshold; + - type: awk + script: "${file::embeddedFile-2}" + source(3): + type: snmpGet + oid: 1.3.6.1.2.1.33.1.9.3.0 + computes: + - type: multiply + column: 1 + value: 1000 + source(4): + type: snmpTable + oid: 1.3.6.1.2.1.33.1.4.4.1 + selectColumns: ID + computes: + - type: leftConcat + column: 1 + value: 'MSHW;Output ' + - type: rightConcat + column: 2 + value: ";${source::monitors.voltage.discovery.sources.source(3)}" + # Create Thresholds + # MSHW;ID;UPSConfigOutputVoltage;UpperThreshold;LowerThreshold; + - type: awk + script: "${file::embeddedFile-2}" + source(5): + type: snmpGet + oid: 1.3.6.1.2.1.33.1.2.5.0 + computes: + - type: leftConcat + column: 1 + value: MSHW;Battery Voltage; + source(6): + type: snmpTable + oid: 1.3.6.1.2.1.33.1.5.3.1 + selectColumns: ID + computes: + - type: leftConcat + column: 1 + value: 'MSHW;Bypass ' + source(7): + type: tableUnion + tables: + - "${source::monitors.voltage.discovery.sources.source(2)}" + - "${source::monitors.voltage.discovery.sources.source(4)}" + - "${source::monitors.voltage.discovery.sources.source(5)}" + - "${source::monitors.voltage.discovery.sources.source(6)}" + mapping: + # InstanceTable = Source(7) + # MSHW;ID;Voltage;UpperThreshold;LowerThreshold; + source: "${source::monitors.voltage.discovery.sources.source(7)}" + attributes: + id: $2 + __display_id: $2 + sensor_location: UPS + hw.parent.type: enclosure + name: "${awk::sprintf(\"%s (%s)\", $2, \"UPS\")}" + metrics: + hw.voltage.limit{limit_type="high.degraded"}: $4 + hw.voltage.limit{limit_type="low.critical"}: $5 + collect: + # Voltage Collect + type: multiInstance + keys: + - id + sources: + source(1): + type: snmpGet + oid: 1.3.6.1.2.1.33.1.2.5.0 + computes: + - type: leftConcat + column: 1 + value: MSHW;Battery Voltage; + # UPSBatteryVoltage is in 0.1v, so multiply by 100 to get mv (1000 mv = 1 v) + # MSHW;ID;UPSBatteryVoltage; + - type: multiply + column: 3 + value: 100 + source(2): + type: snmpTable + oid: 1.3.6.1.2.1.33.1.3.3.1 + selectColumns: "ID,3" + computes: + - type: leftConcat + column: 1 + value: 'MSHW;Input ' + - type: multiply + column: 3 + value: 1000 + source(3): + type: snmpTable + oid: 1.3.6.1.2.1.33.1.4.4.1 + selectColumns: "ID,2" + computes: + - type: leftConcat + column: 1 + value: 'MSHW;Output ' + - type: multiply + column: 3 + value: 1000 + source(4): + type: snmpTable + oid: 1.3.6.1.2.1.33.1.5.3.1 + selectColumns: "ID,2" + computes: + - type: leftConcat + column: 1 + value: 'MSHW;Bypass ' + # upsBypassVoltage is in Volts, so multiply by 1000 to get mv. + # MSHW;ID;UPSBypassVoltage; + - type: multiply + column: 3 + value: 1000 + source(5): + type: tableUnion + tables: + - "${source::monitors.voltage.collect.sources.source(1)}" + - "${source::monitors.voltage.collect.sources.source(2)}" + - "${source::monitors.voltage.collect.sources.source(3)}" + - "${source::monitors.voltage.collect.sources.source(4)}" + mapping: + # ValueTable = Source(5) + # MSHW;ID;Voltage; + source: "${source::monitors.voltage.collect.sources.source(5)}" + attributes: + id: $2 + metrics: + hw.voltage: $3 + temperature: + discovery: + sources: + source(1): + type: snmpGet + oid: 1.3.6.1.2.1.33.1.2.7.0 + mapping: + source: "${source::monitors.temperature.discovery.sources.source(1)}" + attributes: + id: UPS Battery Temperature + __display_id: UPS Battery Temperature + sensor_location: battery + hw.parent.type: enclosure + name: "${awk::sprintf(\"%s (%s)\", \"UPS Battery Temperature\", \"battery\")}" + metrics: + hw.temperature.limit{limit_type="high.degraded"}: 35 + hw.temperature.limit{limit_type="high.critical"}: 45 + collect: + # Temperature Collect + type: multiInstance + keys: + - id + sources: + source(1): + type: snmpGet + oid: 1.3.6.1.2.1.33.1.2.7.0 + mapping: + source: "${source::monitors.temperature.collect.sources.source(1)}" + attributes: + id: UPS Battery Temperature + metrics: + hw.temperature: $1 +translations: + BatteryStatusInformationTranslationTable: + "1": Unknown + "2": Battery Normal + "3": Battery Low + "4": Battery Depleted + BatteryStatusTranslationTable: + "1": WARNING + "2": ok + "3": WARNING + "4": failed + enclosureStatusTranslationTable: + "0": ok + default: failed diff --git a/src/it/metricshub-connectors/src/main/connector/hardware/GenericUPS/embeddedFile-1 b/src/it/metricshub-connectors/src/main/connector/hardware/GenericUPS/embeddedFile-1 new file mode 100644 index 0000000..7faa44d --- /dev/null +++ b/src/it/metricshub-connectors/src/main/connector/hardware/GenericUPS/embeddedFile-1 @@ -0,0 +1,12 @@ +{ + # Loop through each line and sum the values + output += $1; +} + +# Print the final sum +END +{ + input = output / 0.9; + powerconsumption = input - output; + print "MSHW;" powerconsumption";" +} \ No newline at end of file diff --git a/src/it/metricshub-connectors/src/main/connector/hardware/GenericUPS/embeddedFile-2 b/src/it/metricshub-connectors/src/main/connector/hardware/GenericUPS/embeddedFile-2 new file mode 100644 index 0000000..a6d7c7e --- /dev/null +++ b/src/it/metricshub-connectors/src/main/connector/hardware/GenericUPS/embeddedFile-2 @@ -0,0 +1,12 @@ +BEGIN { FS = ";"; upper=""; lower=""} +{ + if (length($3) > 0) + { + upper=$3/100*110 + lower=$3/100*90 + } +} +END +{ + print $1";"$2";"$3";"upper";"lower";" +} diff --git a/src/it/metricshub-connectors/src/main/connector/hardware/Hardware/Hardware.yaml b/src/it/metricshub-connectors/src/main/connector/hardware/Hardware/Hardware.yaml new file mode 100644 index 0000000..0b48068 --- /dev/null +++ b/src/it/metricshub-connectors/src/main/connector/hardware/Hardware/Hardware.yaml @@ -0,0 +1,322 @@ +metrics: + + hw.enclosure.energy: + description: Energy consumed by the enclosure since the start of the MetricsHub Agent. + type: Counter + unit: J + + hw.enclosure.power: + description: Instantaneous power consumed by the enclosure, in Watts (hw.enclosure.energy is preferred). + type: Gauge + unit: W + + hw.energy: + description: Energy consumed by the component since the start of the MetricsHub Agent. + type: Counter + unit: J + + hw.errors: + description: Number of errors encountered by the component. + type: Counter + unit: "{errors}" + + hw.host.ambient_temperature: + description: Host's current ambient temperature in degrees Celsius (°C). This metric is only reported if the value is between 5°C and 35°C. + type: Gauge + unit: Cel + + hw.host.energy: + description: Energy consumed by the host since the start of the MetricsHub Agent. + type: Counter + unit: J + + hw.host.heating_margin: + description: Number of degrees Celsius (°C) remaining before the temperature reaches the closest warning threshold. + type: Gauge + unit: Cel + + hw.host.power: + description: Instantaneous power consumed by the host, in Watts (hw.host.energy is preferred). + type: Gauge + unit: W + + hw.power: + description: Instantaneous power consumed by the component, in Watts (hw.energy is preferred). + type: Gauge + unit: W + + hw.status: + description: 'Operational status: 1 (true) or 0 (false) for each of the possible states.' + type: + stateSet: + - degraded + - failed + - ok + + hw.battery.charge: + description: Remaining fraction of battery charge. + type: Gauge + unit: 1 + + hw.battery.charge.limit: + description: Lower limit of battery charge fraction to ensure proper operation. + type: Gauge + unit: 1 + + hw.battery.time_left: + description: Number of seconds left before recharging the battery when state is discharging. + type: Gauge + unit: s + + hw.power_state: + description: Power state. Each of the possible states (off, on and suspended) will either take the value 1 (true) or 0 (false). + type: + stateSet: + - "off" + - "on" + - suspended + + metricshub.connector.status: + description: Connector operational status. + type: + stateSet: + - failed + - ok + + hw.errors.limit: + description: Number of detected and corrected errors that will generate a warning or an alarm when limit_type is degraded or critical. + type: Gauge + unit: "{errors}" + + hw.cpu.speed: + description: CPU current frequency. + type: Gauge + unit: Hz + + hw.cpu.speed.limit: + description: CPU maximum frequency. + type: Gauge + unit: Hz + + hw.fan.speed: + description: Fan speed. + type: Gauge + unit: rpm + + hw.fan.speed.limit: + description: Speed of the corresponding fan (in revolutions/minute) that will generate a warning or an alarm when limit_type is low.degraded or low.critical. + type: Gauge + unit: rpm + + hw.fan.speed_ratio: + description: Fan speed expressed as a fraction of its maximum speed. + type: Gauge + unit: 1 + + hw.fan.speed_ratio.limit: + description: Fan speed ratio that will generate a warning or an alarm when limit_type is low.degraded or low.critical. + type: Gauge + unit: 1 + + hw.gpu.io: + description: Received and transmitted bytes by the GPU. + type: Counter + unit: By + + hw.gpu.memory.limit: + description: Size of the GPU memory. + type: UpDownCounter + unit: By + + hw.gpu.memory.utilization: + description: Fraction of GPU memory used. + type: Gauge + unit: 1 + + hw.gpu.memory.utilization.limit: + description: GPU memory utilization ratio that will generate a warning or an alarm when limit_type is degraded or critical. + type: Gauge + unit: 1 + + hw.gpu.memory.usage: + description: GPU memory used. + type: UpDownCounter + unit: By + + hw.gpu.power: + description: GPU instantaneous power consumption in Watts. + type: Gauge + unit: W + + hw.gpu.utilization: + description: Ratio of time spent by the GPU for each task (decoder, encoder and general). + type: Gauge + unit: 1 + + hw.gpu.utilization.limit: + description: GPU used time ratio that will generate a warning or an alarm when limit_type is degraded or critical. + type: Gauge + unit: 1 + + hw.logical_disk.limit: + description: Size of the logical disk. + type: UpDownCounter + unit: By + + hw.logical_disk.usage: + description: Logical disk space usage. + type: UpDownCounter + unit: By + + hw.logical_disk.utilization: + description: Logical disk space utilization as a fraction. + type: Gauge + unit: 1 + + hw.lun.paths: + description: Number of available paths. + type: Gauge + unit: "{paths}" + + hw.lun.paths.limit: + description: Number of available paths that will generate a warning when limit_type is low.degraded. + type: Gauge + unit: "{paths}" + + hw.memory.limit: + description: Memory module size. + type: Gauge + unit: By + + hw.network.bandwidth.limit: + description: Speed that the network adapter and its remote counterpart currently use to communicate with each other. + type: UpDownCounter + unit: By + + hw.network.bandwidth.utilization: + description: Utilization of the network bandwidth as a fraction. + type: Gauge + unit: 1 + + hw.network.error_ratio: + description: Ratio of sent and received packets that were in error. + type: Gauge + unit: 1 + + hw.network.error_ratio.limit: + description: Network interface error ratio that will generate a warning or an alarm when limit_type is degraded or critical. + type: Gauge + unit: 1 + + hw.network.full_duplex: + description: Whether the port is configured to operate in full-duplex mode. + type: UpDownCounter + + hw.network.io: + description: Received and transmitted network traffic in bytes. + type: Counter + unit: By + + hw.network.packets: + description: Received and transmitted network traffic in packets (or frames). + type: Counter + unit: "{packets}" + + hw.network.up: + description: Link status. + type: UpDownCounter + + hw.other_device.uses: + description: Number of times the device has been used. + type: Counter + unit: "{uses}" + + hw.other_device.uses.limit: + description: Number of times the device has been used which will generate a warning or an alarm when limit_type is degraded or critical. + type: Gauge + unit: "{uses}" + + hw.other_device.value: + description: Currently reported value of the device. + type: Gauge + + hw.other_device.value.limit: + description: Device reported value that will generate a warning or an alarm when limit_type is degraded or critical. + type: Gauge + + hw.physical_disk.endurance_utilization: + description: Physical disk remaining endurance ratio. + type: Gauge + unit: 1 + + hw.physical_disk.size: + description: Size of the disk. + type: Gauge + unit: By + + hw.physical_disk.smart: + description: Value of the corresponding S.M.A.R.T. attribute. + type: Gauge + unit: 1 + + hw.power_supply.limit: + description: Maximum power output of the power supply. + type: Gauge + unit: W + + hw.power_supply.utilization: + description: Utilization of the power supply as a fraction of its maximum output. + type: Gauge + unit: 1 + + hw.robotics.moves: + description: Number of moves operations that occurred during the last collect interval. + type: Counter + unit: "{moves}" + + hw.tape_drive.operations: + description: Operations performed by the tape drive. + type: Counter + unit: "{operations}" + + hw.temperature: + description: Temperature of the component. + type: Gauge + unit: Cel + + hw.temperature.limit: + description: Temperature of the corresponding component that will generate a warning or an alarm when limit_type is high.degraded or high.critical. + type: Gauge + unit: Cel + + hw.vm.power_ratio: + description: Ratio of host power consumed by the virtual machine. + type: Gauge + unit: Cel + + hw.voltage: + description: Voltage measured by the sensor. + type: Gauge + unit: V + + hw.voltage.limit: + description: Voltage limit in Volts. + type: Gauge + unit: V + + hw.power.limit: + description: Energy consumption of the corresponding component that will generate a warning or an alarm when limit_type is high.degraded or high.critical. + type: Gauge + unit: W + + metricshub.agent.info: + description: MetricsHub Agent information. + type: Gauge + + metricshub.host.configured: + description: Whether the host is configured or not. + type: UpDownCounter + + metricshub.host.up: + description: Whether the configured protocol (http, ipmi, snmp, ssh, wbem, winrm and wmi) is up (1) or not (0). + type: UpDownCounter diff --git a/src/it/metricshub-connectors/src/main/connector/hardware/HyperV/HyperV.yaml b/src/it/metricshub-connectors/src/main/connector/hardware/HyperV/HyperV.yaml new file mode 100644 index 0000000..a20317e --- /dev/null +++ b/src/it/metricshub-connectors/src/main/connector/hardware/HyperV/HyperV.yaml @@ -0,0 +1,89 @@ +--- +extends: +- ../Hardware/Hardware +connector: + displayName: Hyper-V + platforms: Hyper-V Servers + reliesOn: PowerShell + version: 1.0 + information: This connector provides hardware monitoring through Hyper-V PowerShell cmdlets. The user requires Hyper-V Administrators membership. + detection: + connectionTypes: + - remote + - local + appliesTo: + - NT + criteria: + - type: productRequirements + kmVersion: 11.3.00 + - type: osCommand + commandLine: Powershell.exe Get-VM + expectedResult: State +monitors: + vm: + discovery: + sources: + source(1): + type: osCommand + commandLine: "PowerShell.exe \"Get-VM | Select-Object VMName, Id | ConvertTo-CSV -Delimiter ';'\"" + beginAtLineNumber: 3 + computes: + - type: replace + column: 1 + existingValue: '"' + newValue: "" + - type: replace + column: 2 + existingValue: '"' + newValue: "" + - type: leftConcat + column: 2 + value: "GUID: " + mapping: + source: "${source::monitors.vm.discovery.sources.source(1)}" + attributes: + id: $1 + __display_id: $1 + vm.host.name: $1 + info: $2 + hw.parent.type: enclosure + name: "${awk::sprintf(\"%s (%s)\", $1, $1)}" + conditionalCollection: + hw.power{hw.type="vm"}: 0 + collect: + type: multiInstance + keys: + - id + sources: + source(1): + type: osCommand + commandLine: "PowerShell.exe \"Get-VM | Select-Object VMName, ProcessorCount, State | ConvertTo-CSV -Delimiter ';'\"" + beginAtLineNumber: 3 + computes: + - type: replace + column: 1 + existingValue: '"' + newValue: "" + - type: replace + column: 2 + existingValue: '"' + newValue: "" + - type: replace + column: 3 + existingValue: '"' + newValue: "" + - type: translate + column: 3 + translationTable: "${translation::stateTranslationTable}" + mapping: + source: "${source::monitors.vm.collect.sources.source(1)}" + attributes: + id: $1 + metrics: + hw.vm.power_ratio: computePowerShareRatio($2) + hw.power_state{hw.type="vm"}: $3 +translations: + stateTranslationTable: + Default: "off" + Running: "on" + Suspended: "suspended" diff --git a/src/it/metricshub-connectors/src/main/connector/hardware/IpmiTool/IpmiTool.yaml b/src/it/metricshub-connectors/src/main/connector/hardware/IpmiTool/IpmiTool.yaml new file mode 100644 index 0000000..cdeb4b4 --- /dev/null +++ b/src/it/metricshub-connectors/src/main/connector/hardware/IpmiTool/IpmiTool.yaml @@ -0,0 +1,808 @@ +--- +extends: +- ../Hardware/Hardware +connector: + displayName: IPMI + platforms: Any system with IPMI + reliesOn: IPMI + information: "Gives environmental information (temperatures, fans, etc.) on several IPMI-enabled servers in-band and out-of-band." + version: 2.0 + detection: + connectionTypes: + - remote + - local + appliesTo: + - Linux + - Solaris + - NT + - OOB + criteria: + # Let the KM/PM validate whether IPMI is available or not + - type: ipmi + forceSerialization: true +monitors: + enclosure: + discovery: + sources: + source(1): + # Source(1) = IPMI source + type: ipmi + forceSerialization: true + computes: + # Process IPMI result througn an AWK script to interpret what can be interpreted + - type: awk + script: "${file::embeddedFile-1}" + keep: ^MSHW; + separators: ; + selectColumns: "2,3,4,5,6,7,8,9,10,11" + source(2): + # Source(2) = copy of Source(1) + type: copy + from: "${source::monitors.enclosure.discovery.sources.source(1)}" + computes: + # Keep only the enclosure line + # Enclosure;Vendor;Model;SerialNumber;Status;StatusInformation;PowerConsumption;AdditionalInformation + - type: keepOnlyMatchingLines + column: 1 + valueList: enclosure + mapping: + # InstanceTable = Source(2) + source: "${source::monitors.enclosure.discovery.sources.source(2)}" + attributes: + id: IPMI + vendor: $2 + model: $3 + serial_number: $4 + info: $8 + name: "${awk::sprintf(\"Enclosure: (%s %s)\", $2, $3)}" + conditionalCollection: + hw.status{hw.type="enclosure"}: $5 + hw.enclosure.power: $7 + collect: + # Collect type is "all instances in one shot + type: multiInstance + keys: + - id + sources: + source(1): + # Source(1) = Get IPMI, will be re-used by all other collects + type: ipmi + forceSerialization: true + computes: + # Post-processing of the IPMI mess (which has already been greatly sorted out!) + - type: awk + script: "${file::embeddedFile-1}" + keep: ^MSHW; + separators: ; + selectColumns: "2,3,4,5,6,7,8,9,10,11" + source(2): + # Source(2) = copy of Source(1) + type: copy + from: "${source::monitors.enclosure.collect.sources.source(1)}" + computes: + # Keep only the enclosure line + # Enclosure;Vendor;Model;SerialNumber;StatusArray;StatusInformation;PowerConsumption + - type: keepOnlyMatchingLines + column: 1 + valueList: enclosure + # Convert StatusArray to a simple (unique) PATROLStatus + # Enclosure;Vendor;Model;SerialNumber;Status;StatusInformation;PowerConsumption + - type: convert + column: 5 + conversion: array2SimpleStatus + mapping: + # ValueTable = Source(2) + source: "${source::monitors.enclosure.collect.sources.source(2)}" + attributes: + id: IPMI + metrics: + hw.status{hw.type="enclosure"}: $5 + hw.enclosure.power: $7 + hw.enclosure.energy: fakeCounter($7) + legacyTextParameters: + StatusInformation: $6 + fan: + discovery: + sources: + source(1): + # Source(1) = copy of Enclosure.Discovery.Source(1) + # SensorType;SensorID;SensorName;Location;FanSpeed;WarningThreshold;AlarmThreshold + type: copy + from: "${source::monitors.enclosure.discovery.sources.source(1)}" + computes: + # Keep only fans + # Fan;SensorID;SensorName;Location;FanSpeed;WarningThreshold;AlarmThreshold + - type: keepOnlyMatchingLines + column: 1 + valueList: fan + # Add empty Status and StatusInformation columns + # Fan;SensorID;SensorName;StatusArray;StatusInformation;Location;FanSpeed;WarningThreshold;AlarmThreshold; + - type: rightConcat + column: 3 + value: ;; + # Add empty AdditionalInformation1 column at the very end + # Fan;SensorID;SensorName;StatusArray;StatusInformation;Location;FanSpeed;WarningThreshold;AlarmThreshold;AdditionalInformation1; + - type: rightConcat + column: 9 + value: ; + source(2): + # Source(2) = copy of Enclosure.Discovery.Source(1) + type: copy + from: "${source::monitors.enclosure.discovery.sources.source(1)}" + computes: + # Keep only "Fan Device" lines + # Fan Device;DeviceID;Entity ID;Vendor;Model;SerialNumber;StatusArray;StatusInformation;AdditionalInformation1; + - type: keepOnlyMatchingLines + column: 1 + valueList: Fan Device + # Keep only interesting columns + # Fan Device;DeviceID;StatusArray;StatusInformation;AdditionalInformation1; + - type: keepColumns + columnNumbers: "1,2,3,7,8,9" + # Add empty Location, FanSpeed, WarningThreshold and AlarmThreshold columns + # Fan Device;DeviceID;DisplayID;StatusArray;StatusInformation;Location;FanSpeed;WarningThreshold;AlarmThreshold;AdditionalInformation1; + - type: rightConcat + column: 5 + value: ;;;; + source(3): + # Source(3) = Table Union of Source(1) and Source(2) + # Fan;DeviceID;SensorName;StatusArray;StatusInformation;Location;FanSpeed;WarningThreshold;AlarmThreshold;AdditionalInformation1; + type: tableUnion + tables: + - "${source::monitors.fan.discovery.sources.source(1)}" + - "${source::monitors.fan.discovery.sources.source(2)}" + mapping: + # The instance table + source: "${source::monitors.fan.discovery.sources.source(3)}" + attributes: + id: $2 + __display_id: $3 + sensor_location: $6 + info: $10 + hw.parent.type: enclosure + hw.parent.id: IPMI + name: "${awk::sprintf(\"%s (%s)\", $3, $6)}" + metrics: + hw.fan.speed.limit{limit_type="low.degraded"}: $8 + hw.fan.speed.limit{limit_type="low.critical"}: $9 + conditionalCollection: + hw.status{hw.type="fan"}: $4 + hw.fan.speed: $7 + collect: + # Collect type is "all instances in one shot + type: multiInstance + keys: + - id + sources: + source(1): + # Source(1) = copy of Enclosure.Collect.Source(1) + # SensorType;SensorID;SensorName;Location;Value;WarningThreshold;AlarmThreshold + type: copy + from: "${source::monitors.enclosure.collect.sources.source(1)}" + computes: + # Keep only fans + # Fan;SensorID;SensorName;Location;Value;WarningThreshold;AlarmThreshold + - type: keepOnlyMatchingLines + column: 1 + valueList: fan + # Add empty Status and StatusInformation columns + # Fan;SensorID;SensorName;StatusArray;StatusInformation;Location;FanSpeed;WarningThreshold;AlarmThreshold + - type: rightConcat + column: 3 + value: ;; + source(2): + # Source(2) = copy of Enclosure.Discovery.Source(1) + type: copy + from: "${source::monitors.enclosure.collect.sources.source(1)}" + computes: + # Keep only "Fan Device" lines + # Fan Device;DeviceID;Entity ID;Vendor;Model;SerialNumber;StatusArray;StatusInformation;AdditionalInformation1; + - type: keepOnlyMatchingLines + column: 1 + valueList: Fan Device + # Keep only interesting columns + # Fan Device;DeviceID;StatusArray;StatusInformation;AdditionalInformation1; + - type: keepColumns + columnNumbers: "1,2,7,8,9" + # Add an empty "SensorName" column + # Fan Device;DeviceID;SensorName;StatusArray;StatusInformation;AdditionalInformation1; + - type: rightConcat + column: 2 + value: ; + # Add empty FanSpeed, WarningThreshold and AlarmThreshold columns + # Fan Device;DeviceID;SensorName;StatusArray;StatusInformation;AdditionalInformation1;FanSpeed;WarningThreshold;AlarmThreshold + - type: rightConcat + column: 6 + value: ;;; + # Convert StatusArray to a simple (unique) PATROLStatus + # Fan Device;DeviceID;SensorName;Status;StatusInformation;AdditionalInformation1;FanSpeed;WarningThreshold;AlarmThreshold + - type: convert + column: 4 + conversion: array2SimpleStatus + source(3): + # Source(3) = Table Union of Source(1) and Source(2) + # Fan;DeviceID;SensorName;Status;StatusInformation;AdditionalInformation1;FanSpeed;WarningThreshold;AlarmThreshold + type: tableUnion + tables: + - "${source::monitors.fan.collect.sources.source(1)}" + - "${source::monitors.fan.collect.sources.source(2)}" + mapping: + # CollectTable = Source(3) + # Fan;DeviceID;SensorName;Status;StatusInformation;AdditionalInformation1;FanSpeed;WarningThreshold;AlarmThreshold + source: "${source::monitors.fan.collect.sources.source(3)}" + attributes: + id: $2 + metrics: + hw.fan.speed: $7 + hw.status{hw.type="fan"}: $4 + legacyTextParameters: + StatusInformation: $5 + temperature: + discovery: + sources: + source(1): + # Source(1) = copy of Enclosure.Discovery.Source(1) + # SensorType;SensorID;SensorName;Location;Value;WarningThreshold;AlarmThreshold + type: copy + from: "${source::monitors.enclosure.discovery.sources.source(1)}" + computes: + # Keep only fans + # Temperature;SensorID;SensorName;Location;Value;WarningThreshold;AlarmThreshold + - type: keepOnlyMatchingLines + column: 1 + valueList: temperature + mapping: + # The instance table + source: "${source::monitors.temperature.discovery.sources.source(1)}" + attributes: + id: $2 + __display_id: $3 + sensor_location: $4 + hw.parent.type: enclosure + hw.parent.id: IPMI + name: "${awk::sprintf(\"%s (%s)\", $3, $4)}" + metrics: + hw.temperature.limit{limit_type="high.degraded"}: $6 + hw.temperature.limit{limit_type="high.critical"}: $7 + collect: + # Collect type is "all instances in one shot + type: multiInstance + keys: + - id + sources: + source(1): + # Source(1) = copy of Enclosure.Collect.Source(1) + # SensorType;SensorID;SensorName;Location;Value;WarningThreshold;AlarmThreshold + type: copy + from: "${source::monitors.enclosure.collect.sources.source(1)}" + computes: + # Keep only fans + # Temperature;SensorID;SensorName;Location;Value;WarningThreshold;AlarmThreshold + - type: keepOnlyMatchingLines + column: 1 + valueList: temperature + mapping: + # CollectTable = Source(1) + source: "${source::monitors.temperature.collect.sources.source(1)}" + attributes: + id: $2 + metrics: + hw.temperature: $5 + voltage: + discovery: + sources: + source(1): + # Source(1) = copy of Enclosure.Discovery.Source(1) + # SensorType;SensorID;SensorName;Location;Value;WarningThreshold;AlarmThreshold + type: copy + from: "${source::monitors.enclosure.discovery.sources.source(1)}" + computes: + # Keep only fans + # Voltage;SensorID;SensorName;Location;Value;WarningThreshold;AlarmThreshold + - type: keepOnlyMatchingLines + column: 1 + valueList: voltage + mapping: + # The instance table + source: "${source::monitors.voltage.discovery.sources.source(1)}" + attributes: + id: $2 + __display_id: $3 + sensor_location: $4 + hw.parent.type: enclosure + hw.parent.id: IPMI + name: "${awk::sprintf(\"%s (%s)\", $3, $4)}" + metrics: + hw.voltage.limit{limit_type="low.critical"}: $6 + hw.voltage.limit{limit_type="high.degraded"}: $7 + collect: + # Collect type is "all instances in one shot + type: multiInstance + keys: + - id + sources: + source(1): + # Source(1) = copy of Enclosure.Collect.Source(1) + # SensorType;SensorID;SensorName;Location;Value;WarningThreshold;AlarmThreshold + type: copy + from: "${source::monitors.enclosure.collect.sources.source(1)}" + computes: + # Keep only fans + # Voltage;SensorID;SensorName;Location;Value;WarningThreshold;AlarmThreshold + - type: keepOnlyMatchingLines + column: 1 + valueList: voltage + mapping: + # CollectTable = Source(1) + source: "${source::monitors.voltage.collect.sources.source(1)}" + attributes: + id: $2 + metrics: + hw.voltage: $5 + power_supply: + discovery: + sources: + source(1): + # Source(1) = copy of Enclosure.Discovery.Source(1) + type: copy + from: "${source::monitors.enclosure.discovery.sources.source(1)}" + computes: + # Keep only power supply lines + # Power supply;DeviceID;Entity ID;Vendor;Model;SerialNumber;StatusArray;StatusInformation;AdditionalInformation1; + - type: keepOnlyMatchingLines + column: 1 + valueList: Power supply + - type: leftConcat + column: 6 + value: "SerialNumber: " + - type: leftConcat + column: 5 + value: "Model: " + mapping: + # InstanceTable = Source(1) + source: "${source::monitors.power_supply.discovery.sources.source(1)}" + attributes: + id: $2 + info: "${awk::join(\" \", $9, $6, $5)}" + hw.parent.type: enclosure + hw.parent.id: IPMI + name: $2 + collect: + # Collect type is: multi-instance + type: multiInstance + keys: + - id + sources: + source(1): + # Source(1) = copy of Enclosure.Collect.Source(1) + type: copy + from: "${source::monitors.enclosure.collect.sources.source(1)}" + computes: + # Keep only power supply lines + # Power supply;DeviceID;Entity ID;Vendor;Model;SerialNumber;StatusArray;StatusInformation + - type: keepOnlyMatchingLines + column: 1 + valueList: Power supply + # Convert StatusArray to a simple (unique) PATROLStatus + # Power supply;DeviceID;Entity ID;Vendor;Model;SerialNumber;Status;StatusInformation + - type: convert + column: 7 + conversion: array2SimpleStatus + mapping: + # ValueTable = Source(1) + source: "${source::monitors.power_supply.collect.sources.source(1)}" + attributes: + id: $2 + metrics: + hw.status{hw.type="power_supply"}: $7 + legacyTextParameters: + StatusInformation: $8 + cpu: + discovery: + sources: + source(1): + # Source(1) = copy of Enclosure.Discovery.Source(1) + type: copy + from: "${source::monitors.enclosure.discovery.sources.source(1)}" + computes: + # Keep only processor lines + # Processor;DeviceID;Entity ID;Vendor;Model;SerialNumber;StatusArray;StatusInformation;AdditionalInformation1 + - type: keepOnlyMatchingLines + column: 1 + valueList: Processor + - type: leftConcat + column: 6 + value: "SerialNumber: " + mapping: + # InstanceTable = Source(1) + source: "${source::monitors.cpu.discovery.sources.source(1)}" + attributes: + id: $2 + vendor: $4 + model: $5 + info: "${awk::join(\" \", $9, $6)}" + hw.parent.type: enclosure + hw.parent.id: IPMI + name: "${awk::sprintf(\"%s (%s - %s)\", $2, $4, $5)}" + collect: + # Collect type is: multi-instance + type: multiInstance + keys: + - id + sources: + source(1): + # Source(1) = copy of Enclosure.Collect.Source(1) + type: copy + from: "${source::monitors.enclosure.collect.sources.source(1)}" + computes: + # Keep only processor lines + # Processor;DeviceID;Entity ID;Vendor;Model;SerialNumber;StatusArray;StatusInformation + - type: keepOnlyMatchingLines + column: 1 + valueList: Processor + # Convert StatusArray to a simple (unique) PATROLStatus + # Processor;DeviceID;Entity ID;Vendor;Model;SerialNumber;Status;StatusInformation + - type: convert + column: 7 + conversion: array2SimpleStatus + mapping: + # ValueTable = Source(1) + source: "${source::monitors.cpu.collect.sources.source(1)}" + attributes: + id: $2 + metrics: + hw.status{hw.type="cpu"}: $7 + legacyTextParameters: + StatusInformation: $8 + memory: + discovery: + sources: + source(1): + # Source(1) = copy of Enclosure.Discovery.Source(1) + type: copy + from: "${source::monitors.enclosure.discovery.sources.source(1)}" + computes: + # Keep only memory lines + # Memory module;DeviceID;Entity ID;Vendor;Model;SerialNumber;StatusArray;StatusInformation;AdditionalInformation1 + - type: keepOnlyMatchingLines + column: 1 + valueList: "Memory module,Memory Device" + # Duplicate the "Model" column because it is in the form of Model|Speed + # Memory module;DeviceID;Entity ID;Vendor;Model|Speed;Model|Speed;SerialNumber;StatusArray;StatusInformation;AdditionalInformation1; + - type: duplicateColumn + column: 5 + # Now extract "Model" from "Model|Size" + # Memory module;DeviceID;Entity ID;Vendor;Model;Model|Speed;SerialNumber;StatusArray;StatusInformation;AdditionalInformation1; + - type: extract + column: 5 + subColumn: 1 + subSeparators: '|' + # Now extract "Size" from "Model|Size" + # Memory module;DeviceID;Entity ID;Vendor;Model;Speed;SerialNumber;StatusArray;StatusInformation;AdditionalInformation1; + - type: extract + column: 6 + subColumn: 2 + subSeparators: '|' + mapping: + # InstanceTable = Source(1) + source: "${source::monitors.memory.discovery.sources.source(1)}" + attributes: + id: $2 + __display_id: $3 + vendor: $4 + model: $5 + serial_number: $7 + info: $10 + hw.parent.type: enclosure + hw.parent.id: IPMI + name: "${awk::sprintf(\"%s (%s - %s MB)\", $3, $4, $6)}" + metrics: + hw.memory.limit: mebiByte2Byte($6) + collect: + # Collect type is: multi-instance + type: multiInstance + keys: + - id + sources: + source(1): + # Source(1) = copy of Enclosure.Collect.Source(1) + type: copy + from: "${source::monitors.enclosure.collect.sources.source(1)}" + computes: + # Keep only memory lines + # Memory module;DeviceID;Entity ID;Vendor;Model;SerialNumber;StatusArray;StatusInformation + - type: keepOnlyMatchingLines + column: 1 + valueList: "Memory module,Memory Device" + # Convert StatusArray to a simple (unique) PATROLStatus + # Memory module;DeviceID;Entity ID;Vendor;Model;SerialNumber;Status;StatusInformation + - type: convert + column: 7 + conversion: array2SimpleStatus + mapping: + # ValueTable = Source(1) + source: "${source::monitors.memory.collect.sources.source(1)}" + attributes: + id: $2 + metrics: + hw.status{hw.type="memory"}: $7 + legacyTextParameters: + StatusInformation: $8 + physical_disk: + discovery: + sources: + source(1): + # Source(1) = copy of Enclosure.Discovery.Source(1) + type: copy + from: "${source::monitors.enclosure.discovery.sources.source(1)}" + computes: + # Keep only disk lines + # Disk or disk bay;DeviceID;Entity ID;Vendor;Model;SerialNumber;StatusArray;StatusInformation;AdditionalInformation1; + - type: keepOnlyMatchingLines + column: 1 + valueList: "Disk or disk bay,Disk or Disk Bay,Disk Drive Bay" + mapping: + # InstanceTable = Source(1) + source: "${source::monitors.physical_disk.discovery.sources.source(1)}" + attributes: + id: $2 + vendor: $4 + model: $5 + serial_number: $6 + info: $9 + hw.parent.type: enclosure + hw.parent.id: IPMI + name: "${awk::sprintf(\"%s (%s)\", $2, $4)}" + collect: + # Collect type is: multi-instance + type: multiInstance + keys: + - id + sources: + source(1): + # Source(1) = copy of Enclosure.Collect.Source(1) + type: copy + from: "${source::monitors.enclosure.collect.sources.source(1)}" + computes: + # Keep only disk lines + # Disk or disk bay;DeviceID;Entity ID;Vendor;Model;SerialNumber;StatusArray;StatusInformation + - type: keepOnlyMatchingLines + column: 1 + valueList: "Disk or disk bay,Disk or Disk Bay,Disk Drive Bay" + # Convert StatusArray to a simple (unique) PATROLStatus + # Disk or disk bay;DeviceID;Entity ID;Vendor;Model;SerialNumber;Status;StatusInformation + - type: convert + column: 7 + conversion: array2SimpleStatus + mapping: + # ValueTable = Source(1) + source: "${source::monitors.physical_disk.collect.sources.source(1)}" + attributes: + id: $2 + metrics: + hw.status{hw.type="physical_disk"}: $7 + legacyTextParameters: + StatusInformation: $8 + led: + discovery: + sources: + source(1): + # Source(1) = copy of Enclosure.Discovery.Source(1) + type: copy + from: "${source::monitors.enclosure.discovery.sources.source(1)}" + computes: + # Keep only LED lines + # LED;DeviceID;Name;EntityID;Color;OnStatus;OffStatus;BlinkingStatus;Status; + - type: keepOnlyMatchingLines + column: 1 + valueList: led + mapping: + # InstanceTable = Source(1) + source: "${source::monitors.led.discovery.sources.source(1)}" + attributes: + id: $2 + __display_id: $3 + color: $5 + __on_status: $6 + __off_status: $7 + __blinking_status: $8 + hw.parent.type: enclosure + hw.parent.id: IPMI + name: "${awk::sprintf(\"%s (%s)\", $3, $5)}" + collect: + # Collect type is: multi-instance + type: multiInstance + keys: + - id + sources: + source(1): + # Source(1) = copy of Enclosure.Collect.Source(1) + type: copy + from: "${source::monitors.enclosure.collect.sources.source(1)}" + computes: + # Keep only LED lines + # LED;DeviceID;Name;EntityID;Color;OnStatus;OffStatus;BlinkingStatus;Status; + - type: keepOnlyMatchingLines + column: 1 + valueList: led + # Duplicate the Status column + - type: duplicateColumn + column: 9 + mapping: + # ValueTable = Source(1) + source: "${source::monitors.led.collect.sources.source(1)}" + attributes: + id: $2 + metrics: + hw.status{hw.type="led"}: legacyLedStatus($9) + legacyTextParameters: + StatusInformation: $10 + other_device: + discovery: + sources: + source(1): + # Source(1) = copy of Enclosure.Discovery.Source(1) + type: copy + from: "${source::monitors.enclosure.discovery.sources.source(1)}" + computes: + # Exclude what we know already + # DeviceType;DeviceID;Entity ID;Vendor;Model;SerialNumber;StatusArray;StatusInformation;AdditionalInformation1; + - type: excludeMatchingLines + column: 1 + valueList: "Battery,Disk or Disk Bay,Disk or disk bay,Disk Drive Bay,Memory module,Memory Device,LED,Temperature,Voltage,Power supply,Fan,Fan Device,Enclosure,FRU,Processor,Current,PowerConsumption,EnergyUsage,Blade,Processing Blade" + - type: leftConcat + column: 6 + value: "Serial Number: " + mapping: + # InstanceTable = Source(1) + source: "${source::monitors.other_device.discovery.sources.source(1)}" + attributes: + device_type: $1 + id: $2 + __display_id: $3 + additional_label: $4 + info: "${awk::join(\" \", $9, $6)}" + hw.parent.type: enclosure + hw.parent.id: IPMI + name: "${awk::sprintf(\"%s: %s (%s)\", $1, $3, $4)}" + collect: + # Collect type is: multi-instance + type: multiInstance + keys: + - id + sources: + source(1): + # Source(1) = copy of Enclosure.Collect.Source(1) + type: copy + from: "${source::monitors.enclosure.collect.sources.source(1)}" + computes: + # Exclude what we know already + # DeviceType;DeviceID;Entity ID;Vendor;Model;SerialNumber;StatusArray;StatusInformation + - type: excludeMatchingLines + column: 1 + valueList: "Battery,Disk or Disk Bay,Disk or disk bay,Disk Drive Bay,Memory module,Memory Device,LED,Temperature,Voltage,Power supply,Fan,Fan Device,Enclosure,FRU,Processor,Current,PowerConsumption,EnergyUsage,Blade,Processing Blade" + # Convert StatusArray to a simple (unique) PATROLStatus + # Disk or disk bay;DeviceID;Entity ID;Vendor;Model;SerialNumber;Status;StatusInformation + - type: convert + column: 7 + conversion: array2SimpleStatus + mapping: + # ValueTable = Source(1) + source: "${source::monitors.other_device.collect.sources.source(1)}" + attributes: + id: $2 + metrics: + hw.status{hw.type="other_device"}: $7 + legacyTextParameters: + StatusInformation: $8 + blade: + discovery: + sources: + source(1): + # Source(1) = copy of Enclosure.Discovery.Source(1) + type: copy + from: "${source::monitors.enclosure.discovery.sources.source(1)}" + computes: + # Keep only blade lines + # Blade;DeviceID;Entity ID;Vendor;Model;SerialNumber;StatusArray;StatusInformation;AdditionalInformation1; + - type: keepOnlyMatchingLines + column: 1 + valueList: "Blade,Processing Blade" + mapping: + # InstanceTable = Source(1) + source: "${source::monitors.blade.discovery.sources.source(1)}" + attributes: + id: $2 + model: $5 + serial_number: $6 + info: $9 + hw.parent.type: enclosure + hw.parent.id: IPMI + name: "${awk::sprintf(\"%s (%s)\", $2, $5)}" + collect: + # Collect type is: multi-instance + type: multiInstance + keys: + - id + sources: + source(1): + # Source(1) = copy of Enclosure.Collect.Source(1) + type: copy + from: "${source::monitors.enclosure.collect.sources.source(1)}" + computes: + # Keep only blade lines + # Blade;DeviceID;Entity ID;Vendor;Model;SerialNumber;StatusArray;StatusInformation + - type: keepOnlyMatchingLines + column: 1 + valueList: "Blade,Processing Blade" + # Convert StatusArray to a simple (unique) PATROLStatus + # Blade;DeviceID;Entity ID;Vendor;Model;SerialNumber;Status;StatusInformation + - type: convert + column: 7 + conversion: array2SimpleStatus + mapping: + # ValueTable = Source(1) + source: "${source::monitors.blade.collect.sources.source(1)}" + attributes: + id: $2 + metrics: + hw.status{hw.type="blade"}: $7 + legacyTextParameters: + StatusInformation: $8 + battery: + discovery: + sources: + source(1): + # Source(1) = copy of Enclosure.Discovery.Source(1) + type: copy + from: "${source::monitors.enclosure.discovery.sources.source(1)}" + computes: + # Keep only battery lines + # Battery;DeviceID;Entity ID;Vendor;Model;SerialNumber;StatusArray;StatusInformation;AdditionalInformation1; + - type: keepOnlyMatchingLines + column: 1 + valueList: battery + - type: leftConcat + column: 6 + value: "serialNumber: " + mapping: + # InstanceTable = Source(1) + source: "${source::monitors.battery.discovery.sources.source(1)}" + attributes: + id: $2 + __display_id: $3 + model: $5 + info: "${awk::join(\" \", $9, $6)}" + hw.parent.type: enclosure + hw.parent.id: IPMI + name: "${awk::sprintf(\"%s (%s)\", $3, $5)}" + collect: + # Collect type is: multi-instance + type: multiInstance + keys: + - id + sources: + source(1): + # Source(1) = copy of Enclosure.Collect.Source(1) + type: copy + from: "${source::monitors.enclosure.collect.sources.source(1)}" + computes: + # Keep only Battery lines + # Battery;DeviceID;Entity ID;Vendor;Model;SerialNumber;StatusArray;StatusInformation + - type: keepOnlyMatchingLines + column: 1 + valueList: battery + # Convert StatusArray to a simple (unique) PATROLStatus + # Battery;DeviceID;Entity ID;Vendor;Model;SerialNumber;Status;StatusInformation + - type: convert + column: 7 + conversion: array2SimpleStatus + mapping: + # ValueTable = Source(1) + source: "${source::monitors.battery.collect.sources.source(1)}" + attributes: + id: $2 + metrics: + hw.status{hw.type="battery"}: $7 + legacyTextParameters: + StatusInformation: $8 diff --git a/src/it/metricshub-connectors/src/main/connector/hardware/IpmiTool/embeddedFile-1 b/src/it/metricshub-connectors/src/main/connector/hardware/IpmiTool/embeddedFile-1 new file mode 100644 index 0000000..0338282 --- /dev/null +++ b/src/it/metricshub-connectors/src/main/connector/hardware/IpmiTool/embeddedFile-1 @@ -0,0 +1,1225 @@ +BEGIN { + FS = ";"; + systemType = ""; + systemVendor = ""; + systemModel = ""; + systemSerialNumber = ""; + foundSystemType = 0; + systemPowerConsumption = ""; + systemStatus = ""; + systemStatusInformation = ""; + systemSensorNameList = ""; + machineStatus = "ON"; +} +($1 == "FRU" && foundSystemType == 0) { + systemVendor = $2; + lcaseSystemVendor = tolower(systemVendor); + systemModel = $3; + systemSerialNumber = $4; + + if (substr(lcaseSystemVendor, 1, 4) == "sun " || substr(lcaseSystemVendor, 1, 6) == "oracle" ) + { + if (tolower(systemModel) == "cmm") + { + next; + } + systemVendor = "Sun"; + systemType = systemVendor; + } + else if (substr(lcaseSystemVendor, 1, 7) == "hewlett" || substr(lcaseSystemVendor, 1, 2) == "hp") + { + systemVendor = "HP"; + systemType = systemVendor; + } + else if (substr(lcaseSystemVendor, 1, 4) == "dell") + { + systemVendor = "Dell"; + systemType = systemVendor; + } + else if (substr(lcaseSystemVendor, 1, 22) == "international business" || substr(lcaseSystemVendor, 1, 3) == "ibm" || systemModel ~ /^[Ss]ystem x/) + { + systemVendor = "IBM"; + systemType = systemVendor; + } + else if (substr(lcaseSystemVendor, 1, 5) == "cisco") + { + systemVendor = "Cisco"; + systemType = systemVendor; + systemModel = "UCS " systemModel; + } + else if (substr(lcaseSystemVendor, 1, 7) == "fujitsu") + { + systemVendor = "Fujitsu-Siemens"; + systemType = systemVendor; + } + else if (substr(lcaseSystemVendor, 1, 3) == "nec") + { + systemVendor = "NEC"; + systemType = systemVendor; + } + else if (substr(lcaseSystemVendor, 1, 4) == "bull") + { + systemVendor = "BULL"; + systemType = systemVendor; + } + else if (substr(lcaseSystemVendor, 1, 10) == "supermicro") + { + systemVendor = "SuperMicro"; + systemType = systemVendor; + } + else if (substr(lcaseSystemVendor, 1, 4) == "acer") + { + systemVendor = "Acer"; + systemType = systemVendor; + } + else if (substr(lcaseSystemVendor, 1, 7) == "hitachi" || substr(lcaseSystemVendor, 1, 3) == "hds") + { + systemVendor = "Hitachi" + systemType = systemVendor; + } + + if (systemType != "") + { + foundSystemType = 1; + } +} +($1 == "PowerConsumption") { + + ############################################################## + # + # Section to be customized per vendor/model to handle + # the power consumption of the monitored system + # + # (ex.: recognizing input/output currents, adding up the power + # consumed by several power supplies, etc.) + # + ############################################################## + + if (systemType == "Cisco") + { + if ($3 ~ /^PSU[0-9]_PIN$/ && $5 ~ /^[0-9]/) + { + systemPowerConsumption = systemPowerConsumption + $5; + } + } + else + { + # By default, take the highest reported value + if ($5 ~ /^[0-9]/) + { + if ($5 > systemPowerConsumption || systemPowerConsumption == "") + { + systemPowerConsumption = $5; + } + } + } +} +{ + # Read the device characteristics + deviceType = $1; + deviceID = $2; + entityID = $3; + vendor = $4; + model = $5; + serialNumber = $6; + sensorList = $7; + + # Skip empty stuff + if (deviceType == "" || deviceID == "") + { + next; + } + + # Skip SEL Fullness Temperature Devices + if (deviceType == "Temperature" && entityID == "SEL Fullness") + { + next; + } + + # Simply re-print numeric sensors + if (deviceType == "Voltage" || deviceType == "Temperature" || deviceType == "Fan" || deviceType == "Current" || deviceType == "PowerConsumption" || deviceType == "EnergyUsage" || deviceType == "FRU") + { + print "MSHW;" $0; + next; + } + + ############################################################## + # + # Section where you can customize the device identification + # on a per-vendor basis + # + ############################################################## + if (systemType == "Sun") + { + if (sensorList ~ /nem[0-9]\./ || sensorList ~ "NEM[0-9]/") + { + deviceType = "NEM"; + } + + if (deviceType == "System Board") + { + if (sensorList ~ /bl[0-9]+\./) + { + deviceType = "Blade"; + } + } + } + else if (systemType == "IBM") + { + if (deviceType == "Add-in Card") + { + if (sensorList ~ /SAS Riser/) + { + deviceType = "SAS Riser" + } + else if (sensorList ~ /PCI Riser/) + { + deviceType = "PCI Riser" + } + } + else if (deviceType == "Power Module") + { + if (sensorList ~ /VRD Status/) + { + deviceType = "Voltage Regulator-Down" + } + } + else if (deviceType == "Group") + { + if (sensorList ~ /Mem Card/) + { + deviceType = "Memory Device" + } + } + } + + # Split that list into an array for further processing and interpretation + sensorCount = split(sensorList, sensorArray, "\\|"); + + status = ""; + statusInformation = ""; + sensorNameList = ""; + for (i=1 ; i<=sensorCount ; i++) + { status_atStart = status + equalsIndex = index(sensorArray[i], "="); + if (!equalsIndex) + { + continue; + } + sensorName = substr(sensorArray[i], 1, equalsIndex - 1); + sensorReading = substr(sensorArray[i], equalsIndex + 1, length(sensorArray[i]) - equalsIndex); + lcaseSensorReading = tolower(sensorReading); + + # Add the sensor name to the list of sensor names, so that the user will be able to see the list of + # sensors associated with one device + sensorNameList = sensorNameList sensorName ", "; + + # Sensors whose state can only be YES or NO (Asserted or Deasserted) + # and that, logically, need to be specifically interpreted + # Like (P0_OK == 1) means OK, while (P0_FAULT == 1) means ALARM, see? + if (sensorReading == "" || sensorReading == 0 || sensorReading == 1) + { + # Specific stuff + if (systemType == "Hitachi") + { + if (sensorName ~ / INTR$/) + { + # Skip MB0 INTR and similar sensors, because they only report when a button has been pressed + next; + } + else if (sensorName ~ /PWR MGMT$/ || sensorName ~ /Power Mgmt$/) + { + # Skip power management sensors + next; + } + else if (sensorName == "AGT_Notify") + { + # Skip AGT_Notify, which we can't do anything with, since we couldn't test it + # (it's actually on OEM sensor) + next; + } + else + { + # For all sensors with boolean value, in Hitachi systems, '1' means 'BAD' + if (sensorReading == 1) + { + status = status "ALARM|"; + statusInformation = statusInformation sensorName " Asserted - "; + } + else + { + status = status "OK|"; + } + } + } + + + # Dell Specific stuff + else if (systemType == "Dell") + { + if (sensorName ~ /VCORE/) + { if (sensorReading == 1) + { + status = status "ALARM|"; + statusInformation = statusInformation sensorName " VCORE Fault - "; + } + else + { + status = status "OK|"; + } + } + if (sensorName ~ /VCACHE [0-9]+ PG/) + { if (sensorReading == 1) + { + status = status "ALARM|"; + statusInformation = statusInformation sensorName " VCACHE Fault - "; + } + else + { + status = status "OK|"; + } + } + if (sensorName ~ /PROC VTT PG/) + { if (sensorReading == 1) + { + status = status "ALARM|"; + statusInformation = statusInformation sensorName " PROC VTT PG Fault - "; + } + else + { + status = status "OK|"; + } + } + if (sensorName ~ /[0-9.]+ PG/) + { if (sensorReading == 1) + { + status = status "ALARM|"; + statusInformation = statusInformation sensorName " Voltage Fault - "; + } + else + { + status = status "OK|"; + } + } + if (sensorName ~ /[0-9.]+ VIDEO PG/) + { if (sensorReading == 1) + { + status = status "ALARM|"; + statusInformation = statusInformation sensorName " VIDEO Voltage Fault - "; + } + else + { + status = status "OK|"; + } + } + } + + # Sun Specific stuff + else if (systemType == "Sun") + { + if (sensorName ~ /^PS[0-9]+\057VINOK/) + { if (sensorReading == 0) + { + status = status "ALARM|"; + statusInformation = statusInformation sensorName " Voltage In Fault - "; + } + else + { + status = status "OK|"; + } + } + if (sensorName ~ /^PS[0-9]+\057PWROK/) + { if (sensorReading == 0) + { + status = status "ALARM|"; + statusInformation = statusInformation sensorName " Power In Fault - "; + } + else + { + status = status "OK|"; + } + } + if (sensorName ~ /^PS[0-9]+\057FAN_ERR/) + { if (sensorReading == 1) + { + status = status "ALARM|"; + statusInformation = statusInformation sensorName " Fan In Fault - "; + } + else + { + status = status "OK|"; + } + } + if (sensorName ~ /^PS[0-9]+\057I_OUT_ERR/) + { if (sensorReading == 1) + { + status = status "ALARM|"; + statusInformation = statusInformation sensorName " Output Current Error - "; + } + else + { + status = status "OK|"; + } + } + if (sensorName ~ /^PS[0-9]+\057I_OUT_WARN/) + { if (sensorReading == 1) + { + status = status "WARN|"; + statusInformation = statusInformation sensorName " Output Current Warning - "; + } + else + { + status = status "OK|"; + } + } + if (sensorName ~ /^PS[0-9]+\057T_ERR/) + { if (sensorReading == 1) + { + status = status "ALARM|"; + statusInformation = statusInformation sensorName " Temperature Error - "; + } + else + { + status = status "OK|"; + } + } + if (sensorName ~ /^PS[0-9]+\057FAN_ERR/) + { if (sensorReading == 1) + { + status = status "WARN|"; + statusInformation = statusInformation sensorName " Temperature Warning - "; + } + else + { + status = status "OK|"; + } + } + if (sensorName ~ /^PS[0-9]+\057V_IN_ERR/) + { if (sensorReading == 1) + { + status = status "ALARM|"; + statusInformation = statusInformation sensorName " Input Voltage Error - "; + } + else + { + status = status "OK|"; + } + } + if (sensorName ~ /^PS[0-9]+\057V_IN_WARN/) + { if (sensorReading == 1) + { + status = status "WARN|"; + statusInformation = statusInformation sensorName " Input Voltage Warning - "; + } + else + { + status = status "OK|"; + } + } + if (sensorName ~ /^PS[0-9]+\057V_OUT_ERR/) + { if (sensorReading == 1) + { + status = status "ALARM|"; + statusInformation = statusInformation sensorName " Output Voltage Error - "; + } + else + { + status = status "OK|"; + } + } + if (sensorName ~ /^PS[0-9]+\057V_OUT_OK/) + { if (sensorReading == 0) + { + status = status "ALARM|"; + statusInformation = statusInformation sensorName " Output Voltage Not OK - "; + } + else + { + status = status "OK|"; + } + } + if (sensorName ~ /^INTSW/) + { if (sensorReading == 1) + { + status = status "OK|"; + statusInformation = statusInformation sensorName " Intrusion Detected - "; + } + else + { + status = status "OK|"; + } + } + if (sensorName ~ /^PWRBS/) + { if (sensorReading == 1) + { + status = status "ALARM|"; + statusInformation = statusInformation sensorName " A power supply sensor has detected an error - "; + } + else + { + status = status "OK|"; + } + } + } + + # Non vendor-specific stuff + else + { + # Try to be clever + if (index(tolower(sensorName), "fault") || index(tolower(sensorName), "fail") || tolower(sensorName) ~ /err$/) + { + if (sensorReading == 1) + { + status = status "ALARM|"; + statusInformation = statusInformation sensorName " Asserted - "; + } + else + { + status = status "OK|"; + } + } + else + { + status = status sensorName "=" sensorReading "|"; + } + } + + } + + # Here come OEM specific sensors, whose value need to be even more + # specifically interpreted + else if (substr(sensorReading, 1, 2) == "0x") + { + # Very specific stuff + sensorReading = substr(sensorReading, 3, 4); + digit1 = substr(sensorReading, 1, 1); + digit2 = substr(sensorReading, 2, 1); + digit3 = substr(sensorReading, 3, 1); + digit4 = substr(sensorReading, 4, 1); + + if (systemType == "Cisco") + { + if (substr(sensorName, 1, 4) == "LED_") + { + if (digit3 == 1) + { + color = "Green"; + onStatus = "OK"; + offStatus = "OK"; + blinkingStatus = "OK"; + + } + else if (digit3 == 2) + { + color = "Amber"; + onStatus = "WARN"; + offStatus = "OK"; + blinkingStatus = "WARN"; + } + else if (digit3 == 4) + { + color = "Blue"; + onStatus = "OK"; + offStatus = "OK"; + blinkingStatus = "OK"; + } + else if (digit3 == 8) + { + color = "Red"; + onStatus = "WARN"; + offStatus = "OK"; + blinkingStatus = "WARN"; + } + else + { + color = ""; + onStatus = "OK"; + offStatus = "OK"; + blinkingStatus = "OK"; + } + + if (digit4 == 1) + { + ledStatus = "Off"; + } + else if (digit4 == 2) + { + ledStatus = "On"; + } + else if (digit4 == 4) + { + ledStatus = "Blinking"; + } + else if (digit4 == 8) + { + ledStatus = "Blinking"; + } + else + { + ledStatus = "UNKNOWN"; + } + print "MSHW;LED;" sensorName ";" sensorName ";" deviceID ";" color ";" onStatus ";" offStatus ";" blinkingStatus ";" ledStatus; + continue; + } + else if (substr(sensorName, 1, 5) == "DDR3_") + { + if (digit2 == 1) + { + status = status "OK|"; + } + else + { + status = status "ALARM|"; + statusInformation = statusInformation sensorName " Failed - "; + } + } + } + else + { + status = status sensorName "=" sensorReading "|"; + } + } + + # And now, finally, the normal discrete sensors, with standard values + # These should be easy to interpret, except when vendors screw up their instrumentation + # chip, which does happen, unfortunately... + else + { + # Cisco-specific mess + if (systemType == "Cisco") + { + if (sensorName ~ /^SAS[0-9]+_LINK_STATUS$/) + { + if (lcaseSensorReading == "transition to off line") + { + status = status "OK|"; + } + else + { + status = status "ALARM|"; + statusInformation = statusInformation "Disconnected - "; + } + continue; + } + else if (sensorName == "PSU_REDUNDANCY") + { + # Skip this one entirely, because it creates a "fake" power supply + next; + } + else if (sensorName == "BIST_FAIL") + { + # Skip this sensor (but keep the device). Remove name from Sensor List and continue to next sensor. + gsubregex = sensorName ", " + gsub(gsubregex,"",sensorNameList); + continue; + } + else if (sensorName ~ /^HDD_[0-9]+_STATUS$/) + { + deviceID = substr(sensorName, 1, 6); + if (lcaseSensorReading == "state 0 asserted" || lcaseSensorReading == "drive present") + { + status = status "OK|"; + } + else + { + status = status "ALARM|"; + statusInformation = statusInformation "Faulty - "; + } + continue; + } + } + + # IBM Specific stuff + else if (systemType == "IBM") + { + if (sensorName ~ /^Cooling Zone/) + { + # Skip, because it just says that the fans' redundancy is OK + next; + } + } + + + + ################################ + # IPMI standard states + ################################ + + ########### Digital states + if (lcaseSensorReading == "predictive failure deasserted") + { + status = status "OK|"; + } + else if (lcaseSensorReading == "predictive failure asserted") + { + status = status "WARN|"; + statusInformation = statusInformation sensorName ": Predicted Failure - "; + } + else if (lcaseSensorReading == "limit not exceeded") + { + status = status "OK|"; + } + else if (lcaseSensorReading == "limit exceeded") + { + status = status "ALARM|"; + statusInformation = statusInformation sensorName ": Limit Exceeded - "; + } + else if (lcaseSensorReading == "performance met") + { + status = status "OK|"; + } + else if (lcaseSensorReading == "performance lags") + { + status = status "WARN|"; + statusInformation = statusInformation sensorName ": Performance Lags - "; + } + + + ########## Availability states + if (lcaseSensorReading == "device removed/device absent" || lcaseSensorReading == "device absent") + { + status = "ALARM|"; + statusInformation = "Missing reported by " sensorName; + break; + } + else if (lcaseSensorReading == "device inserted/device present" || lcaseSensorReading == "device present") + { + # Present, well, it doesnt mean much, but at least it s detected + status = status "OK|"; + } + else if (lcaseSensorReading == "device enabled") + { + status = status "OK|"; + } + else if (lcaseSensorReading == "device disabled") + { + status = status "OK|"; + statusInformation = statusInformation "Disabled - "; + } + else if (lcaseSensorReading == "transition to running") + { + status = status "OK|"; + } + else if (lcaseSensorReading == "transition to in test") + { + status = status "OK|"; + statusInformation = statusInformation sensorName ": In Test - "; + } + else if (lcaseSensorReading == "transition to power off") + { + status = status "OK|"; + statusInformation = statusInformation sensorName ": Power Off - "; + } + else if (lcaseSensorReading == "transition to on line") + { + status = status "OK|"; + statusInformation = statusInformation sensorName ": Online - "; + } + else if (lcaseSensorReading == "transition to off line") + { + status = status "ALARM|"; + statusInformation = statusInformation sensorName ": Offline - "; + } + else if (lcaseSensorReading == "transition to off duty") + { + status = status "OK|"; + statusInformation = statusInformation sensorName ": Off Duty - "; + } + else if (lcaseSensorReading == "transition to degraded") + { + status = status "WARN|"; + statusInformation = statusInformation sensorName ": Degraded - "; + } + else if (lcaseSensorReading == "transition to power save") + { + status = status "OK|"; + statusInformation = statusInformation sensorName ": Power Save - "; + } + else if (lcaseSensorReading == "install error") + { + status = status "ALARM|"; + statusInformation = statusInformation sensorName ": Install Error - "; + } + + + ########## Power states + if (lcaseSensorReading == "d0 power state" || lcaseSensorReading == "d1 power state" || lcaseSensorReading == "d2 power state" || lcaseSensorReading == "d3 power state") + { + status = status "OK|"; + statusInformation = statusInformation sensorName ": " lcaseSensorReading " - "; + } + + ########## Redundancy states + if (lcaseSensorReading == "fully redundant") + { + status = status "OK|"; + } + else if (lcaseSensorReading == "redundancy lost" || lcaseSensorReading == "redundancy degraded" || lcaseSensorReading == "redundancy degraded from fully redundant" || lcaseSensorReading == "redundancy degraded from non-redundant" || lcaseSensorReading == "non-redundant: sufficient from redundant" || lcaseSensorReading == "non-redundant: sufficient from insufficient" || lcaseSensorReading == "non-redundant: insufficient resources" || lcaseSensorReading == "non-redundant:sufficient resources from redundant redundancy has been lost but unit is functioning with minimum resources needed for normal operation" || lcaseSensorReading == "non-redundant:sufficient resources from insufficient resources unit has regained minimum resources needed for normal operation" || lcaseSensorReading == "non-redundant:insufficient resources unit is non-redundant and has insufficient resources to maintain normal operation" || lcaseSensorReading == "redundancy degraded from fully redundant unit has lost some redundant resource(s) but is still in a redundant state" || lcaseSensorReading == "redundancy degraded from non-redundant unit has regained some resource(s) and is redundant but not fully redundant") + { + status = status "WARN|"; + statusInformation = statusInformation sensorName ": " lcaseSensorReading " - "; + } + + + ######### Physical security + if (lcaseSensorReading == "general chassis intrusion" || lcaseSensorReading == "drive bay intrusion" || lcaseSensorReading == "i/o card area intrusion" || lcaseSensorReading == "processor area intrusion" || lcaseSensorReading == "system unplugged from lan" || lcaseSensorReading == "unauthorized dock" || lcaseSensorReading == "fan area intrusion") + { + status = status "ALARM|"; + statusInformation = statusInformation sensorName ": " lcaseSensorReading " - "; + } + + + ########## Platform security + if (lcaseSensorReading == "front panel lockout violation attempted" || lcaseSensorReading == "pre-boot password violation - user password" || lcaseSensorReading == "pre-boot password violation - setup password" || lcaseSensorReading == "pre-boot password violation - network boot password" || lcaseSensorReading == "other pre-boot password violation" || lcaseSensorReading == "out-of-band access password violation") + { + status = status "ALARM|"; + statusInformation = statusInformation sensorName ": " lcaseSensorReading " - "; + } + + + ######### Processor states + if (lcaseSensorReading == "ierr" || lcaseSensorReading == "thermal trip" || lcaseSensorReading == "frb1/bist failure" || lcaseSensorReading == "frb2/hang in post failure" || lcaseSensorReading == "frb3/processor startup/init failure" || lcaseSensorReading == "frb3/processor startup/initialization failure" || lcaseSensorReading == "configuration error" || lcaseSensorReading == "sm bios uncorrectable cpu-complex error" || lcaseSensorReading == "sm bios 'uncorrectable cpu-complex error'") + { + status = status "ALARM|"; + statusInformation = statusInformation sensorName ": " lcaseSensorReading " - "; + } + else if (lcaseSensorReading == "presence detected" || lcaseSensorReading == "processor presence detected") + { + status = status "OK|"; + } + else if (lcaseSensorReading == "throttled" || lcaseSensorReading == "processor automatically throttled") + { + status = status "OK|"; + statusInformation = statusInformation sensorName ": Throttled - "; + } + else if (lcaseSensorReading == "disabled" || lcaseSensorReading == "terminator presence detected" || lcaseSensorReading == "terminator presence detected" || lcaseSensorReading == "processor disabled") + { + # Skip a disabled processor, or a terminator + next; + } + + + ########### Power supply states + if (lcaseSensorReading == "presence detected") + { + status = status "OK|"; + } + else if (lcaseSensorReading == "power supply failure detected" || lcaseSensorReading == "failure detected") + { + status = status "ALARM|"; + statusInformation = statusInformation sensorName ": Failed - "; + } + else if (lcaseSensorReading == "predictive failure" || lcaseSensorReading == "predictive failure") + { + status = status "WARN|"; + statusInformation = statusInformation sensorName ": Predicted Failure - "; + } + else if (lcaseSensorReading == "power supply input lost (ac/dc)" || lcaseSensorReading == "power supply ac lost") + { + status = status "ALARM|"; + statusInformation = statusInformation sensorName ": AC Input Lost - "; + } + else if (lcaseSensorReading == "power supply input lost or out-of-range" || lcaseSensorReading == "ac lost or out-of-range") + { + status = status "ALARM|"; + statusInformation = statusInformation sensorName ": AC Input Lost or Out-of-Range - "; + } + else if (lcaseSensorReading == "power supply input out-of-range, but present" || lcaseSensorReading == "ac out-of-range, but present") + { + status = status "ALARM|"; + statusInformation = statusInformation sensorName ": AC Input Out-of-Range - "; + } + else if (lcaseSensorReading == "configuration error" || substr(lcaseSensorReading, 1, 12) == "config error") + { + status = status "ALARM|"; + statusInformation = statusInformation sensorName ": Configuration Error - "; + } + + + ######### Power unit states + if (lcaseSensorReading == "power off/down" || lcaseSensorReading == "power cycle" || lcaseSensorReading == "240va power down" || lcaseSensorReading == "interlock power down" || lcaseSensorReading == "ac lost" || lcaseSensorReading == "soft-power control failure") + { + status = status "ALARM|"; + statusInformation = statusInformation sensorName ": " lcaseSensorReading " - "; + } + else if (lcaseSensorReading == "failure detected") + { + status = status "ALARM|"; + statusInformation = statusInformation sensorName ": Failed - "; + } + else if (lcaseSensorReading == "predictive failure") + { + status = status "WARN|"; + statusInformation = statusInformation sensorName ": Predicted Failure - "; + } + + + ########## Memory states + if (lcaseSensorReading == "correctable ecc" || lcaseSensorReading == "correctable ecc/other correctable memory error") + { + status = status "WARN|"; + statusInformation = statusInformation sensorName ": Corrected Errors - "; + } + else if (lcaseSensorReading == "uncorrectable ecc" || lcaseSensorReading == "uncorrectable ecc/other uncorrectable memory error") + { + status = status "ALARM|"; + statusInformation = statusInformation sensorName ": Uncorrectable Errors - "; + } + else if (lcaseSensorReading == "parity") + { + status = status "OK|"; + } + else if (lcaseSensorReading == "memory scrub failed") + { + status = status "ALARM|"; + statusInformation = statusInformation sensorName ": Memory Scrub Failed - "; + } + else if (lcaseSensorReading == "memory device disabled") + { + # Skip this memory module + next; + } + else if (lcaseSensorReading == "correctable ecc logging limit reached" || lcaseSensorReading == "correctable ecc/other correctable memory error logging limit reached") + { + status = status "WARN|"; + statusInformation = statusInformation sensorName ": Too Many Errors - "; + } + else if (lcaseSensorReading == "presence detected" || lcaseSensorReading == "presence detected") + { + status = status "OK|"; + } + else if (lcaseSensorReading == "configuration error" || lcaseSensorReading == "configuration error") + { + status = status "ALARM|"; + statusInformation = statusInformation sensorName ": Configuration Error - "; + } + else if (lcaseSensorReading == "spare") + { + status = status "OK|"; + statusInformation = statusInformation "Spare - "; + } + else if (lcaseSensorReading == "throttled") + { + status = status "OK|"; + statusInformation = statusInformation sensorName ": Throttled - "; + } + + + ########## Disk states + if (lcaseSensorReading == "drive present") + { + status = status "OK|"; + } + else if (lcaseSensorReading == "drive fault") + { + status = status "ALARM|"; + statusInformation = statusInformation sensorName ": Faulty - "; + } + else if (lcaseSensorReading == "predictive failure") + { + status = status "WARN|"; + statusInformation = statusInformation sensorName ": Predicted Failure - "; + } + else if (lcaseSensorReading == "hot spare" || lcaseSensorReading == "in critical array" || lcaseSensorReading == "in failed array") + { + status = status "OK|"; + statusInformation = statusInformation sensorName ": " lcaseSensorReading " - "; + } + else if (lcaseSensorReading == "parity check in progress" || lcaseSensorReading == "rebuild in progress") + { + status = status "WARN|"; + statusInformation = statusInformation sensorName ": " lcaseSensorReading " - "; + } + else if (lcaseSensorReading == "rebuild aborted") + { + status = status "ALARM|"; + statusInformation = statusInformation sensorName ": Rebuild Aborted - "; + } + + ########### Cable interconnect states + if (lcaseSensorReading == "connected") + { + status = status "OK|"; + } + else if (lcaseSensorReading == "config error") + { + status = status "ALARM|"; + statusInformation = statusInformation sensorName ": Configuration Error - "; + } + + ########### Boot error states + if (lcaseSensorReading == "no bootable media" || lcaseSensorReading == "non-bootable disk in drive" || lcaseSensorReading == "pxe server not found" || lcaseSensorReading == "invalid boot sector" || lcaseSensorReading == "timeout waiting for selection") + { + status = status "ALARM|"; + statusInformation = statusInformation sensorName ": " lcaseSensorReading " - "; + } + + ########## Slot/connector states + if (lcaseSensorReading == "fault status") + { + status = status "ALARM|"; + statusInformation = statusInformation sensorName ": Faulty - "; + } + else if (lcaseSensorReading == "identify status" || lcaseSensorReading == "device installed" || lcaseSensorReading == "ready for device installation" || lcaseSensorReading == "ready for device removal" || lcaseSensorReading == "slot power is off" || lcaseSensorReading == "device removal request" || lcaseSensorReading == "interlock" || lcaseSensorReading == "slot is disabled" || lcaseSensorReading == "spare device") + { + status = status "OK|"; + } + + + ########## Presence states + if (lcaseSensorReading == "present" || lcaseSensorReading == "entity present") + { + status = status "OK|"; + } + else if (lcaseSensorReading == "absent" || lcaseSensorReading == "disabled" || lcaseSensorReading == "entity absent" || lcaseSensorReading == "entity disabled") + { + # Skip + next; + } + + + ########## LAN states + if (lcaseSensorReading == "heartbeat lost") + { + status = status "WARN|"; + statusInformation = statusInformation sensorName ": Heatbeat Lost - "; + } + else if (lcaseSensorReading == "heartbeat") + { + status = status "OK|"; + } + + + ########## Battery states + if (lcaseSensorReading == "low" || lcaseSensorReading == "battery low") + { + status = status "WARN|"; + statusInformation = statusInformation sensorName ": Low - "; + } + else if (lcaseSensorReading == "failed" || lcaseSensorReading == "battery failed") + { + status = status "ALARM|"; + statusInformation = statusInformation sensorName ": Failed - "; + } + else if (lcaseSensorReading == "presence detected" || lcaseSensorReading == "battery presence detected") + { + status = status "OK|"; + } + + + ########### Threshold states + if (lcaseSensorReading == "lower non-critical going low") + { + status = status "WARN|"; + statusInformation = statusInformation sensorName ": Lower Non-critical going low - "; + } + else if (lcaseSensorReading == "lower non-critical going high") + { + status = status "OK|"; + } + else if (lcaseSensorReading == "lower critical going low") + { + status = status "ALARM|"; + statusInformation = statusInformation sensorName ": Lower Critical going low - "; + } + else if (lcaseSensorReading == "lower critical going high") + { + status = status "WARN|"; + statusInformation = statusInformation sensorName ": Lower Critical going high - "; + } + else if (lcaseSensorReading == "lower non-recoverable going low") + { + status = status "ALARM|"; + statusInformation = statusInformation sensorName ": Lower Non-recoverable going low - "; + } + else if (lcaseSensorReading == "lower non-recoverable going high") + { + status = status "ALARM|"; + statusInformation = statusInformation sensorName ": Lower Non-recoverable going high - "; + } + else if (lcaseSensorReading == "upper non-critical going low") + { + status = status "OK|"; + } + else if (lcaseSensorReading == "upper non-critical going high") + { + status = status "WARN|"; + statusInformation = statusInformation sensorName ": Upper Non-critical going high - "; + } + else if (lcaseSensorReading == "upper critical going low") + { + status = status "WARN|"; + statusInformation = statusInformation sensorName ": Upper Critical going low - "; + } + else if (lcaseSensorReading == "upper critical going high") + { + status = status "ALARM|"; + statusInformation = statusInformation sensorName ": Upper Critical going high - "; + } + else if (lcaseSensorReading == "upper non-recoverable going low") + { + status = status "ALARM|"; + statusInformation = statusInformation sensorName ": Upper Non-recoverable going low - "; + } + else if (lcaseSensorReading == "upper non-recoverable going high") + { + status = status "ALARM|"; + statusInformation = statusInformation sensorName ": Upper Non-recoverable going high - "; + } + + + ########### Usage States + if (lcaseSensorReading == "transition to idle") + { + status = status "OK|"; + statusInformation = statusInformation sensorName ": Idle - "; + } + else if (lcaseSensorReading == "transition to active") + { + status = status "OK|"; + statusInformation = statusInformation sensorName ": Active - "; + } + else if (lcaseSensorReading == "transition to busy") + { + status = status "OK|"; + statusInformation = statusInformation sensorName ": Busy - "; + } + + + ########## Severity States + if (lcaseSensorReading == "transition to ok") + { + status = status "OK|"; + } + else if (lcaseSensorReading == "transition to non-critical from ok" || lcaseSensorReading == "transition to non-critical from ok") + { + status = status "WARN|"; + statusInformation = statusInformation sensorName ": Non-critical - "; + } + else if (lcaseSensorReading == "transition to critical from less severe") + { + status = status "ALARM|"; + statusInformation = statusInformation sensorName ": Critical - "; + } + else if (lcaseSensorReading == "transition to non-recoverable from less severe") + { + status = status "ALARM|"; + statusInformation = statusInformation sensorName ": Non-recoverable - "; + } + else if (lcaseSensorReading == "transition to non-critical from more severe" || lcaseSensorReading == "transition to non-critical from more severe") + { + status = status "WARN|"; + statusInformation = statusInformation sensorName ": Non-critical - "; + } + else if (lcaseSensorReading == "transition to critical from non-recoverable") + { + status = status "ALARM|"; + statusInformation = statusInformation sensorName ": Critical - "; + } + else if (lcaseSensorReading == "transition to non-recoverable") + { + status = status "ALARM|"; + statusInformation = statusInformation sensorName ": Non-recoverable - "; + } + else if (lcaseSensorReading == "monitor") + { + status = status "WARN|"; + statusInformation = statusInformation sensorName ": Monitor - "; + } + else if (lcaseSensorReading == "informational") + { + status = status "OK|"; + statusInformation = statusInformation sensorName ": Informational - "; + } + + ######### System ACPI Power State + if (lcaseSensorReading == "s0/g0: working" || lcaseSensorReading == "s0/g0 working" || lcaseSensorReading == "legacy on state") + { + status = status "OK|"; + } + else if (lcaseSensorReading == "s1: sleeping with system hw & processor context maintained" || lcaseSensorReading == "s1 sleeping with system h/w and processor context maintained" || lcaseSensorReading == "s2: sleeping, processor context lost" || lcaseSensorReading == "s2 sleeping,processor context lost" || lcaseSensorReading == "s3: sleeping, processor & hw context lost, memory retained" || lcaseSensorReading == "s3 sleeping,processor and h/w context lost, memory maintained" || lcaseSensorReading == "s4: non-volatile sleep/suspend-to-disk" || lcaseSensorReading == "s4 non-volatile sleep/suspend to disk" || lcaseSensorReading == "sleeping in s1/s2/s3 state" || lcaseSensorReading == "sleeping in an s1,s2 or s3 states" || lcaseSensorReading == "g1: sleeping" || lcaseSensorReading == "g1 sleeping") + { + status = status "OK|"; + statusInformation = statusInformation sensorName ": Sleeping - "; + machineStatus = "Sleeping"; + } + else if (lcaseSensorReading == "s5/g2: soft-off" || lcaseSensorReading == "s5/g2 soft-off" || lcaseSensorReading == "s4/s5: soft-off" || lcaseSensorReading == "s4/s5 soft-off" || lcaseSensorReading == "g3: mechanical off" || lcaseSensorReading == "g3/mechanical off" || lcaseSensorReading == "s5: entered by override" || lcaseSensorReading == "s5 entered by override" || lcaseSensorReading == "legacy off state") + { + status = status "OK|"; + statusInformation = statusInformation sensorName ": OFF - "; + machineStatus = "OFF"; + } + + ########### Other unknown states + else if (status_atStart == status) + { + status = status sensorName "=" sensorReading "|" + } + + + } + + #print "MSHW;" sensorName ";" sensorReading; + } + + # Last phase of processing + # Make sure we at least got something, otherwise it means we are not able to intepret anything anyway + if (status == "") + { + next; + } + + # Last question before we go, is this a system board, or BIOS, or stuff like that? + # In which case, we''re going to attach that to the main enclosure + if (tolower(deviceType) == "bios" || tolower(deviceType) == "system board") + { + systemStatus = systemStatus status; + systemStatusInformation = systemStatusInformation statusInformation; + systemSensorNameList = systemSensorNameList sensorNameList; + next; + } + + # Remove trailing comma at the end of sensorNameList + if (length(sensorNameList) > 2) + { + sensorNameList = substr(sensorNameList, 1, length(sensorNameList) - 2); + } + + # Good! + print "MSHW;" deviceType ";" deviceID ";" entityID ";" vendor ";" model ";" serialNumber ";" status ";" statusInformation ";Monitored by sensors: " sensorNameList; +} +END { + # At the very end, provide information about the system, including (if possible), power consumption, etc. + + # By the way, if we got no systemPowerConsumption but we know that the machine is sleeping of OFF, provide a low value instead + # of nothing. Because if we provide no value, the KM (and PM) will try to evaluate the power consumption based on the devices + # that we have discovered. Unfortunately, this will not take into account the fact that the machine may be not running, and + # thus calculate a value that is meaningless + if (systemPowerConsumption == "") + { + if (machineStatus == "Sleeping") + { + # If sleeping, assume 10 Watts for the system, plus another 10 Watts for the management card + # which is still running (because able to tell us that the main system is sleeping) + systemPowerConsumption = 20; + } + else if (machineStatus == "OFF") + { + # If OFF, assume 0 Watts for the main system, plus another 10 Watts for the management card + # (again, the management card must be present and running, since it told us the system was OFF) + systemPowerConsumption = 10; + } + + # If the machine is ON, leave the powerConsumption empty (as it was before we arrived in this place) + # so that Hardware Sentry will evaluate the power consumption by himself + } + + # Remove trailing comma at the end of systemSensorNameList + if (length(systemSensorNameList) > 2) + { + systemSensorNameList = substr(systemSensorNameList, 1, length(systemSensorNameList) - 2); + } + + # Print the enclosure stuff + print "MSHW;Enclosure;" systemVendor ";" systemModel ";" systemSerialNumber ";" systemStatus ";" systemStatusInformation ";" systemPowerConsumption ";Monitored by sensors: " systemSensorNameList; +} \ No newline at end of file diff --git a/src/it/metricshub-connectors/src/main/connector/hardware/LibreHardwareMonitor/LibreHardwareMonitor.yaml b/src/it/metricshub-connectors/src/main/connector/hardware/LibreHardwareMonitor/LibreHardwareMonitor.yaml new file mode 100644 index 0000000..0e51dcb --- /dev/null +++ b/src/it/metricshub-connectors/src/main/connector/hardware/LibreHardwareMonitor/LibreHardwareMonitor.yaml @@ -0,0 +1,564 @@ +--- +extends: +- ../Hardware/Hardware +connector: + displayName: Libre Hardware Monitor + platforms: Any Windows system + reliesOn: Libre Hardware Monitor + information: "This connector provides the hardware monitoring of Processors, Temperatures, Fans, Voltages, GPU, Memory Modules, Physical Disk of a computer." + detection: + connectionTypes: + - remote + - local + appliesTo: + - NT + criteria: + # DETECTION + # OS must be NT + - type: deviceType + keep: + - NT + # The root\LibreHardwareMonitor must be available + - type: wmi + query: SELECT Name FROM WMINET_InstrumentedAssembly + namespace: root\LibreHardwareMonitor +monitors: + enclosure: + discovery: + sources: + source(1): + # Hardware Discovery (no information about Enclosure) + # Identifier, Name, HardwareType + type: wmi + query: "SELECT Identifier, Name, HardwareType FROM Hardware" + namespace: root\LibreHardwareMonitor + source(2): + # Hardware Discovery (no information about Enclosure) + # Manufacturer, Model + type: wmi + query: "SELECT Manufacturer, Model FROM Win32_ComputerSystem" + namespace: root\cimv2 + mapping: + # InstanceTable + # Manufacturer, Model + source: "${source::monitors.enclosure.discovery.sources.source(2)}" + attributes: + id: PC + type: Computer + vendor: $1 + model: $2 + name: "${awk::sprintf(\"Computer: (%s %s)\", $1, $2)}" + voltage: + discovery: + sources: + source(1): + # Source 1 = Voltage Sensor + # Identifier, Name, SensorType, Parent, SensorValue + type: wmi + query: "SELECT Identifier, Name, Parent, Value FROM Sensor WHERE SensorType = 'Voltage'" + namespace: root\LibreHardwareMonitor + computes: + # Remove voltage sensors reporting zero + - type: excludeMatchingLines + column: 4 + regExp: "^[0.]+$" + source(2): + # Source 2 = Merge source 1 (voltage) and enclosure Hardware source + # SensorIdentifier, SensorName, SensorParent, SensorValue, HardwareIdentifier, HardwareName, HardwareType + type: tableJoin + leftTable: "${source::monitors.voltage.discovery.sources.source(1)}" + rightTable: "${source::monitors.enclosure.discovery.sources.source(1)}" + leftKeyColumn: 3 + rightKeyColumn: 1 + mapping: + # Instance table + # SensorIdentifier, SensorName, SensorParent, SensorValue, HardwareIdentifier, HardwareName, HardwareType + source: "${source::monitors.voltage.discovery.sources.source(2)}" + attributes: + id: $1 + __display_id: $6 + sensor_location: $2 + hw.parent.type: enclosure + hw.parent.id: PC + name: "${awk::sprintf(\"%s (%s)\", $6, $2)}" + collect: + type: multiInstance + keys: + - id + sources: + source(1): + # Source(1) = The Processor WMI class + # Identifier, Value, SensorType + type: wmi + query: "SELECT Identifier, Value FROM Sensor WHERE SensorType = 'Voltage'" + namespace: root\LibreHardwareMonitor + computes: + # Convert volts to millivolts + # Identifier, Value, SensorType + - type: multiply + column: 2 + value: 1000 + mapping: + # Value Table + # Identifier, Value + source: "${source::monitors.voltage.collect.sources.source(1)}" + attributes: + id: $1 + metrics: + hw.voltage: $2 + temperature: + discovery: + sources: + source(1): + # Source 1 = Temperature Sensor + # Identifier, Name, Parent, Value + type: wmi + query: "SELECT Identifier, Name, Parent, Value FROM Sensor WHERE SensorType ='Temperature'" + namespace: root\LibreHardwareMonitor + source(2): + # Source 3 = Merge Source 1 and 2 Together + # SensorIdentifier, SensorName, SensorParent, SensorValue, HardwareIdentifier, HardwareName, HardwareType + type: tableJoin + leftTable: "${source::monitors.temperature.discovery.sources.source(1)}" + rightTable: "${source::monitors.enclosure.discovery.sources.source(1)}" + leftKeyColumn: 3 + rightKeyColumn: 1 + computes: + # Perform some computation to exclude non-real sensors, and calculate alarm thresholds when possible + # SensorId;Name;WarningThreshold;AlarmThreshold; + - type: awk + script: "${file::embeddedFile-2}" + keep: ^MSHW; + separators: ; + selectColumns: "2,3,4,5" + mapping: + # Instance table + # SensorId;Name;WarningThreshold;AlarmThreshold; + source: "${source::monitors.temperature.discovery.sources.source(2)}" + attributes: + id: $1 + __display_id: $1 + sensor_location: $2 + hw.parent.type: enclosure + hw.parent.id: PC + name: "${awk::sprintf(\"%s (%s)\", $1, $2)}" + metrics: + hw.temperature.limit{limit_type="high.degraded"}: $3 + hw.temperature.limit{limit_type="high.critical"}: $4 + collect: + type: multiInstance + keys: + - id + sources: + source(1): + # Source(1) = The Processor WMI class + # Identifier, Value + type: wmi + query: "SELECT Identifier, Value FROM Sensor where SensorType = 'Temperature'" + namespace: root\LibreHardwareMonitor + mapping: + # Value Table + # Identifier, Value + source: "${source::monitors.temperature.collect.sources.source(1)}" + attributes: + id: $1 + metrics: + hw.temperature: $2 + physical_disk: + discovery: + sources: + source(1): + # Source 1 = Hardware Storage + # Identifier, Name, HardwareType + type: copy + from: "${source::monitors.enclosure.discovery.sources.source(1)}" + computes: + # Keep only Storage Type + # Identifier, Name, HardwareType + - type: keepOnlyMatchingLines + column: 3 + regExp: Storage + source(2): + # Source 2 = Storage Sensor + # Parent + type: wmi + query: SELECT Parent FROM Sensor where Name = 'Available Spare' OR Name = 'Remaining Life' + namespace: root\LibreHardwareMonitor + source(3): + # Source 3 = Table join to keep only disks that do have an Endurance Remaining sensor + # Identifier, Name, HardwareType, Identifier + type: tableJoin + leftTable: "${source::monitors.physical_disk.discovery.sources.source(1)}" + rightTable: "${source::monitors.physical_disk.discovery.sources.source(2)}" + leftKeyColumn: 1 + rightKeyColumn: 1 + mapping: + # Instance table + # Identifier, Name, HardwareType + source: "${source::monitors.physical_disk.discovery.sources.source(3)}" + attributes: + id: $1 + __display_id: $1 + vendor: $2 + hw.parent.type: enclosure + hw.parent.id: PC + name: "${awk::sprintf(\"%s (%s)\", $1, $2)}" + collect: + # Collect + type: multiInstance + keys: + - id + sources: + source(1): + # Source 1 = Storage Sensor + # Identifier, Parent, Value, Name + type: wmi + query: "SELECT Identifier, Parent, Value, Name FROM Sensor where Name = 'Available Spare' OR Name = 'Remaining Life'" + namespace: root\LibreHardwareMonitor + source(2): + # LeftTable = SensorIdentifier, SensorParent, SensorValue, SensorName + # RightTable = Identifier, Name, HardwareType + # Match SensorParent with HardwareIdentifier + # TableJoint = SensorIdentifier, SensorParent, SensorValue, SensorName, HardwareIdentifier, HardwareName, HardwareType + type: tableJoin + leftTable: "${source::monitors.physical_disk.collect.sources.source(1)}" + rightTable: "${source::monitors.enclosure.discovery.sources.source(1)}" + leftKeyColumn: 2 + rightKeyColumn: 1 + mapping: + # Instance table + # SensorIdentifier, SensorParent, SensorValue, SensorName, HardwareIdentifier, HardwareName, HardwareType + source: "${source::monitors.physical_disk.collect.sources.source(2)}" + attributes: + id: $2 + metrics: + hw.physical_disk.endurance_utilization{state="remaining"}: percent2Ratio($3) + fan: + discovery: + sources: + source(1): + # Source 1 = Fan Sensor + # Identifier, Name, Parent + # SensorIdentifier, SensorName, SensorParent, SensorValue + type: wmi + query: "SELECT Identifier, Name, Parent, Value FROM Sensor WHERE SensorType = 'Fan'" + namespace: root\LibreHardwareMonitor + computes: + # Remove fans that are not spinning + # SensorIdentifier, SensorName, SensorParent, SensorValue + - type: excludeMatchingLines + column: 4 + regExp: "^[0.]+$" + source(2): + # Source 3 = Merge Source 1 and 2 Together + # SensorIdentifier, SensorName, SensorParent, SensorValue, HardwareIdentifier, HardwareName, HardwareType + type: tableJoin + leftTable: "${source::monitors.fan.discovery.sources.source(1)}" + rightTable: "${source::monitors.enclosure.discovery.sources.source(1)}" + leftKeyColumn: 3 + rightKeyColumn: 1 + computes: + # SensorIdentifier, SensorName, SensorParent, SensorValue, HardwareIdentifier, HardwareName, HardwareType + - type: leftConcat + column: 6 + value: ' - ' + # Concatenate SensorName and HardwareName + # SensorIdentifier, SensorName, SensorParent, SensorValue, HardwareIdentifier, HardwareName, HardwareType + - type: leftConcat + column: 6 + value: $2 + mapping: + # Instance table + # SensorIdentifier, SensorName, SensorParent, HardwareIdentifier, HardwareName, HardwareType + source: "${source::monitors.fan.discovery.sources.source(2)}" + attributes: + id: $1 + __display_id: $6 + hw.parent.type: enclosure + hw.parent.id: PC + name: $6 + collect: + type: multiInstance + keys: + - id + sources: + source(1): + # Source(1) = The Processor WMI class + # Identifier, Value + type: wmi + query: "SELECT Identifier, Value FROM Sensor where SensorType ='Fan'" + namespace: root\LibreHardwareMonitor + mapping: + # Value Table + # Identifier, Value + source: "${source::monitors.fan.collect.sources.source(1)}" + attributes: + id: $1 + metrics: + hw.fan.speed: $2 + cpu: + discovery: + sources: + source(1): + # Source 1 = Hardware CPU + # Identifier, Name, HardwareType + type: copy + from: "${source::monitors.enclosure.discovery.sources.source(1)}" + computes: + # Keep only Storage Type + # HardwareIdentifier, HardwareName, HardwareType + - type: keepOnlyMatchingLines + column: 3 + regExp: cpu + source(2): + # Source 2 = CPU Power Consumption + # Name, Parent, CPUPowerConsumption + type: wmi + query: "SELECT Name, Parent, Value FROM Sensor where SensorType='Power' and Name = 'CPU Package' or Name ='Package'" + namespace: root\LibreHardwareMonitor + source(3): + # Source 1 : Hardware ID and Name + # Source 2 : CPUPowerConsumption + # HardwareIdentifier, HardwareName, HardwareType, Name, Parent, CPUPowerConsumption + type: tableJoin + leftTable: "${source::monitors.cpu.discovery.sources.source(1)}" + rightTable: "${source::monitors.cpu.discovery.sources.source(2)}" + leftKeyColumn: 1 + rightKeyColumn: 2 + defaultRightLine: ;;;; + computes: + # If CPUPowerConsumption < 1 then disable PowerConsumption parameter + - type: extract + column: 6 + subColumn: 1 + subSeparators: . + # Replace 0 by empty: ParameterActivation must be empty to be disabled + - type: replace + column: 6 + existingValue: 0 + newValue: "" + mapping: + # Instance table + # HardwareIdentifier, HardwareName, HardwareType, Name, Parent, CPUPowerConsumption + source: "${source::monitors.cpu.discovery.sources.source(3)}" + attributes: + id: $1 + __display_id: $1 + model: $2 + hw.parent.type: enclosure + hw.parent.id: PC + name: "${awk::sprintf(\"%s (%s)\", $1, $2)}" + conditionalCollection: + hw.power{hw.type="cpu"}: $6 + collect: + type: multiInstance + keys: + - id + sources: + source(1): + # Source(1) = Sensor type 'Clock' + # SensorName;CpuDeviceID;Clock + type: wmi + query: "SELECT Name, Parent, Value FROM Sensor where SensorType ='Clock'" + namespace: root\LibreHardwareMonitor + computes: + # Process that through an AWK script that will compute the clock speed average + # (yes, we have the clock speed for each core, and they can be different) + # CpuDeviceID;AverageClock + - type: awk + script: "${file::embeddedFile-3}" + keep: ^MSHW; + separators: ; + selectColumns: "2,3" + source(2): + # SensorName;CpuDeviceID;CPUPowerConsumption + type: wmi + query: "SELECT Name, Parent, Value FROM Sensor where SensorType='Power' and Name = 'CPU Package' or Name ='Package'" + namespace: root\LibreHardwareMonitor + source(3): + # Source 1 : Hardware ID and Name + # Source 2 : CPUPowerConsumption + # CpuDeviceID;AverageClock;SensorName;CpuDeviceID;CPUPowerConsumption + type: tableJoin + leftTable: "${source::monitors.cpu.collect.sources.source(1)}" + rightTable: "${source::monitors.cpu.collect.sources.source(2)}" + leftKeyColumn: 1 + rightKeyColumn: 2 + defaultRightLine: ;;;; + mapping: + # The Collect Table + # CpuDeviceID;AverageClock;SensorName;CpuDeviceID;CPUPowerConsumption + source: "${source::monitors.cpu.collect.sources.source(3)}" + attributes: + id: $1 + metrics: + hw.cpu.speed: megaHertz2Hertz($2) + hw.power{hw.type="cpu"}: $5 + hw.energy{hw.type="cpu"}: fakeCounter($5) + memory: + discovery: + sources: + source(1): + # Source 1 = Hardware Memory + # Identifier, Name, HardwareType + type: copy + from: "${source::monitors.enclosure.discovery.sources.source(1)}" + computes: + # Keep only Storage Type + # Identifier, Name, HardwareType + - type: keepOnlyMatchingLines + column: 3 + regExp: memory + source(2): + # Source 2 = Memory Sensor + # Name, Parent, MemoryAvailable + type: wmi + query: "SELECT Name, Parent, Value FROM Sensor where Name = 'Memory Available' OR Name ='Memory Used'" + namespace: root\LibreHardwareMonitor + source(3): + # Keep only Memory Available + # Name, Parent, MemoryAvailable + type: copy + from: "${source::monitors.memory.discovery.sources.source(2)}" + computes: + - type: keepOnlyMatchingLines + column: 1 + regExp: Memory Available + source(4): + # Keep only ram + # Name, Parent, MemoryUsed + type: copy + from: "${source::monitors.memory.discovery.sources.source(2)}" + computes: + - type: keepOnlyMatchingLines + column: 1 + regExp: Memory Used + source(5): + # Source 2 : MemoryAvailable + # Source 3 : MemoryUsed + # Name, Parent, MemoryAvailable, Name, Parent, MemoryUsed + type: tableJoin + leftTable: "${source::monitors.memory.discovery.sources.source(3)}" + rightTable: "${source::monitors.memory.discovery.sources.source(4)}" + leftKeyColumn: 2 + rightKeyColumn: 2 + computes: + # Clean duplicate column + # Parent, MemoryAvailable; MemoryUsed + - type: keepColumns + columnNumbers: "2,3,6" + # Sum up MemoryAvailable and MemoryUsed to get MemorySize + # Parent, MemorySize; MemoryUsed + - type: add + column: 2 + value: $3 + # Convert to GB + # Parent, MemorySize; MemoryUsed + - type: multiply + column: 2 + value: 1024 + source(6): + # Source 1 : MemoryName + # Source 6 : MemorySize + # Identifier, Name, HardwareType, Parent, MemorySize; MemoryUsed + type: tableJoin + leftTable: "${source::monitors.memory.discovery.sources.source(1)}" + rightTable: "${source::monitors.memory.discovery.sources.source(5)}" + leftKeyColumn: 1 + rightKeyColumn: 1 + mapping: + # Instance table + # Identifier, Name, HardwareType, Parent, MemorySize; MemoryUsed + source: "${source::monitors.memory.discovery.sources.source(6)}" + attributes: + id: $1 + __display_id: $2 + hw.parent.type: enclosure + hw.parent.id: PC + name: "${awk::sprintf(\"%s (%s MB)\", $2, $5)}" + metrics: + hw.memory.limit: mebiByte2Byte($5) + gpu: + discovery: + sources: + source(1): + # Discovery + # Source 1 = Hardware GPU + # Identifier, Name, HardwareType + type: copy + from: "${source::monitors.enclosure.discovery.sources.source(1)}" + computes: + # Keep only GPU + # Identifier, Name, HardwareType + - type: keepOnlyMatchingLines + column: 3 + regExp: gpu + # Remove the Gpu word in HardwareVendor + # Identifier, Name, HardwareType + - type: replace + column: 3 + existingValue: gpu + newValue: "" + source(2): + # Source 2 = Retrieve GPU Memory Size from Sensor + # Parent, Name, Value + type: wmi + query: "SELECT Parent, Name, Value FROM Sensor WHERE Name = 'GPU Memory Total'" + namespace: root\LibreHardwareMonitor + source(3): + # Source 3 = Joint source + # Source 1 : Get the Hardware Name + # Source 2 : Get the Memory Size + # Identifier, Name, HardwareType, ParentSensor, SensorName, MemorySize + type: tableJoin + leftTable: "${source::monitors.gpu.discovery.sources.source(1)}" + rightTable: "${source::monitors.gpu.discovery.sources.source(2)}" + leftKeyColumn: 1 + rightKeyColumn: 1 + defaultRightLine: ;;; + mapping: + # Instance table + # Identifier, Name, HardwareType, ParentSensor, SensorName, MemorySize + source: "${source::monitors.gpu.discovery.sources.source(3)}" + attributes: + id: $1 + __display_id: $2 + vendor: $3 + hw.parent.type: enclosure + hw.parent.id: PC + name: "${awk::sprintf(\"%s (%s - %s)\", $2, $3, mebiBytes2HumanFormat($6))}" + metrics: + hw.gpu.memory.limit: mebiByte2Byte($6) + collect: + type: multiInstance + keys: + - id + sources: + source(1): + # Source(1) = The Processor WMI class + # Name, Parent, Value, SensorType + type: wmi + query: "SELECT Name, Parent, Value, SensorType FROM Sensor" + namespace: root\LibreHardwareMonitor + computes: + # Name, gpuId, Value, SensorType + - type: awk + script: "${file::embeddedFile-1}" + keep: ^MSHW; + separators: ; + selectColumns: "2,3,4,5,6,7,8,9" + mapping: + # Value Table + # gpuID, VideoDecode, VideoEncode, Memory, PowerConsumption, ReveivedBytes, TransmittedBytes, UsedTimePercent + source: "${source::monitors.gpu.collect.sources.source(1)}" + attributes: + id: $1 + metrics: + hw.gpu.utilization{task="decoder"}: percent2Ratio($2) + hw.gpu.utilization{task="encoder"}: percent2Ratio($3) + hw.gpu.memory.utilization: percent2Ratio($4) + hw.power{hw.type="gpu"}: $5 + hw.energy{hw.type="gpu"}: fakeCounter($5) + hw.gpu.io{direction="receive"}: fakeCounter($6) + hw.gpu.io{direction="transmit"}: fakeCounter($7) + hw.gpu.utilization{task="general"}: percent2Ratio($8) diff --git a/src/it/metricshub-connectors/src/main/connector/hardware/LibreHardwareMonitor/embeddedFile-1 b/src/it/metricshub-connectors/src/main/connector/hardware/LibreHardwareMonitor/embeddedFile-1 new file mode 100644 index 0000000..fb1c995 --- /dev/null +++ b/src/it/metricshub-connectors/src/main/connector/hardware/LibreHardwareMonitor/embeddedFile-1 @@ -0,0 +1,18 @@ +BEGIN { + FS = ";"; +} +{ + if ($2 ~ /gpu-/) { gpuId = $2; gpuIds[gpuId] = gpuId} + if ($1 ~ /D3D Video Decode/) { gpuId = $2; VideoDecode[gpuId] = $3 } + if ($1 ~ /D3D Video Encode/) { gpuId = $2; VideoEncode[gpuId] = $3 } + if ($1 ~ /GPU Package/) { gpuId = $2; PowerConsumption[gpuId] = $3 } + if ($1 ~ /GPU PCIe Tx/) { gpuId = $2; TransmittedBytes[gpuId] = $3 } + if ($1 ~ /GPU PCIe Rx/) { gpuId = $2; ReceivedBytes[gpuId] = $3 } + if ($1 ~ /GPU Core/ && $4 ~ /Load/) { gpuId = $2; UsedTimePercent[gpuId] = $3 } + if ($1 ~ /GPU Memory$/ && $4 ~ /Load/) { gpuId = $2; Memory[gpuId] = $3} +} +END { + for (gpuId in gpuIds) { + print ("MSHW;"gpuId";"VideoDecode[gpuId]";"VideoEncode[gpuId]";"Memory[gpuId]";"PowerConsumption[gpuId]";"int(ReceivedBytes[gpuId])";"int(TransmittedBytes[gpuId])";"UsedTimePercent[gpuId]";" ); + } +} \ No newline at end of file diff --git a/src/it/metricshub-connectors/src/main/connector/hardware/LibreHardwareMonitor/embeddedFile-2 b/src/it/metricshub-connectors/src/main/connector/hardware/LibreHardwareMonitor/embeddedFile-2 new file mode 100644 index 0000000..c093075 --- /dev/null +++ b/src/it/metricshub-connectors/src/main/connector/hardware/LibreHardwareMonitor/embeddedFile-2 @@ -0,0 +1,87 @@ +BEGIN { FS = ";" } + +# Skip sensors that mention "Max" or "Average" (they are not real sensors anyway) +($2 ~ " Max" || $2 ~ " Average") { next; } + +# Skip sensors from it8721f, because this chip is known to provide false values +($3 ~ "it8721f") { next; } + +# Distance to TjMax sensors value are stored in an array to be retrieved later +($2 ~ / Distance to TjMax$/) { + name = $2; + gsub(/ Distance to TjMax$/, "", name); + distanceToThreshold[name] = $4 + next; +} + +# Parse all remaining lines and store the values in arrays that will be parsed +# in the END section (and reconciled with the distanceToThreshold array) +{ + sensorId = $1; + sensorName[sensorId] = $2; + sensorParent[sensorId] = $3; + sensorValue[sensorId] = $4; + parentId[sensorId] = $5; + parentType[sensorId] = $7; +} + +# Now process what we got in our arrays and join with the thresholds +END { + for (sensorId in sensorName) { + + # Retrieve the sensor properties from the arrays + name = sensorName[sensorId]; + parent = sensorParent[sensorId]; + value = sensorValue[sensorId]; + parentDeviceId = parentId[sensorId]; + deviceType = parentType[sensorId]; + + # Clean-up deviceType + if (deviceType == "Cpu") { deviceType = "CPU"; } + else if (deviceType ~ "Gpu") { deviceType = "GPU"; } + else if (deviceType == "Psu") { deviceType = "Power Supply"; } + + # Do we have a distance to TjMax? + distanceToTjMax = distanceToThreshold[name]; # not sensorId! + if (distanceToTjMax != "") { + wThres = ""; + aThres = value + distanceToTjMax; + } else { + # If no threshold from TjMax, try to figure something out + if (deviceType == "CPU") { + wThres = 80; + aThres = 90; + } else if (deviceType == "GPU") { + wThres = 85; + aThres = 100; + } else if (parentDeviceId ~ "^/(ssd|nvme)") { + wThres = 50; + aThres = 70; + } else if (parentDeviceId ~ "^/hdd") { + wThres = 45; + aThres = 60; + } else if (deviceType == "Motherboard") { + wThres = 40; + aThres = 45; + } else if (deviceType == "Memory" || deviceType == "Network") { + wThres = 50; + aThres = 60; + } else if (deviceType == "Power Supply") { + wThres = 55; + aThres = 65; + } else { + wThres = ""; + aThres = ""; + } + } + + # Build label + label = deviceType " " parentDeviceId; + if (name != "Temperature" && name != "") { + label = label " - " name + } + + # Print + print "MSHW;" sensorId ";" label ";" wThres ";" aThres ";" + } +} \ No newline at end of file diff --git a/src/it/metricshub-connectors/src/main/connector/hardware/LibreHardwareMonitor/embeddedFile-3 b/src/it/metricshub-connectors/src/main/connector/hardware/LibreHardwareMonitor/embeddedFile-3 new file mode 100644 index 0000000..a2319e4 --- /dev/null +++ b/src/it/metricshub-connectors/src/main/connector/hardware/LibreHardwareMonitor/embeddedFile-3 @@ -0,0 +1,21 @@ +BEGIN { FS = ";" } + +# Skip bus clock speed +($1 ~ /[Bb]us/) { next; } + +# Process all remaining lines +{ + if (clockCount[$2] == "") { + clockCount[$2] = 0; + clockSum[$2] = 0; + } + clockCount[$2] = clockCount[$2] + 1; + clockSum[$2] = clockSum[$2] + $3 +} + +# Print the result at the end +END { + for (cpuId in clockCount) { + print "MSHW;" cpuId ";" clockSum[cpuId] / clockCount[cpuId] + } +} \ No newline at end of file diff --git a/src/it/metricshub-connectors/src/main/connector/hardware/LinuxIPNetwork/LinuxIPNetwork.yaml b/src/it/metricshub-connectors/src/main/connector/hardware/LinuxIPNetwork/LinuxIPNetwork.yaml new file mode 100644 index 0000000..c8ec2bd --- /dev/null +++ b/src/it/metricshub-connectors/src/main/connector/hardware/LinuxIPNetwork/LinuxIPNetwork.yaml @@ -0,0 +1,21 @@ +--- +extends: +- ../Hardware/Hardware +- ../LinuxNetwork-header/LinuxNetwork-header +constants: + GLOBAL_COMMAND_LINE: /sbin/ip a + COLLECT_COMMAND_LINE: "/sbin/ip -s link show dev ${attribute::id}" +connector: + displayName: Linux - Network (ip) + platforms: Any system + reliesOn: Linux system commands (ip) + version: 1.0 + information: This connector provides the monitoring of active network cards on all Linux systems using ip command. + detection: + connectionTypes: + - remote + - local + appliesTo: + - Linux + supersedes: + - LinuxIfConfigNetwork diff --git a/src/it/metricshub-connectors/src/main/connector/hardware/LinuxIPNetwork/embeddedFile-1 b/src/it/metricshub-connectors/src/main/connector/hardware/LinuxIPNetwork/embeddedFile-1 new file mode 100644 index 0000000..8d4edbe --- /dev/null +++ b/src/it/metricshub-connectors/src/main/connector/hardware/LinuxIPNetwork/embeddedFile-1 @@ -0,0 +1,25 @@ +BEGIN { + deviceID = "" +} +/^lo[0-9]* / {deviceID = "" } +# ip a +$2 ~ /^lo[0-9]*/ {deviceID = "" } +$2 ~ /^eth[0-9][0-9]*:?|^vmnic[0-9][0-9]*:?|^em[0-9]*:?|^[Pp][0-9][0-9]*[Pp][0-9][0-9]*:?|^en[os][0-9]*:?|^enp[0-9]*s[0-9]*:?/ { + deviceID = $2 ; gsub(":","",deviceID) + ports[deviceID]=deviceID + if ($2 ~ /UP/ || $3 ~ /UP/ ) {portActive[deviceID] = 1} + } +$1 ~ /link.ether/ {macAddress[deviceID] = $2} +/ +inet [0-9]+/ { + ipAddress[deviceID] = $2 + gsub("/.*", "", ipAddress[deviceID]) +} +(/ UP / ) {portActive[deviceID] = 1} + +END { +for (deviceID in ports) { + if (ports[deviceID] != "" && portActive[deviceID] == 1) { + print "MSHW;" deviceID ";" macAddress[deviceID] ";" ipAddress[deviceID] ";" + } + } +} \ No newline at end of file diff --git a/src/it/metricshub-connectors/src/main/connector/hardware/LinuxIPNetwork/embeddedFile-2 b/src/it/metricshub-connectors/src/main/connector/hardware/LinuxIPNetwork/embeddedFile-2 new file mode 100644 index 0000000..a766720 --- /dev/null +++ b/src/it/metricshub-connectors/src/main/connector/hardware/LinuxIPNetwork/embeddedFile-2 @@ -0,0 +1,28 @@ +BEGIN { + transmitPackets = "" + transmitErrors = "" + receivePackets = "" + receiveErrors = "" + transmitBytes = "" + receiveBytes = "" +} +# ip a +$2 ~ /^eth[0-9][0-9]*:?|^vmnic[0-9][0-9]*:?|^em[0-9]*:?|^[Pp][0-9][0-9]*[Pp][0-9][0-9]*:?|^en[os][0-9]*:?|^enp[0-9]*s[0-9]*:?/ { + deviceID = $2 ; gsub(":","",deviceID) +} +$1 ~ /RX:/ && $2 ~ /bytes/ && $3 ~ /packets/ { + getline + receiveBytes = $1 + receivePackets = $2 + receiveErrors = $3 + } +$1 ~ /TX:/ && $2 ~ /bytes/ && $3 ~ /packets/ { + getline + transmitBytes = $1 + transmitPackets = $2 + transmitErrors = $3 + } + +END { + print "MSHW;" deviceID ";" receivePackets ";" transmitPackets ";" receiveErrors + transmitErrors ";" receiveBytes ";" transmitBytes +} \ No newline at end of file diff --git a/src/it/metricshub-connectors/src/main/connector/hardware/LinuxIfConfigNetwork/LinuxIfConfigNetwork.yaml b/src/it/metricshub-connectors/src/main/connector/hardware/LinuxIfConfigNetwork/LinuxIfConfigNetwork.yaml new file mode 100644 index 0000000..6d95a94 --- /dev/null +++ b/src/it/metricshub-connectors/src/main/connector/hardware/LinuxIfConfigNetwork/LinuxIfConfigNetwork.yaml @@ -0,0 +1,19 @@ +--- +extends: +- ../Hardware/Hardware +- ../LinuxNetwork-header/LinuxNetwork-header +constants: + GLOBAL_COMMAND_LINE: /sbin/ifconfig -a + COLLECT_COMMAND_LINE: "/sbin/ifconfig -a ${attribute::id}" +connector: + displayName: Linux - Network (ifconfig) + platforms: Any system + reliesOn: Linux system commands (ifconfig) + version: 1.0 + information: This connector provides the monitoring of active network cards on all Linux systems using ifconfig command. + detection: + connectionTypes: + - remote + - local + appliesTo: + - Linux diff --git a/src/it/metricshub-connectors/src/main/connector/hardware/LinuxIfConfigNetwork/embeddedFile-1 b/src/it/metricshub-connectors/src/main/connector/hardware/LinuxIfConfigNetwork/embeddedFile-1 new file mode 100644 index 0000000..0cb1e32 --- /dev/null +++ b/src/it/metricshub-connectors/src/main/connector/hardware/LinuxIfConfigNetwork/embeddedFile-1 @@ -0,0 +1,28 @@ +BEGIN { + deviceID = "" +} +/^lo[0-9]* / {deviceID = "" } +#ifconfig +$1 ~ /^eth[0-9][0-9]*:?|^vmnic[0-9][0-9]*:?|^em[0-9]*:?|^[Pp][0-9][0-9]*[Pp][0-9][0-9]*:?|^en[os][0-9]*:?|^enp[0-9]*s[0-9]*:?/ { + deviceID = $1 ; gsub(":","",deviceID) + ports[deviceID]=deviceID + if ($(NF-1) == "HWaddr") + { + macAddress[deviceID] = $NF + } + if ($2 ~ /UP/ || $3 ~ /UP/ ) {portActive[deviceID] = 1} +} +/ +inet addr:[0-9]+/ { + ipAddress[deviceID] = $2 + gsub("addr:", "", ipAddress[deviceID]) +} +(/ UP / ) {portActive[deviceID] = 1} +$1 ~ /^ether$/ && $2 ~ /[0-9A-Fa-f][0-9A-Fa-f]:[0-9A-Fa-f][0-9A-Fa-f]:/ {macAddress[deviceID] = $2} + +END { +for (deviceID in ports) { + if (ports[deviceID] != "" && portActive[deviceID] == 1) { + print "MSHW;" deviceID ";" macAddress[deviceID] ";" ipAddress[deviceID] ";" + } + } +} \ No newline at end of file diff --git a/src/it/metricshub-connectors/src/main/connector/hardware/LinuxIfConfigNetwork/embeddedFile-2 b/src/it/metricshub-connectors/src/main/connector/hardware/LinuxIfConfigNetwork/embeddedFile-2 new file mode 100644 index 0000000..5e07645 --- /dev/null +++ b/src/it/metricshub-connectors/src/main/connector/hardware/LinuxIfConfigNetwork/embeddedFile-2 @@ -0,0 +1,33 @@ +BEGIN { + transmitPackets = "" + transmitErrors = "" + receivePackets = "" + receiveErrors = "" + transmitBytes = "" + receiveBytes = "" +} +# ifconfig +$1 ~ /^eth[0-9][0-9]*:?|^vmnic[0-9][0-9]*:?|^em[0-9]*:?|^[Pp][0-9][0-9]*[Pp][0-9][0-9]*:?|^en[os][0-9]*:?|^enp[0-9]*s[0-9]*:?/ { + deviceID = $1 ; gsub(":","",deviceID) +} +/^ +RX packets:/ { + receivePackets = substr($2, 9, length($2) - 8) + receiveErrors = substr($3, 8, length($3) - 7) +} +/^ +TX packets:/ { + transmitPackets = substr($2, 9, length($2) - 8) + transmitErrors = substr($3, 8, length($3) - 7) +} +/^ +RX bytes:.*TX bytes:/ { + receiveBytes = substr($2, 7, length($2) - 6) + transmitBytes = substr($6, 7, length($6) - 6) +} + +$1 == "RX" && $2 == "packets" && $4 == "bytes" { receivePackets = $3 ; receiveBytes = $5 } +$1 == "TX" && $2 == "packets" && $4 == "bytes" { transmitPackets = $3 ; transmitBytes = $5} +$1 == "RX" && $2 == "errors" { receiveErrors = $3 } +$1 == "TX" && $2 == "errors" { transmitErrors = $3 } + +END { + print "MSHW;" deviceID ";" receivePackets ";" transmitPackets ";" receiveErrors + transmitErrors ";" receiveBytes ";" transmitBytes +} \ No newline at end of file diff --git a/src/it/metricshub-connectors/src/main/connector/hardware/LinuxMultipath/LinuxMultipath.yaml b/src/it/metricshub-connectors/src/main/connector/hardware/LinuxMultipath/LinuxMultipath.yaml new file mode 100644 index 0000000..009e3b7 --- /dev/null +++ b/src/it/metricshub-connectors/src/main/connector/hardware/LinuxMultipath/LinuxMultipath.yaml @@ -0,0 +1,79 @@ +--- +extends: +- ../Hardware/Hardware +connector: + displayName: Linux - Multipath + platforms: Any system with multipath + reliesOn: Linux multipath utility + version: 1.0 + information: This connector provides the monitoring of HBA cards on all Linux systems through the multipath utility. + detection: + connectionTypes: + - remote + - local + appliesTo: + - Linux + criteria: + # OS should be Linux + - type: deviceType + keep: + - Linux + # Test the multipath command + - type: osCommand + commandLine: "%{SUDO:multipath} multipath -l" + expectedResult: \(^\\_\)\|\(^\`\-\+\-\) +sudoCommands: +- multipath +monitors: + lun: + discovery: + sources: + source(1): + # Get the list of Ports from multipath -l + type: osCommand + commandLine: "%{SUDO:multipath} multipath -l" + computes: + # AWK it + # LunName;LunInfo;LunStatus; + - type: awk + script: "${file::embeddedFile-1}" + keep: ^MSHW; + separators: ; + selectColumns: "2,3,5" + mapping: + # InstanceTable = Source(1) + # LunName;LunInfo;LunStatus; + source: "${source::monitors.lun.discovery.sources.source(1)}" + attributes: + id: $1 + array_name: $2 + hw.parent.type: enclosure + name: $2 + collect: + # Collect type is multi-instance + type: multiInstance + keys: + - id + sources: + source(1): + # Get the list of Ports from multipath -l + type: osCommand + commandLine: "%{SUDO:multipath} multipath -l" + computes: + # AWK it + # LunName;NumberPaths;LunStatus;LunStatusInfo;AvailablePathInformation; + - type: awk + script: "${file::embeddedFile-1}" + keep: ^MSHW; + separators: ; + selectColumns: "2,4,5,6,7" + mapping: + # ValueTable = Source(1) + # LunName;NumberPaths;LunStatus;LunStatusInfo;AvailablePathInformation; + source: "${source::monitors.lun.collect.sources.source(1)}" + attributes: + id: $1 + metrics: + hw.lun.paths: $2 + legacyTextParameters: + AvailablePathInformation: $5 diff --git a/src/it/metricshub-connectors/src/main/connector/hardware/LinuxMultipath/embeddedFile-1 b/src/it/metricshub-connectors/src/main/connector/hardware/LinuxMultipath/embeddedFile-1 new file mode 100644 index 0000000..0332f32 --- /dev/null +++ b/src/it/metricshub-connectors/src/main/connector/hardware/LinuxMultipath/embeddedFile-1 @@ -0,0 +1,49 @@ +BEGIN {NumberPaths=0; LunName=""; LunInfo="" ; LunStatus=""; LunStatusInfo="";AvailablePaths=""} + +# RHEL 5 uses the format: +# 0HP_OPEN-E_500060e802c43216001e0f00000000000000c600dm-2 HP,OPEN-E +# [size=14G][features=0][hwhandler=0] +# \_ round-robin 0 [prio=0][active] +# \_ 1:0:0:30 sdc 8:32 [active][undef] + +# RHEL 6 uses the format: +# mpathc (360000970000294901120533030303644) dm-0 EMC,SYMMETRIX +# size=5.0G features='1 queue_if_no_path' hwhandler='0' wp=rw +# `-+- policy='round-robin 0' prio=0 status=active +# |- 2:0:0:3 sdc 8:32 active undef running +# `- 2:0:1:3 sde 8:64 active undef running +# +# OR +# +# mpath2 (36001b97004106bf304106bf26b52a9fc) dm-10 VIOLIN,SAN ARRAY +# size=128G features='0' hwhandler='0' wp=rw +# |-+- policy='round-robin 0' prio=0 status=enabled +# | `- 2:0:0:3 sdau 66:224 active undef running +# |-+- policy='round-robin 0' prio=0 status=enabled +# | `- 1:0:2:3 sdw 65:96 active undef running +# `-+- policy='round-robin 0' prio=0 status=enabled +# `- 1:0:3:3 sdag 66:0 active undef running + +$1 ~ /^[0-9A-Za-z][0-9A-Za-z]/ && $1 !~ /[=\[]/ { + if (LunName != "") {print ("MSHW;"LunName";"LunInfo";"NumberPaths";"LunStatus";"LunStatusInfo";"AvailablePaths";")} + NumberPaths=0; LunName=""; LunInfo="" ; LunStatus=""; LunStatusInfo="";AvailablePaths=""; + LunName=$1 ; LunInfo = $0 ; + } + +($1 ~ /\\_/ || $1 ~ /[\|\`]\-/) && $2 !~ /[0-9]+:[0-9]+:[0-9]+:[0-9]+/ { + if ($0 ~ /(active)|(ready)|(enabled)/) {LunStatus="OK" ;} + else if ($NF ~ /(faulty)|(failed)/) {LunStatus="ALARM" ; LunStatusInfo = $NF} + } +(($1 ~ /\\_/ || $1 ~ /[\|\`]\-/) && $2 ~ /[0-9]+:[0-9]+:[0-9]+:[0-9]+/ ) { + if ($0 ~ /(active)|(ready)|(enabled)/) {NumberPaths=NumberPaths+1 + if (AvailablePaths=="") {AvailablePaths="Path: " $2 " " $3} + else {AvailablePaths=AvailablePaths " - Path: " $2 " " $3}} + } + +($1 ~ /[|]/ && $2 ~ /[\|\`]\-/ && $3 ~ /[0-9]+:[0-9]+:[0-9]+:[0-9]+/ ) { + if ($0 ~ /(active)|(ready)|(enabled)/) {NumberPaths=NumberPaths+1 + if (AvailablePaths=="") {AvailablePaths="Path: " $3 " " $4} + else {AvailablePaths=AvailablePaths " - Path: " $3 " " $4}} + } + +END {if (LunName != "" ) {print ("MSHW;"LunName";"LunInfo";"NumberPaths";"LunStatus";"LunStatusInfo";"AvailablePaths";")}} \ No newline at end of file diff --git a/src/it/metricshub-connectors/src/main/connector/hardware/LinuxNetwork-header/LinuxNetwork-header.yaml b/src/it/metricshub-connectors/src/main/connector/hardware/LinuxNetwork-header/LinuxNetwork-header.yaml new file mode 100644 index 0000000..465b6cb --- /dev/null +++ b/src/it/metricshub-connectors/src/main/connector/hardware/LinuxNetwork-header/LinuxNetwork-header.yaml @@ -0,0 +1,91 @@ +--- +sudoCommands: +- ethtool +connector: + detection: + criteria: + # DETECTION + - type: deviceType + keep: + - Linux + - type: osCommand + commandLine: GLOBAL_COMMAND_LINE + expectedResult: LOOPBACK +monitors: + network: + discovery: + sources: + source(1): + # Discovery + # Source(1) = output of the command ipconfig or ip + type: osCommand + commandLine: GLOBAL_COMMAND_LINE + computes: + # Process the output of one of the two commands (ifconfig or ip) through an AWK script + # DeviceID;MacAddress;IPAddress; + - type: awk + script: "${file::embeddedFile-1}" + keep: ^MSHW; + separators: ; + selectColumns: "2,3,4" + mapping: + # InstanceTable = Source(1) + source: "${source::monitors.network.discovery.sources.source(1)}" + attributes: + id: $1 + physical_address: $2 + physical_address_type: MAC + logical_address: $3 + logical_address_type: IP + hw.parent.type: enclosure + name: $1 + collect: + # Collect + # Collect type is: mono-collect + type: monoInstance + sources: + source(1): + # Source(1) = output of the ipconfig or ip command for this interface + type: osCommand + commandLine: COLLECT_COMMAND_LINE + computes: + # Process the output of one of the two commands (ifconfig or ip) to retrieve statistics about the network interface + # DeviceID;ReceivedPackets;TransmittedPackets;Errors;ReceivedBytes;TransmittedBytes; + - type: awk + script: "${file::embeddedFile-2}" + keep: ^MSHW; + separators: ; + selectColumns: "2,3,4,5,6,7" + source(2): + # Source(2) = output of ethtool + type: osCommand + commandLine: "%{SUDO:ethtool}/usr/sbin/ethtool ${attribute::id}" + computes: + # Process the output of ethtool through an AWK script + # DeviceID;LinkStatus;LinkSpeed;DuplexMode; + - type: awk + script: "${file::embeddedFile-3}" + keep: ^MSHW; + separators: ; + selectColumns: "2,3,4,5" + source(3): + # Source(3) = table joint of Source(1) and Source(2) + # DeviceID;ReceivedPackets;TransmittedPackets;Errors;ReceivedBytes;TransmittedBytes;DeviceID;LinkStatus;LinkSpeed;DuplexMode; + type: tableJoin + leftTable: "${source::monitors.network.collect.sources.source(1)}" + rightTable: "${source::monitors.network.collect.sources.source(2)}" + leftKeyColumn: 1 + rightKeyColumn: 1 + defaultRightLine: ;;;; + mapping: + # And here is the ValueTable + source: "${source::monitors.network.collect.sources.source(3)}" + metrics: + hw.network.packets{direction="receive"}: $2 + hw.network.packets{direction="transmit"}: $3 + hw.errors{hw.type="network"}: $4 + hw.network.io{direction="receive"}: $5 + hw.network.io{direction="transmit"}: $6 + hw.network.up: legacyLinkStatus($8) + hw.network.bandwidth.limit: megaBit2Bit($9) + hw.network.full_duplex: legacyFullDuplex($10) diff --git a/src/it/metricshub-connectors/src/main/connector/hardware/LinuxNetwork-header/embeddedFile-3 b/src/it/metricshub-connectors/src/main/connector/hardware/LinuxNetwork-header/embeddedFile-3 new file mode 100644 index 0000000..f1ee3c8 --- /dev/null +++ b/src/it/metricshub-connectors/src/main/connector/hardware/LinuxNetwork-header/embeddedFile-3 @@ -0,0 +1,44 @@ +BEGIN { + linkSpeed = ""; + duplexMode = ""; + linkStatus = ""; + deviceID = ""; +} +/^Settings for / { + deviceID = $3; + gsub(":", "", deviceID); +} +/^[ \t]+Speed: / { + linkSpeed = $2; + gsub("Mb/s", "", linkSpeed); +} +/^[ \t]+Duplex: / { + if ($2 == "Full") + { + duplexMode = "Full"; + } + else + { + duplexMode = "Half"; + } +} +/^[ \t]+Link detected: / { + if ($3 == "yes") + { + linkStatus = "OK"; + } + else if ($3 == "no") + { + linkStatus = "WARN"; + } + else + { + linkStatus = ""; + } +} +END { + if (deviceID != "") + { + print "MSHW;" deviceID ";" linkStatus ";" linkSpeed ";" duplexMode ";" + } +} \ No newline at end of file diff --git a/src/it/metricshub-connectors/src/main/connector/hardware/LinuxNetwork/LinuxNetwork.yaml b/src/it/metricshub-connectors/src/main/connector/hardware/LinuxNetwork/LinuxNetwork.yaml new file mode 100644 index 0000000..57b8e31 --- /dev/null +++ b/src/it/metricshub-connectors/src/main/connector/hardware/LinuxNetwork/LinuxNetwork.yaml @@ -0,0 +1 @@ +--- {} diff --git a/src/it/metricshub-connectors/src/main/connector/hardware/MIB2-header/MIB2-header.yaml b/src/it/metricshub-connectors/src/main/connector/hardware/MIB2-header/MIB2-header.yaml new file mode 100644 index 0000000..b364fd7 --- /dev/null +++ b/src/it/metricshub-connectors/src/main/connector/hardware/MIB2-header/MIB2-header.yaml @@ -0,0 +1,268 @@ +--- +connector: + reliesOn: MIB-2 Standard SNMP Agent + version: 1.0 + information: This connector discovers the enclosure and Ethernet ports of a system equipped with an MIB-2 standard SNMP Agent. + detection: + connectionTypes: + - remote + - local + criteria: + # Criteria(1): there must be something in the ifTable SNMP Table + - type: snmpGetNext + oid: 1.3.6.1.2.1.2.2.1 +monitors: + network: + discovery: + sources: + source(1): + # Source(1) = ifTable SNMP Table + # PortID;Description;TypeCode;MacAddress;AdminStatus; + type: snmpTable + oid: 1.3.6.1.2.1.2.2.1 + selectColumns: "ID,2,3,6,7" + computes: + # Keep only ports whose administrative status is 'up' + # PortID;Description;TypeCode;MacAddress;AdminStatus; + - type: keepOnlyMatchingLines + column: 5 + valueList: 1 + # Keep only real Ethernet and/or FC ports + # PortID;Description;TypeCode;MacAddress;AdminStatus; + - type: keepOnlyMatchingLines + column: 3 + valueList: "6,7,26,32,37,62,94,95,96,97,117,166" + # Translate the TypeCode to a readable string + # PortID;Description;PortType;MacAddress;AdminStatus; + - type: translate + column: 3 + translationTable: "${translation::PortTypeTranslationTable}" + source(2): + # Get information from the ifXtable + # ID;Name;Alias; + type: snmpTable + oid: 1.3.6.1.2.1.31.1.1.1 + selectColumns: "ID,1,18" + source(3): + # Join the 32bit MIB2 table with the 64 bit IfXTable + # PortID;Description;TypeCode;MacAddress;AdminStatus;ID;Name;Alias; + type: tableJoin + leftTable: "${source::monitors.network.discovery.sources.source(1)}" + rightTable: "${source::monitors.network.discovery.sources.source(2)}" + leftKeyColumn: 1 + rightKeyColumn: 1 + defaultRightLine: ;;; + collect: + # Collect type = multi-instance + type: multiInstance + keys: + - id + sources: + source(1): + # Source(1) = ifTable SNMP Table + # PortID;Description;Speed;OperationalStatus;ReceivedBytes;ReceivedUnicastPackets;ReceivedNonUnicastPackets;ReceivedErrors;TransmittedBytes;TransmittedUnicastPackets;TransmittedNonUnicastPackets;TransmittedErrors; + type: snmpTable + oid: 1.3.6.1.2.1.2.2.1 + selectColumns: "ID,2,5,8,10,11,12,14,16,17,18,20" + computes: + # Duplicate Status twice + # PortID;Description;Speed;OperationalStatus;OperationalStatus;OperationalStatus;ReceivedBytes;ReceivedUnicastPackets;ReceivedNonUnicastPackets;ReceivedErrors;TransmittedBytes;TransmittedUnicastPackets;TransmittedNonUnicastPackets;TransmittedErrors; + - type: duplicateColumn + column: 4 + - type: duplicateColumn + column: 5 + # Translate the first column status into a PATROLStatus + # PortID;Description;Speed;PATROLStatus;OperationalStatus;OperationalStatus;ReceivedBytes;ReceivedUnicastPackets;ReceivedNonUnicastPackets;ReceivedErrors;TransmittedBytes;TransmittedUnicastPackets;TransmittedNonUnicastPackets;TransmittedErrors; + - type: translate + column: 4 + translationTable: "${translation::PortStatusTranslationTable}" + # Translate the second column status into a more readable string + # PortID;Description;Speed;PATROLStatus;StatusInformation;OperationalStatus;ReceivedBytes;ReceivedUnicastPackets;ReceivedNonUnicastPackets;ReceivedErrors;TransmittedBytes;TransmittedUnicastPackets;TransmittedNonUnicastPackets;TransmittedErrors; + - type: translate + column: 5 + translationTable: "${translation::PortStatusInformationTranslationTable}" + # Translate the third column status into a LinkStatus value + # PortID;Description;Speed;PATROLStatus;StatusInformation;LinkStatus;ReceivedBytes;ReceivedUnicastPackets;ReceivedNonUnicastPackets;ReceivedErrors;TransmittedBytes;TransmittedUnicastPackets;TransmittedNonUnicastPackets;TransmittedErrors; + - type: translate + column: 6 + translationTable: "${translation::PortLinkStatusInformationTranslationTable}" + # Convert bits/sec into Mbps + # PortID;Description;SpeedMBps;PATROLStatus;StatusInformation;LinkStatus;ReceivedBytes;ReceivedUnicastPackets;ReceivedNonUnicastPackets;ReceivedErrors;TransmittedBytes;TransmittedUnicastPackets;TransmittedNonUnicastPackets;TransmittedErrors; + - type: divide + column: 3 + value: 1000000 + # Add up ReceivedUnicastPackets and ReceivedNonUnicastPackets + # PortID;Description;SpeedMBps;PATROLStatus;StatusInformation;LinkStatus;ReceivedBytes;ReceivedPackets;ReceivedNonUnicastPackets;ReceivedErrors;TransmittedBytes;TransmittedUnicastPackets;TransmittedNonUnicastPackets;TransmittedErrors; + - type: add + column: 8 + value: $9 + # Add up TransmittedUnicastPackets and TransmittedNonUnicastPackets + # PortID;Description;SpeedMBps;PATROLStatus;StatusInformation;LinkStatus;ReceivedBytes;ReceivedPackets;ReceivedNonUnicastPackets;ReceivedErrors;TransmittedBytes;TransmittedPackets;TransmittedNonUnicastPackets;TransmittedErrors; + - type: add + column: 12 + value: $13 + # Add up ReceivedErrors and TransmittedErrors + # PortID;Description;SpeedMBps;PATROLStatus;StatusInformation;LinkStatus;ReceivedBytes;ReceivedPackets;ReceivedNonUnicastPackets;TotalErrors;TransmittedBytes;TransmittedPackets;TransmittedNonUnicastPackets;TransmittedErrors; + - type: add + column: 10 + value: $14 + source(2): + # Source(2) = ifMIBObjects SNMP Table + # PortID;SpeedMBs;ReceivedBytes64;ReceivedUnicastPackets64;ifHCInMulticastPkts64;ifHCInBroadcastPkts64;TransmittedBytes64;TransmittedPackets64;ifHCOutMulticastPkts;ifHCOutBroadcastPkts; + type: snmpTable + oid: 1.3.6.1.2.1.31.1.1.1 + selectColumns: "ID,15,6,7,8,9,10,11,12,13" + computes: + # Add MulticastPkts and BroadcastPkts to UnicastPackets + # PortID;SpeedMBs;ReceivedBytes64;ReceivedUnicastPackets64;ifHCInMulticastPkts64;ifHCInBroadcastPkts64;TransmittedBytes64;TransmittedPackets64;ifHCOutMulticastPkts;ifHCOutBroadcastPkts; + - type: add + column: 4 + value: $5 + - type: add + column: 4 + value: $6 + - type: add + column: 8 + value: $9 + - type: add + column: 8 + value: $10 + # Keep only useful columns + # PortID;SpeedMBs;ReceivedBytes64;ReceivedPackets64;TransmittedBytes64;TransmittedPackets64; + - type: keepColumns + columnNumbers: "1,2,3,4,7,8" + source(3): + # Source(3) = Table Joint of (1) and (2) + # PortID;Description;SpeedMBps;PATROLStatus;StatusInformation;LinkStatus;ReceivedBytes;ReceivedPackets;ReceivedNonUnicastPackets;TotalErrors;TransmittedBytes;TransmittedPackets;TransmittedNonUnicastPackets;TransmittedErrors;PortID;SpeedMBs;ReceivedBytes64;ReceivedPackets64;TransmittedBytes64;TransmittedPackets64; + type: tableJoin + leftTable: "${source::monitors.network.collect.sources.source(1)}" + rightTable: "${source::monitors.network.collect.sources.source(2)}" + leftKeyColumn: 1 + rightKeyColumn: 1 + defaultRightLine: ;;;;;; + computes: + # Now add MSHW to the Left and Right of the ifMIBObjects SpeedMBS + # PortID;Description;SpeedMBps;PATROLStatus;StatusInformation;LinkStatus;ReceivedBytes;ReceivedPackets;ReceivedNonUnicastPackets;TotalErrors;TransmittedBytes;TransmittedPackets;TransmittedNonUnicastPackets;TransmittedErrors;PortID;SpeedMBs; + - type: leftConcat + column: 16 + value: MSHW + - type: rightConcat + column: 16 + value: MSHW + # Replace "MSHWMSHW", i.e. a blank column with the ifTable value. + # This will use the old 32^2 limited value of link bps speed if a Mbps value is not available + # PortID;Description;SpeedMBps;PATROLStatus;StatusInformation;LinkStatus;ReceivedBytes;ReceivedPackets;ReceivedNonUnicastPackets;TotalErrors;TransmittedBytes;TransmittedPackets;TransmittedNonUnicastPackets;TransmittedErrors;PortID;SpeedMBs; + - type: replace + column: 16 + existingValue: MSHWMSHW + newValue: $3 + # Now get rid of any remaining MSHWs + # PortID;Description;SpeedMBps;PATROLStatus;StatusInformation;LinkStatus;ReceivedBytes;ReceivedPackets;ReceivedNonUnicastPackets;TotalErrors;TransmittedBytes;TransmittedPackets;TransmittedNonUnicastPackets;TransmittedErrors;PortID;SpeedMBs; + - type: replace + column: 16 + existingValue: MSHW + newValue: "" + source(4): + # Make a copy of Source(3) + type: copy + from: "${source::monitors.network.collect.sources.source(3)}" + computes: + # Keep only interfaces with 64bit counters + # 1 2 3 4 # 5 # 6 7 # 8 # 9 # 10 11 12 # 13 # # 14 # 15 16 17 # 18 # 19 # 20 + # PortID;Description;SpeedMBps;PATROLStatus;StatusInformation;LinkStatus;ReceivedBytes;ReceivedPackets;ReceivedNonUnicastPackets;TotalErrors;TransmittedBytes;TransmittedPackets;TransmittedNonUnicastPackets;TransmittedErrors;PortID;SpeedMBs;ReceivedBytes64;ReceivedPackets64;TransmittedBytes64;TransmittedPackets64; + - type: keepOnlyMatchingLines + column: 20 + regExp: . + # Replace 32 bit counters with 64 bit ones + # ReceivedBytes + # PortID;Description;SpeedMBps;PATROLStatus;StatusInformation;LinkStatus;ReceivedBytes;ReceivedPackets;ReceivedNonUnicastPackets;TotalErrors;TransmittedBytes;TransmittedPackets;TransmittedNonUnicastPackets;TransmittedErrors;PortID;SpeedMBs;ReceivedBytes64;ReceivedPackets64;TransmittedBytes64;TransmittedPackets64; + - type: replace + column: 7 + existingValue: $7 + newValue: $17 + # ReceivedPackets + - type: replace + column: 8 + existingValue: $8 + newValue: $18 + # TransmittedBytes + - type: replace + column: 11 + existingValue: $11 + newValue: $19 + # TransmittedPackets + - type: replace + column: 12 + existingValue: $12 + newValue: $20 + source(5): + # Make a copy of Source(3) + type: copy + from: "${source::monitors.network.collect.sources.source(3)}" + computes: + # Exclude interfaces with 64bit counters + # 1 2 3 4 # 5 # 6 7 # 8 # 9 # 10 11 12 # 13 # # 14 # 15 16 17 # 18 # 19 # 20 + # PortID;Description;SpeedMBps;PATROLStatus;StatusInformation;LinkStatus;ReceivedBytes;ReceivedPackets;ReceivedNonUnicastPackets;TotalErrors;TransmittedBytes;TransmittedPackets;TransmittedNonUnicastPackets;TransmittedErrors;PortID;SpeedMBs;ReceivedBytes64;ReceivedPackets64;TransmittedBytes64;TransmittedPackets64; + - type: excludeMatchingLines + column: 20 + regExp: . + source(6): + # Union 32 and 64 bit tables + # PortID;Description;SpeedMBps;PATROLStatus;StatusInformation;LinkStatus;ReceivedBytes;ReceivedPackets;ReceivedNonUnicastPackets;TotalErrors;TransmittedBytes;TransmittedPackets;TransmittedNonUnicastPackets;TransmittedErrors;PortID;SpeedMBs;ReceivedBytes64;ReceivedPackets64;TransmittedBytes64;TransmittedPackets64; + type: tableUnion + tables: + - "${source::monitors.network.collect.sources.source(4)}" + - "${source::monitors.network.collect.sources.source(5)}" + mapping: + # ValueTable = Source(6) + source: "${source::monitors.network.collect.sources.source(6)}" + attributes: + id: $1 + metrics: + hw.status{hw.type="network"}: $4 + hw.network.up: legacyLinkStatus($6) + hw.network.bandwidth.limit: megaBit2Bit($16) + hw.errors{hw.type="network"}: $10 + hw.network.packets{direction="receive"}: $8 + hw.network.packets{direction="transmit"}: $12 + hw.network.io{direction="receive"}: $7 + hw.network.io{direction="transmit"}: $11 + legacyTextParameters: + StatusInformation: $5 +translations: + PortLinkStatusInformationTranslationTable: + "2": degraded + "6": degraded + "7": degraded + Default: ok + PortStatusInformationTranslationTable: + "1": Up + "2": Down + "3": Testing + "5": Dormant + "6": Component Not Present + "7": Lower Layer Down + Default: Unknown Status + PortTypeTranslationTable: + "56": FC Port + "26": Ethernet + "37": ATM + Default: Ethernet + "166": MPLS + "6": Ethernet + "7": Ethernet + "117": Ethernet + "94": ADSL + "62": Ethernet + "95": RSDL + "96": SDSL + "97": VDSL + "32": Frame Relay + PortStatusTranslationTable: + "1": ok + "2": ok + "3": degraded + "5": ok + "6": failed + "7": failed + Default: UNKNOWN diff --git a/src/it/metricshub-connectors/src/main/connector/hardware/MIB2/MIB2.yaml b/src/it/metricshub-connectors/src/main/connector/hardware/MIB2/MIB2.yaml new file mode 100644 index 0000000..cab7063 --- /dev/null +++ b/src/it/metricshub-connectors/src/main/connector/hardware/MIB2/MIB2.yaml @@ -0,0 +1,55 @@ +--- +extends: +- ../Hardware/Hardware +- ../MIB2-header/MIB2-header +connector: + displayName: MIB-2 Standard SNMP Agent - Network Interfaces + platforms: Any system with SNMP + detection: + appliesTo: + - Network + - OOB + - HP + - Storage + - VMS + - OSF1 + supersedes: + - HPUXNetwork +monitors: + network: + discovery: + sources: + source(3): + computes: + # Now add MSHW to the Left and Right of the ifMIBObjects SpeedMBS + # PortID;Description;PortType;MacAddress;AdminStatus;ID;Name;Alias; + - type: leftConcat + column: 7 + value: MSHW + - type: rightConcat + column: 7 + value: MSHW + # Replace "MSHWMSHW", i.e. a blank column with the ifTable value. + # PortID;Description;PortType;MacAddress;AdminStatus;ID;Name;Alias; + - type: replace + column: 7 + existingValue: MSHWMSHW + newValue: $2 + # Now get rid of any remaining MSHWs + # PortID;Description;PortType;MacAddress;AdminStatus;ID;Name;Alias; + - type: replace + column: 7 + existingValue: MSHW + newValue: "" + mapping: + # InstanceTable = Source(1) + # PortID;Description;PortType;MacAddress;AdminStatus;ID;Name;Alias; + source: "${source::monitors.network.discovery.sources.source(3)}" + attributes: + id: $1 + __display_id: $7 + physical_address: $4 + physical_address_type: MAC + device_type: $3 + hw.parent.type: enclosure + name: "${awk::sprintf(\"%s (%s)\", $7, $3)}" diff --git a/src/it/metricshub-connectors/src/main/connector/hardware/MIB2EntitySensor-header/MIB2EntitySensor-header.yaml b/src/it/metricshub-connectors/src/main/connector/hardware/MIB2EntitySensor-header/MIB2EntitySensor-header.yaml new file mode 100644 index 0000000..891bd6f --- /dev/null +++ b/src/it/metricshub-connectors/src/main/connector/hardware/MIB2EntitySensor-header/MIB2EntitySensor-header.yaml @@ -0,0 +1,262 @@ +--- +monitors: + enclosure: + discovery: + sources: + source(1): + # Source(1) = SNMP Table + # ID;PhysicalDescription;Parent;Class;HardwareRev;FirmwareRev;SerialNumber;Manufacturer;Model;FRU + type: snmpTable + oid: 1.3.6.1.2.1.47.1.1.1.1 + selectColumns: "ID,2,4,5,8,9,11,12,13,16" + source(2): + # ID;type; + type: snmpTable + oid: 1.3.6.1.2.1.99.1.1.1 + selectColumns: "ID,1,5" + source(3): + # ID;PhysicalDescription;Parent;Class;HardwareRev;FirmwareRev;SerialNumber;Manufacturer;Model;FRU;SensorID;type;value; + type: tableJoin + leftTable: "${source::monitors.enclosure.discovery.sources.source(1)}" + rightTable: "${source::monitors.enclosure.discovery.sources.source(2)}" + leftKeyColumn: 1 + rightKeyColumn: 1 + defaultRightLine: ;;; + computes: + # Exclude sensors with status "No Longer Present + - type: excludeMatchingLines + column: 13 + regExp: 5 + source(4): + # Keep Source 3 clean for other classes + type: copy + from: "${source::monitors.enclosure.discovery.sources.source(3)}" + computes: + # Keep only units of type 3 (chassis) + # ID;PhysicalDescription;Parent;Class;HardwareRev;FirmwareRev;SerialNumber;Manufacturer;Model;FRU;SensorID;type; + - type: keepOnlyMatchingLines + column: 4 + valueList: 3 + mapping: + # Instance Table + source: "${source::monitors.enclosure.discovery.sources.source(4)}" + attributes: + id: _DEVICE_ID + __display_id: $2 + vendor: $8 + model: $9 + serial_number: $7 + bios_version: $5 + type: _ENCLOSURE_TYPE + name: "${awk::sprintf(\"Enclosure: %s (%s %s)\", $2, $8, $9)}" + collect: + sources: + source(1): + # Entity-Sensor-MIB + # ID;type;precision;Value;State; + type: snmpTable + oid: 1.3.6.1.2.1.99.1.1.1 + selectColumns: "ID,1,3,4,5" + computes: + - type: translate + column: 3 + translationTable: "${translation::entPhySensorPrecisionTranslationTable}" + - type: divide + column: 4 + value: $3 + - type: duplicateColumn + column: 5 + source(2): + # For use in all other classes + type: copy + from: "${source::monitors.enclosure.collect.sources.source(1)}" + computes: + # Translate state status into a Status + # ID;type;precision;Value;PATROLStatus;StatusInformation; + - type: translate + column: 5 + translationTable: "${translation::SensorStatusTranslationTable}" + # Translate the second state status into a StatusInformation + # ID;type;precision;Value;PATROLStatus;StatusInformation; + - type: translate + column: 6 + translationTable: "${translation::SensorStatusInformationTranslationTable}" + mapping: + # No status for chassis is available therefore it is not collected. + # ValueTable = Source(2) + source: "${source::monitors.enclosure.collect.sources.source(2)}" + attributes: + id: _DEVICE_ID + temperature: + discovery: + sources: + source(1): + # ID;PhysicalDescription;Parent;Class;HardwareRev;FirmwareRev;SerialNumber;Manufacturer;Model;FRU;SensorID;type; + type: copy + from: "${source::monitors.enclosure.discovery.sources.source(3)}" + computes: + # Keep only celcius(8) (temperature sensors) + # ID;PhysicalDescription;Parent;Class;HardwareRev;FirmwareRev;SerialNumber;Manufacturer;Model;FRU;SensorID;type; + - type: keepOnlyMatchingLines + column: 12 + valueList: 8 + mapping: + # Instance Table + source: "${source::monitors.temperature.discovery.sources.source(1)}" + attributes: + id: $11 + __display_id: $2 + hw.parent.type: enclosure + hw.parent.id: _DEVICE_ID + name: $2 + collect: + # Collect type = multi-instance + type: multiInstance + keys: + - id + mapping: + source: "${source::monitors.enclosure.collect.sources.source(2)}" + attributes: + id: $1 + metrics: + hw.temperature: $4 + hw.status{hw.type="temperature"}: $5 + legacyTextParameters: + StatusInformation: $6 + voltage: + discovery: + sources: + source(1): + # ID;PhysicalDescription;Parent;Class;HardwareRev;FirmwareRev;SerialNumber;Manufacturer;Model;FRU;SensorID;type; + type: copy + from: "${source::monitors.enclosure.discovery.sources.source(3)}" + computes: + # Keep Voltages (voltsAC, voltsDC) + - type: keepOnlyMatchingLines + column: 12 + valueList: "3,4" + mapping: + # InstanceTable = Source(1) + source: "${source::monitors.voltage.discovery.sources.source(1)}" + attributes: + id: $11 + __display_id: $2 + sensor_location: $2 + hw.parent.type: enclosure + hw.parent.id: _DEVICE_ID + name: "${awk::sprintf(\"%s (%s)\", $2, $2)}" + collect: + # Collect type = multi-instance + type: multiInstance + keys: + - id + sources: + source(1): + type: copy + from: "${source::monitors.enclosure.collect.sources.source(2)}" + computes: + # Volts into mV + - type: multiply + column: 4 + value: 1000 + mapping: + # Value Table + source: "${source::monitors.voltage.collect.sources.source(1)}" + attributes: + id: $1 + metrics: + hw.voltage: $4 + hw.status{hw.type="voltage"}: $5 + legacyTextParameters: + StatusInformation: $6 + fan: + discovery: + sources: + source(1): + # ID;PhysicalDescription;Parent;Class;HardwareRev;FirmwareRev;SerialNumber;Manufacturer;Model;FRU;SensorID;type; + type: copy + from: "${source::monitors.enclosure.discovery.sources.source(3)}" + computes: + # Keep only Fan sensors + # ID;PhysicalDescription;Parent;Class;HardwareRev;FirmwareRev;SerialNumber;Manufacturer;Model;FRU;SensorID;type; + - type: keepOnlyMatchingLines + column: 12 + valueList: 10 + # Substract 11 from ID to get FRU Parent + # ID;PhysicalDescription;Parent;Class;HardwareRev;FirmwareRev;SerialNumber;Manufacturer;Model;FRU;SensorID;type; + - type: awk + script: "${file::embeddedFile-1}" + source(2): + # ID;PhysicalDescription;Parent;Class;HardwareRev;FirmwareRev;SerialNumber;Manufacturer;Model;FRU;SensorID;type;value; + type: tableJoin + leftTable: "${source::monitors.fan.discovery.sources.source(1)}" + rightTable: "${source::monitors.enclosure.discovery.sources.source(3)}" + leftKeyColumn: 1 + rightKeyColumn: 1 + computes: + - type: keepColumns + columnNumbers: "14,15,16,17,18,19,20,21,22,23,24,25" + - type: leftConcat + column: 9 + value: "Model: " + - type: leftConcat + column: 8 + value: "Manufacturer: " + - type: leftConcat + column: 7 + value: "Serial Number: " + mapping: + # Instance Table + source: "${source::monitors.fan.discovery.sources.source(2)}" + attributes: + id: $1 + __display_id: $2 + info: "${awk::join(\" \", $7, $8, $9)}" + hw.parent.type: enclosure + hw.parent.id: _DEVICE_ID + name: $2 + collect: + # Collect type = multi-instance + type: multiInstance + keys: + - id + sources: + source(1): + type: copy + from: "${source::monitors.enclosure.collect.sources.source(2)}" + computes: + # Substract 11 from ID to get FRU Parent + - type: awk + script: "${file::embeddedFile-1}" + mapping: + # ValueTable = Enclosure.Collect.Source(1) + source: "${source::monitors.fan.collect.sources.source(1)}" + attributes: + id: $1 + metrics: + hw.fan.speed: $4 + hw.status{hw.type="fan"}: $5 + legacyTextParameters: + StatusInformation: $6 +translations: + SensorStatusTranslationTable: + "1": ok + "2": degraded + default: UNKNOWN + "3": failed + "4": failed + "5": failed + "6": failed + SensorStatusInformationTranslationTable: + "1": "" + "2": Warning + default: Unknown Status + "3": Critical + "4": Shutdown + "5": No Longer Present + "6": Not Functioning + entPhySensorPrecisionTranslationTable: + "1": 10 + default: 1 + "2": 100 + "3": 1000 diff --git a/src/it/metricshub-connectors/src/main/connector/hardware/MIB2EntitySensor-header/embeddedFile-1 b/src/it/metricshub-connectors/src/main/connector/hardware/MIB2EntitySensor-header/embeddedFile-1 new file mode 100644 index 0000000..550bc4f --- /dev/null +++ b/src/it/metricshub-connectors/src/main/connector/hardware/MIB2EntitySensor-header/embeddedFile-1 @@ -0,0 +1,6 @@ +BEGIN { + FS=";" +} +{ + print $1-11";"$2";"$3";"$4";"$5";"$6";"$7";"$8";"$9";"$10";"$11";"$12";"$13";" +} \ No newline at end of file diff --git a/src/it/metricshub-connectors/src/main/connector/hardware/MIB2Linux/MIB2Linux.yaml b/src/it/metricshub-connectors/src/main/connector/hardware/MIB2Linux/MIB2Linux.yaml new file mode 100644 index 0000000..ab08c0b --- /dev/null +++ b/src/it/metricshub-connectors/src/main/connector/hardware/MIB2Linux/MIB2Linux.yaml @@ -0,0 +1,36 @@ +--- +extends: +- ../Hardware/Hardware +- ../MIB2-header/MIB2-header +connector: + displayName: MIB-2 Standard SNMP Agent - Network Interfaces - Linux + platforms: Any system with SNMP + detection: + appliesTo: + - Linux + supersedes: + - LinuxIPNetwork + - LinuxIfConfigNetwork +monitors: + network: + discovery: + sources: + source(1): + computes: + # Keep only eth[0-9] and vmnic[0-9] cards + # PortID;Description;PortType;MacAddress;AdminStatus; + - type: keepOnlyMatchingLines + column: 2 + regExp: "\\(^eth[0-9]+$\\)\\|\\(^vmnic[0-9]+$\\)\\|\\(^em[0-9]+$\\)\\|\\(^[Pp][0-9]+[Pp][0-9]+$\\)\\|\\(^en[ospx]\\)" + mapping: + # InstanceTable = Source(1) + # PortID;Description;PortType;MacAddress;AdminStatus; + source: "${source::monitors.network.discovery.sources.source(1)}" + attributes: + id: $1 + __display_id: $2 + physical_address: $4 + physical_address_type: MAC + device_type: $3 + hw.parent.type: enclosure + name: "${awk::sprintf(\"%s (%s)\", $2, $3)}" diff --git a/src/it/metricshub-connectors/src/main/connector/hardware/MIB2NT/MIB2NT.yaml b/src/it/metricshub-connectors/src/main/connector/hardware/MIB2NT/MIB2NT.yaml new file mode 100644 index 0000000..c7bef2a --- /dev/null +++ b/src/it/metricshub-connectors/src/main/connector/hardware/MIB2NT/MIB2NT.yaml @@ -0,0 +1,64 @@ +--- +extends: +- ../Hardware/Hardware +- ../MIB2-header/MIB2-header +connector: + displayName: MIB-2 Standard SNMP Agent - Network Interfaces - Windows + platforms: Any system with SNMP + detection: + appliesTo: + - NT + supersedes: + - WBEMGenNetwork +monitors: + network: + discovery: + sources: + source(1): + computes: + # Small trick: make a bitwise _and_ on the portID to remove the + # weird bits that the Windows SNMP agent puts in the network interface ID + # PortID;Description;PortType;MacAddress;AdminStatus; + - type: and + column: 1 + value: 1023 + # Exclude network cards that are not actual physical cards (while still declared as "Ethernet" stuff by the Windows SNMP stack, I tell you... ) + # 57 41 43 20 is "WAN " in HEX. 4D 69 63 72 6F 73 6F 66 74 is Microsoft. + # PortID;Description;PortType;MacAddress;AdminStatus; + - type: excludeMatchingLines + column: 2 + regExp: "\\(WAN\\)\\|\\(QoS\\)\\|\\([Ff]ilter\\)\\|\\([Ss]witch\\)\\|\\(57 41 4E 20\\)\\|\\(4D 69 63 72 6F 73 6F 66 74\\)" + # Keep only network cards that have a description + # PortID;Description;PortType;MacAddress;AdminStatus; + - type: keepOnlyMatchingLines + column: 2 + regExp: . + mapping: + # InstanceTable = Source(1) + # PortID;Description;PortType;MacAddress;AdminStatus;ID;Name;Alias; + source: "${source::monitors.network.discovery.sources.source(3)}" + attributes: + id: $1 + __display_id: $8 + vendor: $2 + physical_address: $4 + physical_address_type: MAC + device_type: $3 + hw.parent.type: enclosure + name: "${awk::sprintf(\"%s (%s - %s)\", $8, $3, $2)}" + collect: + sources: + source(1): + computes: + # Same as in the discovery, remove the weird high-level bits + # that mean nothing + # PortID;Description;SpeedMBps;PATROLStatus;StatusInformation;LinkStatus;ReceivedBytes;ReceivedPackets;ReceivedNonUnicastPackets;TotalErrors;TransmittedBytes;TransmittedPackets;TransmittedNonUnicastPackets;TransmittedErrors; + - type: and + column: 1 + value: 1023 + # Replace 4294967295 with zero + # PortID;Description;SpeedMBps;PATROLStatus;StatusInformation;LinkStatus;ReceivedBytes;ReceivedPackets;ReceivedNonUnicastPackets;TotalErrors;TransmittedBytes;TransmittedPackets;TransmittedNonUnicastPackets;TransmittedErrors; + - type: replace + column: 3 + existingValue: 4294967295 + newValue: 0 diff --git a/src/it/metricshub-connectors/src/main/connector/hardware/MIB2Solaris/MIB2Solaris.yaml b/src/it/metricshub-connectors/src/main/connector/hardware/MIB2Solaris/MIB2Solaris.yaml new file mode 100644 index 0000000..37947e9 --- /dev/null +++ b/src/it/metricshub-connectors/src/main/connector/hardware/MIB2Solaris/MIB2Solaris.yaml @@ -0,0 +1,55 @@ +--- +extends: +- ../Hardware/Hardware +- ../MIB2-header/MIB2-header +- ../SunEthernetDriver-header/SunEthernetDriver-header +connector: + displayName: MIB-2 Standard SNMP Agent - Network Interfaces - Solaris + platforms: Any system with SNMP + detection: + appliesTo: + - Solaris + supersedes: + - SunNetwork +monitors: + network: + discovery: + sources: + source(1): + computes: + # Process that through an AWK script to use Description as the device ID + # and isolate the driver name, so we can translate that into a card model + # Also keep only cards where the first word is a purely alphanumeric driverInstance name + # DeviceID;DriverName;PortType;MacAddress;AdminStatus; + - type: awk + script: "${file::embeddedFile-1}" + keep: ^MSHW; + separators: ; + selectColumns: "2,3,4,5,6" + # Translate the Driver column into a model name + # DeviceID;Model;PortType;MacAddress;AdminStatus; + - type: translate + column: 2 + translationTable: "${translation::DriverTranslationTable}" + mapping: + # InstanceTable = Source(1) + # PortID;Description;PortType;MacAddress;AdminStatus; + source: "${source::monitors.network.discovery.sources.source(1)}" + attributes: + id: $1 + vendor: $2 + physical_address: $4 + physical_address_type: MAC + device_type: $3 + hw.parent.type: enclosure + name: "${awk::sprintf(\"%s (%s - %s)\", $1, $3, $2)}" + collect: + sources: + source(6): + computes: + # Same as in the discovery, use Description as the DeviceID + # PortID;Description;SpeedMBps;PATROLStatus;StatusInformation;LinkStatus;ReceivedBytes;ReceivedPackets;ReceivedNonUnicastPackets;TotalErrors;TransmittedBytes;TransmittedPackets;TransmittedNonUnicastPackets;TransmittedErrors; + - type: replace + column: 1 + existingValue: $1 + newValue: $2 diff --git a/src/it/metricshub-connectors/src/main/connector/hardware/MIB2Solaris/embeddedFile-1 b/src/it/metricshub-connectors/src/main/connector/hardware/MIB2Solaris/embeddedFile-1 new file mode 100644 index 0000000..74395b1 --- /dev/null +++ b/src/it/metricshub-connectors/src/main/connector/hardware/MIB2Solaris/embeddedFile-1 @@ -0,0 +1,16 @@ +BEGIN { + FS = ";" +} +($2 ~ /^[a-zA-Z0-9]+$/) { + DeviceID = $2 + driver = DeviceID + while (substr(driver, length(driver), 1) ~ /[0-9]/) + { + driver = substr(driver, 1, length(driver) - 1) + } + + if (driver != "lo" && driver != "lpfc" && driver != "jnet" && driver != "dman" && driver != "aggr" && driver != "clprivnet" && driver != "sppp") + { + print "MSHW;" DeviceID ";" driver ";" $3 ";" $4 ";" $5 + } +} \ No newline at end of file diff --git a/src/it/metricshub-connectors/src/main/connector/hardware/MIB2Switch/MIB2Switch.yaml b/src/it/metricshub-connectors/src/main/connector/hardware/MIB2Switch/MIB2Switch.yaml new file mode 100644 index 0000000..1ef4470 --- /dev/null +++ b/src/it/metricshub-connectors/src/main/connector/hardware/MIB2Switch/MIB2Switch.yaml @@ -0,0 +1,57 @@ +--- +extends: +- ../Hardware/Hardware +- ../MIB2-header/MIB2-header +- ../MIB2EntitySensor-header/MIB2EntitySensor-header +constants: + _ENCLOSURE_TYPE: Switch + _DETECTION_INDEX: 2 + _DEVICE_ID: EthernetSwitch +connector: + displayName: Ethernet Switch with Sensors (SNMP) + platforms: MIB-2 and Entity-Sensor-MIB Supported Switch + reliesOn: "MIB-2, Entity-Sensor-MIB, SNMP" + version: 1.0 + information: This connector provides an enclosure and sensors for Ethernet switches that provide it. + detection: + connectionTypes: + - remote + - local + appliesTo: + - Network + supersedes: + - GenericSwitchEnclosure +monitors: + network: + discovery: + sources: + source(4): + # Source(4) = ifTable SNMP Table + # PortID;OperationalStatus + type: snmpTable + oid: 1.3.6.1.2.1.2.2.1 + selectColumns: "ID,8" + computes: + # Exclude OperationalStatus 6 = Component Not Present + - type: excludeMatchingLines + column: 2 + valueList: 6 + source(5): + # PortID;OperationalStatus;PortID;Description;TypeCode;MacAddress;AdminStatus;ID;Name;Alias; + type: tableJoin + leftTable: "${source::monitors.network.discovery.sources.source(4)}" + rightTable: "${source::monitors.network.discovery.sources.source(3)}" + leftKeyColumn: 1 + rightKeyColumn: 1 + mapping: + # PortID;OperationalStatus;PortID;Description;TypeCode;MacAddress;AdminStatus;ID;Name;Alias; + source: "${source::monitors.network.discovery.sources.source(5)}" + attributes: + id: $1 + __display_id: $9 + physical_address: $6 + physical_address_type: MAC + device_type: $5 + hw.parent.type: enclosure + hw.parent.id: _DEVICE_ID + name: "${awk::sprintf(\"%s (%s)\", $9, $5)}" diff --git a/src/it/metricshub-connectors/src/main/connector/hardware/NvidiaSmi/NvidiaSmi.yaml b/src/it/metricshub-connectors/src/main/connector/hardware/NvidiaSmi/NvidiaSmi.yaml new file mode 100644 index 0000000..ad22673 --- /dev/null +++ b/src/it/metricshub-connectors/src/main/connector/hardware/NvidiaSmi/NvidiaSmi.yaml @@ -0,0 +1,209 @@ +--- +extends: +- ../Hardware/Hardware +connector: + displayName: Nvidia-Smi + platforms: Any system with Nvidia GPUs + reliesOn: NVIDIA drivers with NVIDIA-SMI support. + information: Gives hardware information on most Nvidia GPUs. (Clocking) + detection: + connectionTypes: + - remote + - local + appliesTo: + - NT + - Linux + criteria: + - type: osCommand + commandLine: nvidia-smi + expectedResult: Driver Version + errorMessage: nvidia-smi not found at target host. +monitors: + enclosure: + discovery: + sources: + source(1): + type: osCommand + commandLine: nvidia-smi -q + timeout: 90 + computes: + - type: awk + script: "${file::embeddedFile-1}" + mapping: + source: "${source::monitors.enclosure.discovery.sources.source(1)}" + gpu: + discovery: + sources: + source(1): + type: copy + from: "${source::monitors.enclosure.discovery.sources.source(1)}" + computes: + - type: keepOnlyMatchingLines + column: 1 + regExp: ^MSHW_GPU + mapping: + source: "${source::monitors.gpu.discovery.sources.source(1)}" + attributes: + vendor: NVIDIA + id: $2 + __display_id: $2 + model: $14 + serial_number: $15 + driver_version: $17 + firmware_version: $19 + info: "${awk::join(\" \", $16, $18)}" + hw.parent.type: enclosure + name: "${awk::sprintf(\"%s (%s - %s - %s)\", $2, \"NVIDIA\", $14, mebiBytes2HumanFormat($20))}" + metrics: + hw.gpu.memory.limit: mebiByte2Byte($20) + collect: + type: multiInstance + keys: + - id + sources: + source(1): + type: osCommand + commandLine: nvidia-smi -q + timeout: 90 + computes: + - type: awk + script: "${file::embeddedFile-1}" + - type: keepOnlyMatchingLines + column: 1 + regExp: ^MSHW_GPU + mapping: + source: "${source::monitors.gpu.collect.sources.source(1)}" + attributes: + id: $2 + metrics: + hw.gpu.utilization{task="general"}: percent2Ratio($5) + hw.gpu.memory.utilization: percent2Ratio($6) + hw.gpu.utilization{task="encoder"}: percent2Ratio($7) + hw.gpu.utilization{task="decoder"}: percent2Ratio($8) + hw.gpu.io{direction="transmit"}: fakeCounter($3) + hw.gpu.io{direction="receive"}: fakeCounter($4) + hw.power{hw.type="gpu"}: $9 + hw.energy{hw.type="gpu"}: fakeCounter($9) + temperature: + discovery: + sources: + source(1): + type: copy + from: "${source::monitors.enclosure.discovery.sources.source(1)}" + computes: + - type: keepOnlyMatchingLines + column: 1 + regExp: ^MSHW_TEMP + mapping: + source: "${source::monitors.temperature.discovery.sources.source(1)}" + attributes: + id: $2 + __display_id: $2 + sensor_location: gpu + hw.parent.type: enclosure + name: "${awk::sprintf(\"%s (%s)\", $2, \"gpu\")}" + metrics: + hw.temperature.limit{limit_type="high.degraded"}: $4 + hw.temperature.limit{limit_type="high.critical"}: $5 + collect: + type: multiInstance + keys: + - id + sources: + source(1): + type: osCommand + commandLine: nvidia-smi -q + timeout: 90 + computes: + - type: awk + script: "${file::embeddedFile-1}" + - type: keepOnlyMatchingLines + column: 1 + regExp: ^MSHW_TEMP + mapping: + source: "${source::monitors.temperature.collect.sources.source(1)}" + attributes: + id: $2 + metrics: + hw.temperature: $3 + voltage: + discovery: + sources: + source(1): + type: copy + from: "${source::monitors.enclosure.discovery.sources.source(1)}" + computes: + - type: keepOnlyMatchingLines + column: 3 + regExp: . + mapping: + source: "${source::monitors.voltage.discovery.sources.source(1)}" + attributes: + id: $2 + __display_id: $2 + sensor_location: gpu + hw.parent.type: enclosure + name: "${awk::sprintf(\"%s (%s)\", $2, \"gpu\")}" + collect: + type: multiInstance + keys: + - id + sources: + source(1): + type: osCommand + commandLine: nvidia-smi -q + timeout: 90 + computes: + - type: awk + script: "${file::embeddedFile-1}" + - type: keepOnlyMatchingLines + column: 1 + regExp: ^MSHW_VOLTAGE + mapping: + source: "${source::monitors.voltage.collect.sources.source(1)}" + attributes: + id: $2 + metrics: + hw.voltage: $3 + fan: + discovery: + sources: + source(1): + type: copy + from: "${source::monitors.enclosure.discovery.sources.source(1)}" + computes: + - type: keepOnlyMatchingLines + column: 1 + regExp: ^MSHW_FAN + mapping: + source: "${source::monitors.fan.discovery.sources.source(1)}" + attributes: + id: $2 + __display_id: $2 + sensor_location: gpu + hw.parent.type: enclosure + name: "${awk::sprintf(\"%s (%s)\", $2, \"gpu\")}" + metrics: + hw.fan.speed_ratio.limit{limit_type="low.degraded"}: percent2Ratio("10") + hw.fan.speed_ratio.limit{limit_type="low.critical"}: percent2Ratio("0") + collect: + type: multiInstance + keys: + - id + sources: + source(1): + type: osCommand + commandLine: nvidia-smi -q + timeout: 90 + computes: + - type: awk + script: "${file::embeddedFile-1}" + - type: keepOnlyMatchingLines + column: 1 + regExp: ^MSHW_FAN + mapping: + source: "${source::monitors.fan.collect.sources.source(1)}" + attributes: + id: $2 + metrics: + hw.fan.speed_ratio: percent2Ratio($3) diff --git a/src/it/metricshub-connectors/src/main/connector/hardware/NvidiaSmi/embeddedFile-1 b/src/it/metricshub-connectors/src/main/connector/hardware/NvidiaSmi/embeddedFile-1 new file mode 100644 index 0000000..9ef72ed --- /dev/null +++ b/src/it/metricshub-connectors/src/main/connector/hardware/NvidiaSmi/embeddedFile-1 @@ -0,0 +1,64 @@ +BEGIN { FS = ": "; gpuID=""; model = ""; serialNumber = ""; gpuUuid = ""; vBios = ""; driverVersion = ""; cudaVersion = ""; firmwareVersion = ""; transferredBytes = ""; receivedBytes =""; gpuUtilization =""; memoryUtilization =""; encoderUtilization =""; decoderUtilization =""; fanSpeed =""; voltage =""; temperature =""; warnTemperature =""; critTemperature =""; powerConsumption =""; minPowerLimit =""; maxPowerLimit =""; correctable=""; uncorrectable="";totalMemory=""} + +#Discovery related values and informative fields. + +/GPU 0/ {split($0,outputarray,":"); gpuID=outputarray[2];} +/Product Name/ { model = $2; } +/Serial Number/ && /[0-9]/ { serialNumber = $2; } +/GPU UUID/ { gpuUuid = "GPU UUID: " $2; } +/VBIOS Version/ { vBios = " VBIOS Version: " $2; } +/Driver Version/ { driverVersion = $2; } +/CUDA Version/ { cudaVersion = "CUDA Version: " $2; } +/Firmware Version/ && /[0-9]/ { firmwareVersion = $2;} +/FB Memory Usage/ { getline; totalMemory = $2; gsub(/ MiB/,"",totalMemory)} + +#Collection related values and removal of units of measurement. + +/Tx Throughput/ && /[0-9]/ { transferredBytes = $2; gsub(/ KB\/s/,"",transferredBytes); } +/Rx Throughput/ && /[0-9]/ { receivedBytes = $2; gsub(/ KB\/s/,"",receivedBytes); } +/Gpu/ && /%/ { gpuUtilization = $2; gsub(/ %/,"",gpuUtilization); } +/Memory/ && /%/ { memoryUtilization = $2; gsub(/ %/,"",memoryUtilization); } +/Encoder/ && /%/ { encoderUtilization = $2; gsub(/ %/,"",encoderUtilization); } +/Decoder/ && /%/ { decoderUtilization = $2; gsub(/ %/,"",decoderUtilization); } + +#We will now extract and do the sum of the ecc errors. +#/Correctable/ && /[0-9]/ { correctable+=$2; } +#/Uncorrectable/ && /[0-9]/ { uncorrectable+=$2; } + +#We will now extract the power consumption. + +/Power Draw/ && /[0-9]/ { powerConsumption = $2; gsub(/ W/,"",powerConsumption); } +/Min Power Limit/ && /[0-9]/ { minPowerLimit = $2; gsub(/ W/,"",minPowerLimit); } +/Max Power Limit/ && /[0-9]/ { maxPowerLimit = $2; gsub(/ W/,"",maxPowerLimit);} + +#TBD IF REQUIRED. +#Parsing through the file to gather the clock speeds. As there are several iterations of these strings, we had to isolate them. + +#/^ Clocks$/ { for(i=1; i<=4; i++); +# IF (/Graphics/) getline; graphicsClock = $2; gsub(/ MHz/,"",graphicsClock); +# IF (/SM/) getline; smClock = $2; gsub(/ MHz/,"",smClock); +# IF (/Memory/) getline; memoryClock = $2; gsub(/ MHz/,"",memoryClock); +# IF (/Video/) getline; videoClock = $2; gsub(/ MHz/,"",videoClock); } + +#Collection of fan class values and removal of units of measurement. + +/Fan Speed/ && /[0-9]/ { fanSpeed =$2; gsub(/ %/,"",fanSpeed);} + +#Collection of voltage class values and removal of units of measurement. +/Voltage/ {getline; if (/[0-9]/) voltage = $2; gsub(/ mV/,"",voltage)} + +#We will now extract the temperature along with its thresholds. +/GPU Current Temp/ && /[0-9]/ { temperature = $2; gsub(/ C/,"",temperature); } +/GPU Target Temperature/ && /[0-9]/ { warnTemperature = $2; gsub(/ C/,"",warnTemperature); } +/GPU Slowdown Temp/ && /[0-9]/ { critTemperature = $2; gsub(/ C/,"",critTemperature);} + + +#We will now print all the relevant information, separated by classes. Processes being the last category, this will allow us to print these at the end of each card. +#We then reset all variables to empty values for the next card. + +/Processes/ { +print "MSHW_GPU;" "GPU" gpuID ";" transferredBytes * 1024 ";" receivedBytes * 1024 ";" gpuUtilization ";" memoryUtilization ";" encoderUtilization ";" decoderUtilization ";"powerConsumption ";" minPowerLimit ";" maxPowerLimit ";" correctable ";" uncorrectable ";" model ";" serialNumber ";" gpuUuid vBios ";" driverVersion ";" cudaVersion ";" firmwareVersion ";" totalMemory; +print "MSHW_TEMP;" "GPU" gpuID ";" temperature ";" warnTemperature ";" critTemperature; +print "MSHW_VOLTAGE;" "GPU" gpuID ";" voltage; +print "MSHW_FAN;" "GPU" gpuID ";" fanSpeed ; +gpuID=""; model = ""; serialNumber = ""; gpuUuid = ""; vBios = ""; driverVersion = ""; cudaVersion = ""; firmwareVersion = ""; transferredBytes = ""; receivedBytes =""; gpuUtilization =""; memoryUtilization =""; encoderUtilization =""; decoderUtilization =""; fanSpeed =""; voltage =""; temperature =""; warnTemperature =""; critTemperature =""; powerConsumption =""; minPowerLimit =""; maxPowerLimit =""; correctable=""; uncorrectable="";totalMemory=""} diff --git a/src/it/metricshub-connectors/src/main/connector/hardware/SunEthernetDriver-header/SunEthernetDriver-header.yaml b/src/it/metricshub-connectors/src/main/connector/hardware/SunEthernetDriver-header/SunEthernetDriver-header.yaml new file mode 100644 index 0000000..9604255 --- /dev/null +++ b/src/it/metricshub-connectors/src/main/connector/hardware/SunEthernetDriver-header/SunEthernetDriver-header.yaml @@ -0,0 +1,31 @@ +--- +translations: + DriverTranslationTable: + bge: Broadcom BCM57xx Gigabit Ethernet + e1000g: Intel PRO/1000 Gigabit Ethernet + hme: Fast Ethernet + pcelx: 3Com Etherlink III PCMCIA Ethernet + fjgi: Fujitsu Quad Gigabit Ethernet + nge: nVidia Gigabit Ethernet + spwr: SMC EtherPower II 10/100 (9432) Ethernet + rge: Realtek Gigabit Ethernet + vge: Gigabit Ethernet + dnet: DEC2104x Ethernet + xge: Neterion Xframe 10Gigabit Ethernet + pcn: AMD Pcnet Ethernet + nxge: Sun 10/1 Gigabit Ethernet + eri: Fast Ethernet + ge: GEM Gigabit Ethernet + ce: Cassini Gigabit Ethernet + dmfe: Davicom Fast Ethernet DM9102A + amd8111s: AMD-8111 Fast Ethernet + bnx: Broadcom NetXtreme II Gigabit Ethernet + Default: "" + qfe: Sun Quad Fast Ethernet + elxl: 3Com Ethernet + ipge: Intel 82571 PCI-E Gigabit Ethernet + chxge: Chelsio Ethernet + bcme: Broadcom BCM57xx Gigabit Ethernet + sk98sol: SysKonnect SK-98xx Gigabit Ethernet + ixgb: Intel 82597ex 10 Gigabit Ethernet + rtls: Realtek 8139 Fast Ethernet diff --git a/src/it/metricshub-connectors/src/main/connector/hardware/VMwareESX4i/VMwareESX4i.yaml b/src/it/metricshub-connectors/src/main/connector/hardware/VMwareESX4i/VMwareESX4i.yaml new file mode 100644 index 0000000..34099f2 --- /dev/null +++ b/src/it/metricshub-connectors/src/main/connector/hardware/VMwareESX4i/VMwareESX4i.yaml @@ -0,0 +1,26 @@ +--- +extends: +- ../Hardware/Hardware +- ../VMwareESXi-header/VMwareESXi-header +connector: + displayName: VMware ESXi 4.x + platforms: VMware ESXi + reliesOn: WBEM + version: 1.1 + information: "This connector provides hardware monitoring through the VMware ESXi implementation of SMASH, through the WBEM protocol." + detection: + connectionTypes: + - remote + - local + appliesTo: + - OOB + - Linux + supersedes: + - VMwareESXi + - IpmiTool + criteria: + # Checking the version of ESXi + - type: wbem + namespace: root/cimv2 + query: SELECT MajorVersion FROM VMware_HypervisorSoftwareIdentity + expectedResult: "^[1-9][0-9]\\|[4-9]" diff --git a/src/it/metricshub-connectors/src/main/connector/hardware/VMwareESX5iLUN-header/VMwareESX5iLUN-header.yaml b/src/it/metricshub-connectors/src/main/connector/hardware/VMwareESX5iLUN-header/VMwareESX5iLUN-header.yaml new file mode 100644 index 0000000..372d196 --- /dev/null +++ b/src/it/metricshub-connectors/src/main/connector/hardware/VMwareESX5iLUN-header/VMwareESX5iLUN-header.yaml @@ -0,0 +1,138 @@ +--- +connector: + detection: + criteria: + - type: osCommand + commandLine: _EsxcliConstant system version get + expectedResult: "Version: [567]" + executeLocally: _ExecuteLocally + # Check that there are FC LUNs + - type: osCommand + commandLine: _EsxcliConstant storage core path list + expectedResult: "Transport: fc" + executeLocally: _ExecuteLocally +monitors: + lun: + discovery: + sources: + source(1): + # Get the list of LUNs from esxcli storage core path list + type: osCommand + commandLine: _EsxcliConstant storage core path list + executeLocally: _ExecuteLocally + computes: + # AWK it + # MSHW;LunName;LunInfo;NumberPaths;LunStatusInfo; + # LunName;LunInfo; + - type: awk + script: "${file::embeddedFile-1}" + keep: ^MSHW; + separators: ; + selectColumns: "2,3" + mapping: + # InstanceTable = Source(1) + # LunName;LunInfo; + source: "${source::monitors.lun.discovery.sources.source(1)}" + attributes: + id: $1 + __display_id: $1 + array_name: $2 + hw.parent.type: enclosure + name: $2 + collect: + # Collect type is multi-instance + type: multiInstance + keys: + - id + sources: + source(1): + # Get the list of LUNs from esxcli storage core path list + type: osCommand + commandLine: _EsxcliConstant storage core path list + executeLocally: _ExecuteLocally + computes: + # AWK it + # MSHW;LunName;LunInfo;NumberPaths;LunStatusInfo; + # LunName;NumberPaths;LunStatusInfo; + - type: awk + script: "${file::embeddedFile-1}" + keep: ^MSHW; + separators: ; + selectColumns: "2,4,5" + mapping: + # ValueTable = Source(1) + # LunName;NumberPaths;LunStatusInfo; + source: "${source::monitors.lun.collect.sources.source(1)}" + attributes: + id: $1 + metrics: + hw.lun.paths: $2 + legacyTextParameters: + AvailablePathInformation: $3 + other_device: + discovery: + sources: + source(1): + # Get the list of SD Cards from esxcli storage core device list + type: osCommand + commandLine: _EsxcliConstant storage core device list + executeLocally: _ExecuteLocally + computes: + # AWK it + # Type;USBID;Vendor;Model; + - type: awk + script: "${file::embeddedFile-2}" + keep: ^MSHW; + separators: ; + selectColumns: "2,3,4,5" + mapping: + # InstanceTable = Source(1) + # Type;USBID;Vendor;Model; + source: "${source::monitors.other_device.discovery.sources.source(1)}" + attributes: + id: $2 + __display_id: $2 + device_type: $1 + info: "${awk::join(\" \", $3, $4)}" + hw.parent.type: enclosure + name: "${awk::sprintf(\"%s: %s\", $1, $2)}" + collect: + # Collect type is multi-instance + type: multiInstance + keys: + - id + sources: + source(1): + # Get the list of SD Cards from esxcli storage core device list + type: osCommand + commandLine: _EsxcliConstant storage core device list + executeLocally: _ExecuteLocally + computes: + # AWK it + # USBID;Status;StatusInformation; + - type: awk + script: "${file::embeddedFile-2}" + keep: ^MSHW; + separators: ; + selectColumns: "3,6,7" + # Patrol Status comes from Status + # USBID;PatrolStatus;StatusInformation; + - type: translate + column: 2 + translationTable: "${translation::statusTranslationTable}" + mapping: + # ValueTable = Source(1) + # USBID;Status;StatusInformation; + source: "${source::monitors.other_device.collect.sources.source(1)}" + attributes: + id: $1 + metrics: + hw.status{hw.type="other_device"}: $2 + legacyTextParameters: + StatusInformation: $3 +translations: + statusTranslationTable: + dead: failed + Default: degraded + "off": ok + "on": ok diff --git a/src/it/metricshub-connectors/src/main/connector/hardware/VMwareESX5iLUN-header/embeddedFile-1 b/src/it/metricshub-connectors/src/main/connector/hardware/VMwareESX5iLUN-header/embeddedFile-1 new file mode 100644 index 0000000..078b886 --- /dev/null +++ b/src/it/metricshub-connectors/src/main/connector/hardware/VMwareESX5iLUN-header/embeddedFile-1 @@ -0,0 +1,27 @@ +$1 ~ /UID:/ {LunID = ""} + +$1 ~ /Runtime/ && $2 ~ /Name:/ {PathNameTempVar = $3} + +$1 ~ /Device:/ && $2 ~ /^naa/ { LunID= $2 + PathName[LunID] = PathNameTempVar + } + +$1 ~ /Device:/ && $2 ~ /^eui/ { LunID= $2 + PathName[LunID] = PathNameTempVar + } + + +$1 ~ /Device/ && $2 ~ /Display/ && $3 ~ /Name:/ && LunID != "" { LunName[LunID] = $0 ; + gsub(/\(.+\)/,"",LunName[LunID]) ; + gsub(" *Device Display Name: *","",LunName[LunID]) + } + +$1 ~ /State:/ { if ($2 ~ /active/) {PathCount[LunID] = PathCount[LunID] + 1} + StatusInformation[LunID] = StatusInformation[LunID] PathName[LunID] "=" $2 " " + } +$1 ~ /Transport:/ {Transport[LunID]=$2} + +END { for (LunID in LunName) { if (PathCount[LunID] == "") {PathCount[LunID] = 0} + if (Transport[LunID]=="fc") {print "MSHW;" LunID ";" LunName[LunID] ";" PathCount[LunID] ";" StatusInformation[LunID]} + } + } \ No newline at end of file diff --git a/src/it/metricshub-connectors/src/main/connector/hardware/VMwareESX5iLUN-header/embeddedFile-2 b/src/it/metricshub-connectors/src/main/connector/hardware/VMwareESX5iLUN-header/embeddedFile-2 new file mode 100644 index 0000000..e19f604 --- /dev/null +++ b/src/it/metricshub-connectors/src/main/connector/hardware/VMwareESX5iLUN-header/embeddedFile-2 @@ -0,0 +1,15 @@ + $1 ~ /Display/ && $2 ~ /Name:/ { Type = $0 ; + gsub(/\(.+\)/,"",Type) ; + gsub(" *Display Name: *","",Type) + USBID = $0 + gsub(/.*\(/,"",USBID) ; + gsub(/\).*/,"",USBID) ; + } + +$1 ~ /Vendor:/ { Vendor = $0 ; gsub(/.*: /,"",Vendor) } +$1 ~ /Model:/ { Model = $0 ; gsub(/.*: /,"",Model) } +$1 ~ /Status:/ { + Status = $2; + StatusInformation = $0 ; gsub(/.*: /,"",StatusInformation) + } +$0 ~ /Is Boot USB Device: true/ { print "MSHW;" Type ";" USBID ";Vendor: " Vendor ";Model: " Model";" Status ";" StatusInformation ";" } \ No newline at end of file diff --git a/src/it/metricshub-connectors/src/main/connector/hardware/VMwareESX5iLUNSSH/VMwareESX5iLUNSSH.yaml b/src/it/metricshub-connectors/src/main/connector/hardware/VMwareESX5iLUNSSH/VMwareESX5iLUNSSH.yaml new file mode 100644 index 0000000..78914d5 --- /dev/null +++ b/src/it/metricshub-connectors/src/main/connector/hardware/VMwareESX5iLUNSSH/VMwareESX5iLUNSSH.yaml @@ -0,0 +1,20 @@ +--- +extends: +- ../Hardware/Hardware +- ../VMwareESX5iLUN-header/VMwareESX5iLUN-header +constants: + _ExecuteLocally: 0 + _EsxcliConstant: esxcli +connector: + displayName: VMware ESXi 5 LUN (SSH) + platforms: VMWare ESXi + reliesOn: ESX SSH interface + version: 1.0 + information: This connector discovers the LUNs of an ESX5i server. It requires SSH access to the ESX host. + detection: + connectionTypes: + - remote + - local + appliesTo: + - Linux + - OOB diff --git a/src/it/metricshub-connectors/src/main/connector/hardware/VMwareESX5iLUNesxcli/VMwareESX5iLUNesxcli.yaml b/src/it/metricshub-connectors/src/main/connector/hardware/VMwareESX5iLUNesxcli/VMwareESX5iLUNesxcli.yaml new file mode 100644 index 0000000..f1ac895 --- /dev/null +++ b/src/it/metricshub-connectors/src/main/connector/hardware/VMwareESX5iLUNesxcli/VMwareESX5iLUNesxcli.yaml @@ -0,0 +1,23 @@ +--- +extends: +- ../Hardware/Hardware +- ../VMwareESX5iLUN-header/VMwareESX5iLUN-header +constants: + _ExecuteLocally: 1 + _EsxcliConstant: "esxcli -s %{SUDO:vcenter} %{HOSTNAME} -u %{USERNAME} -p %{PASSWORD}" +connector: + detection: + supersedes: + - VMwareESX5iLUNSSH + connectionTypes: + - remote + appliesTo: + - Linux + - OOB + displayName: VMware ESXi 5 LUN (esxcli) + platforms: VMware ESXi + reliesOn: ESX excli utility (installed locally on the agent host) + version: 1.0 + information: This connector discovers the LUNs of an VMware ESX5i server. It requires HTTPS access to the ESX host. +sudoCommands: +- vcenter diff --git a/src/it/metricshub-connectors/src/main/connector/hardware/VMwareESXVMSNMP/VMwareESXVMSNMP.yaml b/src/it/metricshub-connectors/src/main/connector/hardware/VMwareESXVMSNMP/VMwareESXVMSNMP.yaml new file mode 100644 index 0000000..ed591bb --- /dev/null +++ b/src/it/metricshub-connectors/src/main/connector/hardware/VMwareESXVMSNMP/VMwareESXVMSNMP.yaml @@ -0,0 +1,71 @@ +--- +extends: +- ../Hardware/Hardware +connector: + displayName: VMware ESXi - Virtual Machines (SNMP) + platforms: VMware ESXi + reliesOn: SNMP + information: This connector provides Virtual Machine monitoring (Power consumption and State) through VMWare SNMP agent. + detection: + connectionTypes: + - remote + appliesTo: + - OOB + - Linux + criteria: + # Hardware Sentry v11.3.00+ + - type: productRequirements + kmVersion: 11.3.00 + # Checking if we have VM + - type: snmpGetNext + oid: 1.3.6.1.4.1.6876.2.1.1 +monitors: + vm: + discovery: + sources: + source(1): + # discover the VM + # ID, DisplayName,Powershare + type: snmpTable + oid: 1.3.6.1.4.1.6876.2.1.1 + selectColumns: "ID,2,9" + mapping: + # The Instance table + source: "${source::monitors.vm.discovery.sources.source(1)}" + attributes: + id: $1 + __display_id: $2 + hw.parent.type: enclosure + name: $2 + conditionalCollection: + hw.power{hw.type="vm"}: $3 + collect: + # Collect type is: multi-instance + type: multiInstance + keys: + - id + sources: + source(1): + # ID, Vcpu (for powershare),Power state + type: snmpTable + oid: 1.3.6.1.4.1.6876.2.1.1 + selectColumns: "ID,9,6" + computes: + # remove the powered word from the power state so we can have on off suspended without mapping table + # / ID, Vcpu (for powershare),Power state + - type: translate + column: 3 + translationTable: "${translation::PowerStateTranslationTable}" + mapping: + # / ID, Vcpu (for powershare),Power state + source: "${source::monitors.vm.collect.sources.source(1)}" + attributes: + id: $1 + metrics: + hw.vm.power_ratio: computePowerShareRatio($2) + hw.power_state{hw.type="vm"}: $3 +translations: + PowerStateTranslationTable: + "powered off": "off" + "suspended": "suspended" + "powered on": "on" diff --git a/src/it/metricshub-connectors/src/main/connector/hardware/VMwareESXi-header/VMwareESXi-header.yaml b/src/it/metricshub-connectors/src/main/connector/hardware/VMwareESXi-header/VMwareESXi-header.yaml new file mode 100644 index 0000000..ba2e21d --- /dev/null +++ b/src/it/metricshub-connectors/src/main/connector/hardware/VMwareESXi-header/VMwareESXi-header.yaml @@ -0,0 +1,1362 @@ +--- +monitors: + enclosure: + discovery: + sources: + source(1): + # Source(1) CIM_ComputerSystem class + type: wbem + namespace: root/cimv2 + query: "SELECT Manufacturer,Model,SerialNumber FROM CIM_Chassis" + computes: + # Add a Fake ID to our table so we can join it with Source(2) + - type: leftConcat + column: 1 + value: MSHW_TableID; + source(2): + # Source(2) CIM_Chassis to get the manufacturer + type: wbem + namespace: root/cimv2 + query: "SELECT BaseUnits,CurrentReading FROM CIM_NumericSensor" + computes: + # Keep only lines with units of type 7 (Watts) + # ..hoping that there is only one power sensor per server. + - type: keepOnlyMatchingLines + column: 1 + valueList: 7 + # Add a Fake ID to our table so we can join it with Source(1) + - type: leftConcat + column: 1 + value: MSHW_TableID; + source(3): + # Joint the tables to have + # MSHW_TableID;Manufacturer;Model;SerialNumber;MSHW_TableID;BaseUnits;PowerConsumption; + type: tableJoin + leftTable: "${source::monitors.enclosure.discovery.sources.source(1)}" + rightTable: "${source::monitors.enclosure.discovery.sources.source(2)}" + leftKeyColumn: 1 + rightKeyColumn: 1 + defaultRightLine: ;;; + source(4): + # VMWare sometimes returns more than one, often duplicate, enclosures Will Table Joint to ensure only one is returned. This procedure is only necessary for the PM. + # MSHW_TableID;Manufacturer;Model;SerialNumber;MSHW_TableID;BaseUnits;PowerConsumption;UniqueID; + type: tableJoin + leftTable: MSHW_TableID; + rightTable: "${source::monitors.enclosure.discovery.sources.source(3)}" + leftKeyColumn: 1 + rightKeyColumn: 1 + source(5): + # Make a copy of the discovery in the enclosure section + type: copy + from: "${source::monitors.memory.discovery.sources.source(6)}" + mapping: + # InstanceTable = Source(1) + # MSHW_TableID;MSHW_TableID;Manufacturer;Model;SerialNumber;MSHW_TableID;BaseUnits;PowerConsumption; + source: "${source::monitors.enclosure.discovery.sources.source(4)}" + attributes: + id: ESX4iServer + model: $4 + serial_number: $5 + vendor: $3 + type: Computer + name: "${awk::sprintf(\"Computer: (%s %s)\", $3, $4)}" + conditionalCollection: + hw.enclosure.power: $8 + collect: + type: multiInstance + keys: + - id + sources: + source(1): + # + type: wbem + namespace: root/cimv2 + query: "SELECT BaseUnits,CurrentReading FROM CIM_NumericSensor" + computes: + # Keep only lines with units of type 7 (Watts) + # ..hoping that there is only one power sensor per server. + - type: keepOnlyMatchingLines + column: 1 + valueList: 7 + # Divide by 100 + - type: divide + column: 2 + value: 100 + mapping: + source: "${source::monitors.enclosure.collect.sources.source(1)}" + attributes: + id: ESX4iServer + metrics: + hw.enclosure.power: $2 + hw.enclosure.energy: fakeCounter($2) + cpu: + discovery: + sources: + source(1): + # Caption,CurrentClockSpeed,HealthState,ModelName,DeviceID + type: wbem + query: "SELECT Caption,CurrentClockSpeed,HealthState,ModelName,DeviceID FROM CIM_Processor" + namespace: root/cimv2 + computes: + # Keep only lines with a valid HealthState + # Caption,CurrentClockSpeed,HealthState,ModelName,DeviceID + - type: keepOnlyMatchingLines + column: 3 + valueList: "5,15,20,25,30" + mapping: + # InstanceTable = Source(1) + # Caption,CurrentClockSpeed,HealthState,ModelName,DeviceID + source: "${source::monitors.cpu.discovery.sources.source(1)}" + attributes: + id: $5 + __display_id: $1 + model: $4 + hw.parent.type: enclosure + name: "${awk::sprintf(\"%s (%s - %s)\", $1, $4, megaHertz2HumanFormat($2))}" + metrics: + hw.cpu.speed.limit{limit_type="max"}: megaHertz2Hertz($2) + collect: + type: multiInstance + keys: + - id + sources: + source(1): + # Caption,HealthState + type: wbem + query: "SELECT DeviceID,HealthState FROM CIM_Processor" + namespace: root/cimv2 + computes: + # Caption,HealthState,HealthState + - type: duplicateColumn + column: 2 + # Caption,PatrolStatus,HealthState + - type: translate + column: 2 + translationTable: "${translation::statusTranslationTable}" + # Caption,PatrolStatus,StatusInformation + - type: translate + column: 3 + translationTable: "${translation::statusInformationTranslationTable}" + mapping: + source: "${source::monitors.cpu.collect.sources.source(1)}" + attributes: + id: $1 + metrics: + hw.status{hw.type="cpu"}: $2 + legacyTextParameters: + StatusInformation: $3 + memory: + discovery: + sources: + source(1): + # Source (1) + # Get a list of discrete sensors + # MonitoredDeviceID,Caption,HealthState,IpmiSensorType,Name + type: wbem + query: "SELECT MonitoredDeviceID,Caption,HealthState,IpmiSensorType,Name,CurrentState,DeviceID,SensorType FROM OMC_DiscreteSensor" + namespace: root/cimv2 + computes: + # Add the NumericOnly Flag + # MonitoredDeviceID,Caption,HealthState,IpmiSensorType,Name,NumericOnly,CurrentState,DeviceID,SensorType + - type: rightConcat + column: 5 + value: ;0 + source(2): + # Source (2) + # Get a list of numeric sensors (these are errorcounts, so we don't want the number only the status) + # MonitoredDeviceID,Caption,HealthState,IpmiSensorType,Name,CurrentState,DeviceID + type: wbem + query: "SELECT MonitoredDeviceID,Caption,HealthState,IpmiSensorType,Name,CurrentState,DeviceID,SensorType FROM OMC_NumericSensor" + namespace: root/cimv2 + computes: + # Add the NumericOnly Flag + # MonitoredDeviceID,Caption,HealthState,IpmiSensorType,Name,NumericOnly,CurrentState,DeviceID,SensorType + - type: rightConcat + column: 5 + value: ;1 + source(3): + # Union the Discrete and Numeric Sensors + # MonitoredDeviceID,Caption,HealthState,IpmiSensorType,Name,NumericOnly,CurrentState,DeviceID,SensorType + type: tableUnion + tables: + - "${source::monitors.memory.discovery.sources.source(1)}" + - "${source::monitors.memory.discovery.sources.source(2)}" + computes: + # Keep Only Memory Sensors + # MonitoredDeviceID,Caption,HealthState,IpmiSensorType,Name,NumericOnly,CurrentState,DeviceID,SensorType + - type: keepOnlyMatchingLines + column: 4 + valueList: 12 + # Process the discrete sensors to take worse Status and make each ID unique + # DeviceID,HealthState,Name,NumericOnly + - type: awk + script: "${file::embeddedFile-1}" + keep: ^MSHW; + separators: ; + selectColumns: "2,4,5,6" + source(4): + # Source (4) + # Get a the overall memory status + # HealthState + type: wbem + query: SELECT HealthState FROM OMC_Memory + namespace: root/cimv2 + computes: + # Add the Global ID + # DeviceID,HealthState + - type: leftConcat + column: 1 + value: Global; + # Add the Global Name and the NumericOnly Column + # DeviceID,HealthState,Name,NumericOnly + - type: rightConcat + column: 2 + value: ;Global;0 + source(5): + # Get a copy of the last memory discovery. This was stored under Enclosure 7 + # DeviceID,HealthState,Tag,ElementName,Size + type: copy + from: "${source::monitors.enclosure.discovery.sources.source(5)}" + computes: + # Add OldInstance- to the Name + # DeviceID,HealthState,Name,NumericOnly + - type: leftConcat + column: 3 + value: OldInstance- + # Exclude OldInstance-Global as we're only interested in individual modules. + # DeviceID,HealthState,Name,NumericOnly + - type: excludeMatchingLines + column: 3 + valueList: OldInstance-Global + # Exclude NumericOnly Sensors as they can exist with Global + # DeviceID,HealthState,Name,NumericOnly + - type: excludeMatchingLines + column: 4 + valueList: 1 + source(6): + # Source (6) = Table Union of 4 (OMC_Memory) and 3 (IPMI) and 5 (Old Instance) + # DeviceID,HealthState,Name,NumericOnly + type: tableUnion + tables: + - "${source::monitors.memory.discovery.sources.source(3)}" + - "${source::monitors.memory.discovery.sources.source(4)}" + - "${source::monitors.memory.discovery.sources.source(5)}" + computes: + # Use an awk script to determine if there are any valid IPMI sensors, if not, use the global. + # DeviceID,HealthState,Name,NumericOnly + - type: awk + script: "${file::embeddedFile-2}" + keep: ^MSHW; + separators: ; + selectColumns: "2,3,4,5" + source(7): + # Make a copy of discovery 8 for use as the instance table. Source(8) with its IPMIFound label will be passed to enclosure to be used at the next memory discovery.) + # DeviceID,HealthState,Name,NumericOnly + type: copy + from: "${source::monitors.memory.discovery.sources.source(6)}" + computes: + # Exclude the ipmiFound line + # DeviceID,HealthState,Name,NumericOnly + - type: excludeMatchingLines + column: 1 + valueList: ipmiFound + mapping: + # InstanceTable = Source(9) + # DeviceID,HealthState,Name,NumericOnly + source: "${source::monitors.memory.discovery.sources.source(7)}" + attributes: + id: $1 + __display_id: $3 + hw.parent.type: enclosure + name: $3 + collect: + type: multiInstance + keys: + - id + sources: + source(1): + # Source (1) + # Get a list of discrete sensors + # MonitoredDeviceID,Caption,HealthState,IpmiSensorType,Name,CurrentState,DeviceID + type: wbem + query: "SELECT MonitoredDeviceID,Caption,HealthState,IpmiSensorType,Name,CurrentState,DeviceID FROM OMC_DiscreteSensor" + namespace: root/cimv2 + computes: + # Add a numeric sensor flag + # MonitoredDeviceID,Caption,HealthState,IpmiSensorType,Name,NumericSensor,CurrentState,DeviceID + - type: rightConcat + column: 5 + value: ;0 + source(2): + # Source (2) + # Get a list of numeric sensors + # MonitoredDeviceID,Caption,HealthState,IpmiSensorType,Name,CurrentState,DeviceID + type: wbem + query: "SELECT MonitoredDeviceID,Caption,HealthState,IpmiSensorType,Name,CurrentState,DeviceID FROM OMC_NumericSensor" + namespace: root/cimv2 + computes: + # Add a numeric sensor flag + # MonitoredDeviceID,Caption,HealthState,IpmiSensorType,Name,NumericSensor,CurrentState,DeviceID + - type: rightConcat + column: 4 + value: ;1 + source(3): + # Union the Discrete and Numeric Sensors + # MonitoredDeviceID,Caption,HealthState,IpmiSensorType,Name,NumericSensor,CurrentState,DeviceID + type: tableUnion + tables: + - "${source::monitors.memory.collect.sources.source(1)}" + - "${source::monitors.memory.collect.sources.source(2)}" + computes: + # Process the discrete sensors to take worse Status and make each ID unique + # DeviceID,StatusInformation,HealthState + - type: awk + script: "${file::embeddedFile-1}" + keep: ^MSHW; + separators: ; + selectColumns: "2,3,4" + # Patrol Status comes from HealthState + # MonitoredDeviceID,StatusInformation,HealthState + - type: translate + column: 3 + translationTable: "${translation::statusTranslationTable}" + source(4): + # Get a the overall memory status + # HealthState + type: wbem + query: SELECT HealthState FROM OMC_Memory + namespace: root/cimv2 + computes: + # Add the Global ID and an identifier for use in the AWK script + # MonitoredDeviceID,HealthState + - type: leftConcat + column: 1 + value: Global; + # Duplicate the Status Column + # MonitoredDeviceID,HealthState,HealthState + - type: duplicateColumn + column: 2 + # Translate Status + # MonitoredDeviceID,HealthState,PatrolStatus + - type: translate + column: 3 + translationTable: "${translation::statusTranslationTable}" + # Translate Status Information + # MonitoredDeviceID,StatusInformation,PatrolStatus + - type: translate + column: 2 + translationTable: "${translation::statusInformationTranslationTable}" + source(5): + # Union of Global and IPMI (4 & 3) + # MonitoredDeviceID,StatusInformation,PatrolStatus + type: tableUnion + tables: + - "${source::monitors.memory.collect.sources.source(3)}" + - "${source::monitors.memory.collect.sources.source(4)}" + mapping: + # Instance Table + # MonitoredDeviceID,StatusInformation,PatrolStatus + source: "${source::monitors.memory.collect.sources.source(5)}" + attributes: + id: $1 + metrics: + hw.status{hw.type="memory"}: $3 + legacyTextParameters: + StatusInformation: $2 + power_supply: + discovery: + sources: + source(1): + # Caption,Availability,DeviceID + type: wbem + query: "SELECT Caption,Availability,DeviceID FROM CIM_PowerSupply" + namespace: root/cimv2 + computes: + # Keep only lines with a valid HealthState + # Caption,Availability,DeviceID + # 1 = Other, 2 = Unknown, 6 = Not Applicable, 11 = Not Installed + - type: excludeMatchingLines + column: 2 + valueList: "1,2,6,11" + mapping: + # InstanceTable = Source(1) + # Caption,Availability,DeviceID + source: "${source::monitors.power_supply.discovery.sources.source(1)}" + attributes: + id: $3 + __display_id: $1 + hw.parent.type: enclosure + name: $1 + collect: + type: multiInstance + keys: + - id + sources: + source(1): + # DeviceID,HealthState,Availability + type: wbem + query: "SELECT DeviceID,Healthstate,Availability FROM CIM_PowerSupply" + namespace: root/cimv2 + computes: + # Duplicate Availability + # DeviceID,HealthState,Availability,Availability + - type: duplicateColumn + column: 3 + # Power Supply Patrol Status comes from HealthState + # DeviceID,PatrolStatus,Availability,Availability + - type: translate + column: 2 + translationTable: "${translation::statusTranslationTable}" + # Power Supply Information Status comes from Availability + # DeviceID,PatrolStatus,StatusInformation,Availability + - type: translate + column: 3 + translationTable: "${translation::psuAvailabilityInformationTranslationTable}" + # On some machines (namely Cisco Blades) the Health State Remains 0 (UNKNOWN) even if Availability clearly indicates a status + # Translate the Availability into Patrol Status + # DeviceID,PatrolStatus,StatusInformation,PatrolStatus(Availability) + - type: translate + column: 4 + translationTable: "${translation::psuAvailabilityTranslationTable}" + # Exclude lines whose status is missing. They will be picked up in the discovery as missing. + # DeviceID,PatrolStatus,StatusInformation,PatrolStatus(Availability) + - type: excludeMatchingLines + column: 2 + valueList: MISSING + # Let's add the availability status to the main Patrol Status and then take worse case + # DeviceID,PatrolStatus,StatusInformation,PatrolStatus(Availability) + - type: rightConcat + column: 2 + value: '|' + - type: rightConcat + column: 2 + value: $4 + - type: convert + column: 2 + conversion: array2SimpleStatus + mapping: + # Value Table + # DeviceID,PatrolStatus,StatusInformation + source: "${source::monitors.power_supply.collect.sources.source(1)}" + attributes: + id: $1 + metrics: + hw.status{hw.type="power_supply"}: $2 + legacyTextParameters: + StatusInformation: $3 + temperature: + discovery: + sources: + source(1): + # Source(1) Numeric Thermal Table + # BaseUnits,Caption,CurrentReading,DeviceID,HealthState,UpperThresholdNonCritical,UpperThresholdCritical + type: wbem + query: "SELECT BaseUnits,Caption,CurrentReading,DeviceID,HealthState,UpperThresholdNonCritical,UpperThresholdCritical FROM CIM_NumericSensor" + namespace: root/cimv2 + computes: + # Keep only lines with units of type 2 (Degree C) + # BaseUnits,Caption,CurrentReading,DeviceID,HealthState,UpperThresholdNonCritical,UpperThresholdCritical + - type: keepOnlyMatchingLines + column: 1 + valueList: 2 + # Divide Thresholds by 100 + # BaseUnits,Caption,CurrentReading,DeviceID,HealthState,UpperThresholdNonCritical,UpperThresholdCritical + - type: divide + column: 6 + value: 100 + - type: divide + column: 7 + value: 100 + # Remove Status of Unknown + # BaseUnits,Caption,CurrentReading,DeviceID,HealthState,UpperThresholdNonCritical,UpperThresholdCritical + - type: replace + column: 5 + existingValue: 0 + newValue: "" + # Thresholds for Temperature are sometimes reported as "0" by the agent. Replacing these "0" by no value to prevent incorrect thresholds. + # Add a MSHW_ string to our value and then remove MSHW_0. This should remove a 0 value and only that value + # BaseUnits,Caption,CurrentReading,DeviceID,HealthState,UpperThresholdNonCritical,UpperThresholdCritical + - type: leftConcat + column: 6 + value: MSHW_ + - type: leftConcat + column: 7 + value: MSHW_ + # Remove 0 value + - type: replace + column: 6 + existingValue: MSHW_0 + newValue: "" + - type: replace + column: 7 + existingValue: MSHW_0 + newValue: "" + # Remove MSHW_ for non 0 values + - type: replace + column: 6 + existingValue: MSHW_ + newValue: "" + - type: replace + column: 7 + existingValue: MSHW_ + newValue: "" + # Add a Sensor Type Column + # BaseUnits,Caption,CurrentReading,DeviceID,HealthState,UpperThresholdNonCritical,UpperThresholdCritical,(SensorType) + - type: rightConcat + column: 7 + value: ; + source(2): + # Source(2) Discrete Thermal Table + # Caption,DeviceID,HealthState,SensorType + type: wbem + query: "SELECT Caption,DeviceID,HealthState,SensorType FROM OMC_DiscreteSensor" + namespace: root/cimv2 + computes: + # Keep only lines with sensor type 2 (Thermal) + # Caption,DeviceID,HealthState,SensorType + - type: keepOnlyMatchingLines + column: 3 + valueList: 2 + # Keep only lines with a valid HealthState + # Caption,DeviceID,HealthState,SensorType + - type: keepOnlyMatchingLines + column: 2 + valueList: "5,15,20,25,30" + # Need to add a bunch of columns so the dicrete and numerical tables will matchup + # Add BaseUnits + # (BaseUnits),Caption,DeviceID,HealthState,SensorType + - type: leftConcat + column: 1 + value: ; + # Add CurrentReading,DeviceID + # (BaseUnits),Caption,(CurrentReading),DeviceID,HealthState,SensorType + - type: leftConcat + column: 3 + value: ; + # Add UpperThresholdNonCritical,UpperThresholdCritical + # (BaseUnits),Caption,(CurrentReading),DeviceID,HealthState,(UpperThresholdNonCritical),(UpperThresholdCritical),SensorType + - type: leftConcat + column: 6 + value: ;; + source(3): + # BaseUnits,Caption,CurrentReading,DeviceID,HealthState,UpperThresholdNonCritical,UpperThresholdCritical, SensorType + type: tableUnion + tables: + - "${source::monitors.temperature.discovery.sources.source(1)}" + - "${source::monitors.temperature.discovery.sources.source(2)}" + computes: + # MSHW;BaseUnits,Caption,CurrentReading,DeviceID,HealthState,UpperThresholdNonCritical,UpperThresholdCritical, SensorType + - type: awk + script: "${file::embeddedFile-3}" + - type: keepOnlyMatchingLines + column: 1 + valueList: MSHW + mapping: + # InstanceTable = Source(1) + # 1 2 3 # 4 # 5 # # 6 # 7 + # MSHW;BaseUnits,Caption,CurrentReading,DeviceID,HealthState,UpperThresholdNonCritical,UpperThresholdCritical, SensorType + source: "${source::monitors.temperature.discovery.sources.source(3)}" + attributes: + __display_id: $3 + id: $5 + hw.parent.type: enclosure + name: $3 + metrics: + hw.temperature.limit{limit_type="high.critical"}: $8 + hw.temperature.limit{limit_type="high.degraded"}: $7 + conditionalCollection: + hw.status{hw.type="temperature"}: $6 + hw.temperature: $4 + collect: + type: multiInstance + keys: + - id + sources: + source(1): + # BaseUnits,DeviceID,CurrentReading,HealthState + # Note - Both RPM and Percent Temperature speeds are in Current Reading. + type: wbem + query: "SELECT BaseUnits,DeviceID,CurrentReading,HealthState FROM CIM_NumericSensor" + namespace: root/cimv2 + computes: + # Keep only lines with units of type 2 (Degree C) + # BaseUnits,DeviceID,CurrentReading,HealthState + - type: keepOnlyMatchingLines + column: 1 + valueList: 2 + # Divide Speed by 100 + # BaseUnits,DeviceID,CurrentReading,HealthState + - type: divide + column: 3 + value: 100 + source(2): + # Source(2) Discrete Thermal Table + # SensorType,DeviceID,HealthState + type: wbem + query: "SELECT SensorType,DeviceID,HealthState FROM OMC_DiscreteSensor" + namespace: root/cimv2 + computes: + # Keep only lines with units of type 2 (Thermal) + # SensorType,DeviceID,HealthState + - type: keepOnlyMatchingLines + column: 1 + valueList: 2 + # Add a CurrentReading Column + # SensorType,DeviceID,CurrentReading,HealthState + - type: leftConcat + column: 3 + value: ; + source(3): + # BaseUnits,DeviceID,CurrentReading,HealthState,HealthState + type: tableUnion + tables: + - "${source::monitors.temperature.collect.sources.source(1)}" + - "${source::monitors.temperature.collect.sources.source(2)}" + computes: + # BaseUnits,DeviceID,CurrentReading,HealthState,HealthState + - type: duplicateColumn + column: 4 + # BaseUnits,DeviceID,CurrentReading,PatrolStatus,HealthState + - type: translate + column: 4 + translationTable: "${translation::statusTranslationTable}" + # BaseUnits,DeviceID,CurrentReading,PatrolStatus,StatusInformation + - type: translate + column: 5 + translationTable: "${translation::statusInformationTranslationTable}" + mapping: + # Value Table + # BaseUnits,DeviceID,CurrentReading,PatrolStatus,StatusInformation + source: "${source::monitors.temperature.collect.sources.source(3)}" + attributes: + id: $2 + metrics: + hw.status{hw.type="temperature"}: $4 + hw.temperature: $3 + legacyTextParameters: + StatusInformation: $5 + fan: + discovery: + sources: + source(1): + # BaseUnits,Caption,CurrentReading,DeviceID,FanSpeedThreshold + # Note - Both RPM and Percent Fan speeds are in Current Reading. + type: wbem + query: "SELECT BaseUnits,Caption,CurrentReading,DeviceID,HealthState,LowerThresholdCritical FROM CIM_NumericSensor" + namespace: root/cimv2 + computes: + # Keep only lines with units of type 19 (RPM) + # BaseUnits,Caption,Speed,DeviceID,HealthState,FanSpeedThreshold + - type: keepOnlyMatchingLines + column: 1 + valueList: 19 + # Divide Threshold by 100 + # BaseUnits,Caption,Speed,DeviceID,HealthState,FanSpeedThreshold + - type: divide + column: 6 + value: 100 + # Add an empty FanPercent column + # BaseUnits,Caption,(FanPercent),Speed,DeviceID,HealthState,FanSpeedThreshold + - type: leftConcat + column: 3 + value: ; + # Add an empty FanPercent Threshold column + # BaseUnits,Caption,(FanPercent),Speed,DeviceID,HealthState,FanPercentThreshold,FanSpeedThreshold + - type: leftConcat + column: 7 + value: ; + source(2): + # BaseUnits,Caption,CurrentReading,DeviceID,FanPercentThreshold + # Note - Both RPM and Percent Fan speeds are in Current Reading. + type: wbem + query: "SELECT BaseUnits,Caption,CurrentReading,DeviceID,HealthState,LowerThresholdCritical,SensorType FROM CIM_NumericSensor" + namespace: root/cimv2 + computes: + # Keep only lines with units of type 65 (Percentage) + # BaseUnits,Caption,FanPercent,DeviceID,HealthState,FanPercentThreshold,SensorType + - type: keepOnlyMatchingLines + column: 1 + valueList: 65 + # Keep only lines with sensor type of 4 (Fan) + # BaseUnits,Caption,FanPercent,DeviceID,HealthState,FanPercentThreshold,SensorType + - type: keepOnlyMatchingLines + column: 7 + valueList: 5 + # Remove the IPMISensorType + # BaseUnits,Caption,FanPercent,DeviceID,HealthState,FanPercentThreshold,SensorType + - type: keepColumns + columnNumbers: "1,2,3,4,5,6" + # Divide Threshold by 100 + # BaseUnits,Caption,FanPercent,DeviceID,HealthState,FanPercentThreshold + - type: divide + column: 6 + value: 100 + # Add an empty Speed column + # BaseUnits,Caption,FanPercent,(Speed),DeviceID,HealthState,FanPercentThreshold + - type: rightConcat + column: 3 + value: ; + # Add an empty FanSpeedThreshold + # BaseUnits,Caption,FanPercent,(Speed),DeviceID,HealthState,FanPercentThreshold,(FanSpeedThreshold) + - type: rightConcat + column: 7 + value: ; + source(3): + # BaseUnits,Caption,FanPercent,Speed,DeviceID,HealthState,FanPercentThreshold,FanSpeedThreshold + type: tableUnion + tables: + - "${source::monitors.fan.discovery.sources.source(1)}" + - "${source::monitors.fan.discovery.sources.source(2)}" + computes: + # Remove Status of Unknown + # BaseUnits,Caption,FanPercent,Speed,DeviceID,HealthState,FanPercentThreshold,FanSpeedThreshold + - type: replace + column: 6 + existingValue: 0 + newValue: "" + # Duplicate the Healthstate so that we can use it to activate/deactivate numeric sensors. + # BaseUnits,Caption,FanPercent,Speed,DeviceID,HealthState,NumericActivation,FanPercentThreshold,FanSpeedThreshold + - type: duplicateColumn + column: 6 + - type: leftConcat + column: 7 + value: MSHW + - type: rightConcat + column: 7 + value: MSHW + # Change MSHWMSHW to Activate as there is no valid HealthState + - type: replace + column: 7 + existingValue: MSHWMSHW + newValue: Activate + # Remove MSHW + - type: replace + column: 7 + existingValue: MSHW + newValue: "" + # Remove The original Healthstate so that the activation is blank for sensors with valid HealthStates + - type: replace + column: 7 + existingValue: $6 + newValue: "" + # Ok, now we know if there is no valid healthstate and that we should activate speed / speedpercent. + # Duplicate Speed + # BaseUnits,Caption,FanPercent,SpeedActivation,Speed,DeviceID,HealthState,NumericActivation,FanPercentThreshold,FanSpeedThreshold + - type: duplicateColumn + column: 4 + # Speed - Add Activation column to left and right, then remove ActivateActivate + # BaseUnits,Caption,FanPercent,SpeedActivation,Speed,DeviceID,HealthState,NumericActivation,FanPercentThreshold,FanSpeedThreshold + - type: leftConcat + column: 4 + value: $8 + - type: rightConcat + column: 4 + value: $8 + - type: replace + column: 4 + existingValue: ActivateActivate + newValue: "" + # Now remove the original value + - type: replace + column: 4 + existingValue: $5 + newValue: "" + # Duplicate Speed + # BaseUnits,Caption,FanPercent,SpeedActivation,Speed,DeviceID,HealthState,NumericActivation,FanPercentThreshold,FanSpeedThreshold + - type: duplicateColumn + column: 3 + # Speed - Add Activation column to left and right, then remove ActivateActivate + # BaseUnits,Caption,FanPercentActivation,FanPercent,SpeedActivation,Speed,DeviceID,HealthState,NumericActivation,FanPercentThreshold,FanSpeedThreshold + - type: leftConcat + column: 3 + value: $9 + - type: rightConcat + column: 3 + value: $9 + - type: replace + column: 3 + existingValue: ActivateActivate + newValue: "" + # Now remove the original value + - type: replace + column: 3 + existingValue: $4 + newValue: "" + mapping: + # InstanceTable = Source(3) + # 1 2 # # 3 4 # 5 # 6 7 # 8 # 9 # 10 # 11 + # BaseUnits,Caption,FanPercentActivation,FanPercent,SpeedActivation,Speed,DeviceID,HealthState,NumericActivation,FanPercentThreshold,FanSpeedThreshold + source: "${source::monitors.fan.discovery.sources.source(3)}" + attributes: + id: $7 + __display_id: $2 + hw.parent.type: enclosure + name: $2 + metrics: + hw.fan.speed.limit{limit_type="low.critical"}: $11 + hw.fan.speed_ratio.limit{limit_type="low.critical"}: percent2Ratio($10) + conditionalCollection: + hw.fan.speed: $5 + hw.status{hw.type="fan"}: $8 + hw.fan.speed_ratio: $3 + collect: + type: multiInstance + keys: + - id + sources: + source(1): + # BaseUnits,DeviceID,CurrentReading,DeviceID,FanSpeedThreshold + # Note - Both RPM and Percent Fan speeds are in Current Reading. + type: wbem + query: "SELECT BaseUnits,DeviceID,CurrentReading,HealthState FROM CIM_NumericSensor" + namespace: root/cimv2 + computes: + # Keep only lines with units of type 19 (RPM) + # BaseUnits,DeviceID,Speed,HealthStatus + - type: keepOnlyMatchingLines + column: 1 + valueList: 19 + # Divide Speed by 100 + # BaseUnits,DeviceID,Speed,HealthStatus + - type: divide + column: 3 + value: 100 + # Add an empty FanPercent column + # BaseUnits,DeviceID,(FanPercent),Speed,HealthStatus + - type: leftConcat + column: 3 + value: ; + source(2): + # BaseUnits,DeviceID,CurrentReading,HealthStatus + # Note - Both RPM and Percent Fan speeds are in Current Reading. + type: wbem + query: "SELECT BaseUnits,DeviceID,CurrentReading,HealthState FROM CIM_NumericSensor" + namespace: root/cimv2 + computes: + # Keep only lines with units of type 65 (Percentage) + # BaseUnits,DeviceID,FanPercent,HealthStatus + - type: keepOnlyMatchingLines + column: 1 + valueList: 65 + # Divide FanPercent by 100 + # BaseUnits,DeviceID,FanPercent,HealthStatus + - type: divide + column: 3 + value: 100 + # Add an empty Speed column + # BaseUnits,DeviceID,FanPercent,(Speed),HealthStatus + - type: rightConcat + column: 3 + value: ; + source(3): + # BaseUnits,DeviceID,FanPercent,Speed,HealthStatus + type: tableUnion + tables: + - "${source::monitors.fan.collect.sources.source(1)}" + - "${source::monitors.fan.collect.sources.source(2)}" + computes: + # BaseUnits,DeviceID,FanPercent,Speed,HealthStatus + - type: duplicateColumn + column: 5 + # BaseUnits,DeviceID,FanPercent,Speed,PatrolStatus,HealthStatus + - type: translate + column: 5 + translationTable: "${translation::statusTranslationTable}" + # BaseUnits,DeviceID,FanPercent,Speed,PatrolStatus,StatusInformation + - type: translate + column: 6 + translationTable: "${translation::statusInformationTranslationTable}" + mapping: + # Value Table + # BaseUnits,DeviceID,FanPercent,Speed,PatrolStatus,StatusInformation + source: "${source::monitors.fan.collect.sources.source(3)}" + attributes: + id: $2 + metrics: + hw.status{hw.type="fan"}: $5 + hw.fan.speed: $4 + hw.fan.speed_ratio: percent2Ratio($3) + legacyTextParameters: + StatusInformation: $6 + voltage: + discovery: + sources: + source(1): + # BaseUnits,Caption,CurrentReading,DeviceID,HealthState,LowerThresholdCritical,UpperThresholdCritical + type: wbem + query: "SELECT BaseUnits,Caption,CurrentReading,DeviceID,HealthState,LowerThresholdCritical,UpperThresholdCritical FROM CIM_NumericSensor" + namespace: root/cimv2 + computes: + # Keep only lines with units of type 5 (Volts) + # BaseUnits,Caption,CurrentReading,DeviceID,HealthState,LowerThresholdCritical,UpperThresholdCritical + - type: keepOnlyMatchingLines + column: 1 + valueList: 5 + # Multiply by 10 to get millivolts (Divide Thresholds by 100, then Multiply by 1000) + # BaseUnits,Caption,CurrentReading,DeviceID,HealthState,LowerThresholdCritical,UpperThresholdCritical + - type: multiply + column: 6 + value: 10 + - type: multiply + column: 7 + value: 10 + # Remove Status of Unknown + # BaseUnits,Caption,CurrentReading,DeviceID,HealthState,LowerThresholdCritical,UpperThresholdCritical + - type: replace + column: 5 + existingValue: 0 + newValue: "" + # Thresholds for Voltage are sometimes reported as "0" by the agent. Replacing these "0" by no value to prevent incorrect thresholds. + # Add a MSHW_ string to our value and then remove MSHW_0. This should remove a 0 value and only that value + # BaseUnits,Caption,CurrentReading,DeviceID,HealthState,LowerThresholdCritical,UpperThresholdCritical + - type: leftConcat + column: 6 + value: MSHW_ + - type: leftConcat + column: 7 + value: MSHW_ + # Remove 0 value + - type: replace + column: 6 + existingValue: MSHW_0 + newValue: "" + - type: replace + column: 7 + existingValue: MSHW_0 + newValue: "" + # Remove MSHW_ for non 0 values + - type: replace + column: 6 + existingValue: MSHW_ + newValue: "" + - type: replace + column: 7 + existingValue: MSHW_ + newValue: "" + source(2): + # Source(2) Discrete Voltage Table + # SensorType,Caption,DeviceID,HealthState + type: wbem + query: "SELECT SensorType,Caption,DeviceID,HealthState FROM OMC_DiscreteSensor" + namespace: root/cimv2 + computes: + # Keep only lines with sensor type 3 (Voltage) + # SensorType,Caption,DeviceID,HealthState + - type: keepOnlyMatchingLines + column: 1 + valueList: 3 + # Keep only lines with a valid HealthState + # SensorType,Caption,DeviceID,HealthState + - type: keepOnlyMatchingLines + column: 4 + valueList: "5,15,20,25,30" + # Add columns CurrentReading to the left of Device ID + # and LowerThresholdCritical,UpperThresholdCritical to the right + # SensorType,Caption,CurrentReading,DeviceID,HealthState,LowerThresholdCritical,UpperThresholdCritical + - type: rightConcat + column: 4 + value: ;; + - type: leftConcat + column: 3 + value: ; + source(3): + # SensorType,Caption,CurrentReading,DeviceID,HealthState,LowerThresholdCritical,UpperThresholdCritical + type: tableUnion + tables: + - "${source::monitors.voltage.discovery.sources.source(1)}" + - "${source::monitors.voltage.discovery.sources.source(2)}" + mapping: + # InstanceTable = Source(1) + # 1 2 3 # 4 # 5 # # 6 # 7 + # BaseUnits,Caption,CurrentReading,DeviceID,HealthState,LowerThresholdCritical,UpperThresholdCritical + source: "${source::monitors.voltage.discovery.sources.source(3)}" + attributes: + id: $4 + __display_id: $2 + hw.parent.type: enclosure + name: $2 + metrics: + hw.voltage.limit{limit_type="high.degraded"}: $7 + hw.voltage.limit{limit_type="low.critical"}: $6 + conditionalCollection: + hw.status{hw.type="voltage"}: $5 + hw.voltage: $3 + collect: + type: multiInstance + keys: + - id + sources: + source(1): + # BaseUnits,DeviceID,CurrentReading,HealthState + type: wbem + query: "SELECT BaseUnits,DeviceID,CurrentReading,HealthState FROM CIM_NumericSensor" + namespace: root/cimv2 + computes: + # Keep only lines with units of type 5 (Voltage) + # BaseUnits,DeviceID,CurrentReading,HealthState + - type: keepOnlyMatchingLines + column: 1 + valueList: 5 + # Multiply by 10 to get millivolts (Divide Thresholds by 100, then Multiply by 1000) + # BaseUnits,DeviceID,CurrentReading,HealthState + - type: multiply + column: 3 + value: 10 + source(2): + # Source(2) Discrete Thermal Table + # SensorType,DeviceID,HealthState + type: wbem + query: "SELECT SensorType,DeviceID,HealthState FROM OMC_DiscreteSensor" + namespace: root/cimv2 + computes: + # Keep only lines with units of type 3 (Voltage) + # SensorType,DeviceID,HealthState + - type: keepOnlyMatchingLines + column: 1 + valueList: 3 + # Add a CurrentReading Column + # SensorType,DeviceID,CurrentReading,HealthState + - type: leftConcat + column: 3 + value: ; + source(3): + # BaseUnits,DeviceID,CurrentReading,HealthState,HealthState + type: tableUnion + tables: + - "${source::monitors.voltage.collect.sources.source(1)}" + - "${source::monitors.voltage.collect.sources.source(2)}" + computes: + # BaseUnits,DeviceID,CurrentReading,HealthState,HealthState + - type: duplicateColumn + column: 4 + # BaseUnits,DeviceID,CurrentReading,PatrolStatus,HealthState + - type: translate + column: 4 + translationTable: "${translation::statusTranslationTable}" + # BaseUnits,DeviceID,CurrentReading,PatrolStatus,StatusInformation + - type: translate + column: 5 + translationTable: "${translation::statusInformationTranslationTable}" + mapping: + # Value Table + # BaseUnits,DeviceID,CurrentReading,PatrolStatus,StatusInformation + source: "${source::monitors.voltage.collect.sources.source(3)}" + attributes: + id: $2 + metrics: + hw.status{hw.type="voltage"}: $4 + hw.voltage: $3 + legacyTextParameters: + StatusInformation: $5 + network: + discovery: + sources: + source(1): + # Source(1) = CIM_NetworkPort + # DeviceID;PortTypeCode;Speed;PermanentAddress;EnabledState; + type: wbem + query: "SELECT DeviceID,PortType,Speed,PermanentAddress,EnabledState,FullDuplex FROM CIM_NetworkPort" + namespace: root/cimv2 + computes: + # Keep only activated ports + # DeviceID;PortTypeCode;Speed;PermanentAddress;EnabledState;FullDuplex + - type: excludeMatchingLines + column: 5 + valueList: 3 + # Translate PortType into a more readable string + # DeviceID;PortType;Speed;PermanentAddress;EnabledState;FullDuplex + - type: translate + column: 2 + translationTable: "${translation::PortTypeTranslationTable}" + # Convert Speed from bps to Mbps (divide by 1000000) + # DeviceID;PortType;SpeedMbps;PermanentAddress;EnabledState;FullDuplex + - type: divide + column: 3 + value: 1000000 + # Translate Full Duplex boolean from string to int + # Translate PortType into a more readable string + # DeviceID;PortType;Speed;PermanentAddress;EnabledState;FullDuplex + - type: translate + column: 6 + translationTable: "${translation::DuplexTypeTranslationTable}" + mapping: + # InstanceTable = Source(7) + # DeviceID;PortType;SpeedMbps;PermanentAddress;EnabledState;FullDuplex + source: "${source::monitors.network.discovery.sources.source(1)}" + attributes: + id: $1 + physical_address: $4 + physical_address_type: MAC + device_type: $2 + hw.parent.type: enclosure + name: "${awk::sprintf(\"%s (%s)\", $1, $2)}" + collect: + # The NetworkCard collect is a "MultiInstance" collect + type: multiInstance + keys: + - id + sources: + source(1): + # Source(1) = CIM_FCPort + # PortID;PortTypeCode;EnabledState;Speed;OperationalStatusArray + type: wbem + query: "SELECT DeviceID,PortType,EnabledState,Speed,OperationalStatus,FullDuplex FROM CIM_NetworkPort" + namespace: root/cimv2 + computes: + # Duplicate the "OperationalStatusArray" column + # PortID;PortTypeCode;EnabledState;Speed;OperationalStatusArray;OperationalStatusArray;FullDuplex + - type: duplicateColumn + column: 5 + # Then we translate DMTF's OperationStatus code into OK/WARN/ALARM (for the Status parameter) + # PortID;PortTypeCode;EnabledState;Speed;PATROLStatusArray;Status;FullDuplex + - type: arrayTranslate + column: 5 + translationTable: "${translation::NetworkCardOperationStatusTranslationTable}" + # And finally, we translate DMTF's OperationalStatus code into a more readable status string + # PortID;PortTypeCode;EnabledState;Speed;PATROLStatusArray;StatusInformation;FullDuplex + - type: arrayTranslate + column: 6 + translationTable: "${translation::DMTFOperationStatusInformationTranslationTable}" + resultSeparator: ' - ' + # Convert PATROLStatusArray to a simple (unique) PATROLStatus + # PortID;PortTypeCode;EnabledState;Speed;PATROLStatus;StatusInformation;FullDuplex + - type: convert + column: 5 + conversion: array2SimpleStatus + # // Finally, we check whether we have a 'Speed' number, meaning that the link is up + # PortID;PortTypeCode;EnabledState;Speed;LinkStatus;PATROLStatusArray;Status;FullDuplex + - type: duplicateColumn + column: 4 + # Finally, we check whether we have a 'Speed' number, meaning that the link is up + # Otherwise, if no speed, it means that the link is down + # PortID;PortTypeCode;EnabledState;Speed;LinkStatus;PATROLStatusArray;Status;FullDuplex + - type: translate + column: 5 + translationTable: "${translation::SpeedToLinkStatusTranslationTable}" + # Translate Full Duplex boolean from string to int + # Translate PortType into a more readable string + # PortID;PortTypeCode;EnabledState;Speed;LinkStatus;PATROLStatusArray;Status;FullDuplex + - type: translate + column: 8 + translationTable: "${translation::DuplexTypeTranslationTable}" + # Convert Speed from bps to Mbps (divide by 1000000) + # PortID;PortTypeCode;EnabledState;Speed;LinkStatus;PATROLStatusArray;Status;FullDuplex + - type: divide + column: 4 + value: 1000000 + mapping: + # PortID;PortTypeCode;EnabledState;Speed;LinkStatus;PATROLStatusArray;Status + # ValueTable = Source(1) + source: "${source::monitors.network.collect.sources.source(1)}" + attributes: + id: $1 + metrics: + hw.status{hw.type="network"}: $6 + hw.network.bandwidth.limit: megaBit2Bit($4) + hw.network.up: legacyLinkStatus($5) + hw.network.full_duplex: legacyFullDuplex($8) + legacyTextParameters: + StatusInformation: $7 + battery: + discovery: + sources: + source(1): + # DeviceID,HealthState,IpmiSensorType,Caption,deviceID + type: wbem + query: "SELECT MonitoredDeviceID,HealthState,IpmiSensorType,Caption,DeviceId FROM OMC_DiscreteSensor" + namespace: root/cimv2 + computes: + # Keep only lines with a valid HealthState + # DeviceID,HealthState,IpmiSensorType,Caption,deviceID + - type: keepOnlyMatchingLines + column: 3 + valueList: 41 + # Process the discrete sensors to group sensors together and find a common name + # DeviceID,DisplayID + - type: awk + script: "${file::embeddedFile-4}" + keep: ^MSHW; + separators: ; + selectColumns: "2,3" + mapping: + # InstanceTable = Source(1) + # DeviceID,DisplayID + source: "${source::monitors.battery.discovery.sources.source(1)}" + attributes: + id: $1 + __display_id: $2 + hw.parent.type: enclosure + name: $2 + collect: + type: multiInstance + keys: + - id + sources: + source(1): + # DeviceID,HealthState,IpmiSensorType,Caption,deviceID + type: wbem + query: "SELECT MonitoredDeviceID,HealthState,IpmiSensorType,Caption,DeviceId FROM OMC_DiscreteSensor" + namespace: root/cimv2 + computes: + # Keep only lines with a valid HealthState + # DeviceID,HealthState,IpmiSensorType,Caption,deviceID + - type: keepOnlyMatchingLines + column: 3 + valueList: 41 + # Process the discrete sensors to group sensors together and find a common name + # DeviceID,PatrolStatus,StatusInformation + - type: awk + script: "${file::embeddedFile-4}" + keep: ^MSHW; + separators: ; + selectColumns: "2,4,5" + # DeviceID,PatrolStatus,StatusInformation + - type: translate + column: 2 + translationTable: "${translation::statusTranslationTable}" + mapping: + # Instance Table + # DeviceID,PatrolStatus,StatusInformation + source: "${source::monitors.battery.collect.sources.source(1)}" + attributes: + id: $1 + metrics: + hw.status{hw.type="battery"}: $2 + legacyTextParameters: + StatusInformation: $3 +translations: + psuAvailabilityTranslationTable: + "11": MISSING + "12": failed + "13": UNKNOWN + "14": degraded + "15": ok + "16": ok + "17": degraded + "18": ok + "19": failed + Default: UNKNOWN + "1": MISSING + "2": MISSING + "3": ok + "4": degraded + "5": degraded + "6": MISSING + "7": failed + "8": failed + "9": ok + "20": degraded + "10": degraded + "21": ok + statusInformationTranslationTable: + "0": Unknown + "25": Critical Failure + "15": Minor Failure + "5": "" + "30": Non-Recoverable Error + Default: Unknown Status + "20": Major Failure + "10": Degraded + statusTranslationTable: + "0": UNKNOWN + "25": failed + "15": degraded + "5": ok + "30": failed + Default: UNKNOWN + "20": failed + "10": degraded + psuAvailabilityInformationTranslationTable: + "11": Not Installed + "12": Install Error + "13": Power Save - Unknown + "14": Power Save - Lower Power Mode + "15": Power Save - Standy + "16": Power Cycle + "17": Power Save - Warning + "18": Paused + "19": Not Ready + Default: Unknown Status + "1": Other / Unknown + "2": Unknown + "3": "" + "4": Warning + "5": In Test + "6": Not Applicable + "7": Power Off + "8": Off Line + "9": Off Duty + "20": Not Configured + "10": Degraded + "21": Quiesced + PortTypeTranslationTable: + "11": HBA FC Port NL (Node/Loop) + "55": Ethernet Port + "12": HBA FC Port F/NL (Node/Loop/Fabric) + "56": Ethernet Port + Default: Unknown Network Port Type + "0": Unknown Network Port Type + "110": Ethernet Port + "1": Other Network Port Type + "100": Ethernet Port + "101": Ethernet Port + "102": Ethernet Port + "103": Ethernet Port + "104": Ethernet Port + "105": Ethernet Port + "106": Ethernet Port + "107": Ethernet Port + "108": Ethernet Port + "109": Ethernet Port + "50": Ethernet Port + "51": Ethernet Port + "52": Ethernet Port + "53": Ethernet Port + "10": HBA FC Port N (Node) + "54": Ethernet Port + DuplexTypeTranslationTable: + "false": 0 + Default: 1 + NetworkCardOperationStatusTranslationTable: + "11": degraded + "12": failed + "13": failed + "14": failed + "15": degraded + "16": degraded + "17": ok + "18": ok + Default: UNKNOWN + "2": ok + "3": degraded + "4": degraded + "5": degraded + "6": failed + "7": failed + "8": degraded + "9": degraded + "10": ok + DMTFOperationStatusInformationTranslationTable: + "11": In Service + "12": No Contact + "13": Lost Communication + "14": Aborted + "15": Dormant + "16": Supporting Entity in Error + "17": Completed + "18": Power Mode + Default: Unknown Status + "2": "" + "3": Degraded + "4": Stressed + "5": Predicted Failure + "6": Error + "7": Non-Recoverable Error + "8": Starting + "9": Stopping + "10": Stopped + SpeedToLinkStatusTranslationTable: + "0": degraded + ? "" + : degraded + Default: ok diff --git a/src/it/metricshub-connectors/src/main/connector/hardware/VMwareESXi-header/embeddedFile-1 b/src/it/metricshub-connectors/src/main/connector/hardware/VMwareESXi-header/embeddedFile-1 new file mode 100644 index 0000000..86fa5a5 --- /dev/null +++ b/src/it/metricshub-connectors/src/main/connector/hardware/VMwareESXi-header/embeddedFile-1 @@ -0,0 +1,42 @@ +# MonitoredDeviceID,Caption,HealthState,IpmiSensorType,Name,NumericOnly,CurrentState,DeviceID,SensorType + +BEGIN {FS="[;]";} + +{ +# MonitoredDeviceID = $1 + Caption = $2 + HealthState = $3 + IpmiSensorType = $4 + Name = $5 + NumericOnly = $6 + CurrentState = $7 + DeviceID = $8 ; gsub("\.[^.]*$","",DeviceID); + SensorType = $9 + if ( SensorType == 11 && CurrentState == "Deassert" ) { MemoryNotPresent[DeviceID] = "True" ; print $0 } + + if ( DeviceID in Devices ) { + if ( NumericOnly == 0 ) { NumericOnlyArray[DeviceID] = 0 } + if ( Status[DeviceID] < HealthState) { Status[DeviceID] = HealthState} + if ( HealthState > 5) { + if (StatusInformation[DeviceID] == "") { StatusInformation[DeviceID] = Caption } + else { StatusInformation[DeviceID] = StatusInformation[DeviceID] " - " Caption } + } + } + else { + NumericOnlyArray[DeviceID] = NumericOnly + DisplayID[DeviceID] = Caption ; gsub (/\(.*\)/,"",DisplayID[DeviceID]); gsub (/:.*$/,"",DisplayID[DeviceID]); + if ( HealthState > 0 ) { + Devices[DeviceID] = DeviceID + Status[DeviceID] = HealthState + if ( HealthState > 5) { + StatusInformation[DeviceID] = Caption + } + } + } +} +END { for (DeviceID in Devices) { + if ( MemoryNotPresent[DeviceID] != "True" ) { + print ("MSHW;" DeviceID ";" StatusInformation[DeviceID] ";" Status[DeviceID] ";" DisplayID[DeviceID]";" NumericOnlyArray[DeviceID] ";") + } + } + } \ No newline at end of file diff --git a/src/it/metricshub-connectors/src/main/connector/hardware/VMwareESXi-header/embeddedFile-2 b/src/it/metricshub-connectors/src/main/connector/hardware/VMwareESXi-header/embeddedFile-2 new file mode 100644 index 0000000..6230199 --- /dev/null +++ b/src/it/metricshub-connectors/src/main/connector/hardware/VMwareESXi-header/embeddedFile-2 @@ -0,0 +1,16 @@ +BEGIN {FS="[;]";ipmiFound="false";globalFound="false";} +{if ($1 ~ /Global/) {if ($2 > 0) {globalFound=("MSHW;" $0) + } + } +# If an OldInstance is found, then deactivate the GlobalFound as the previous discovery found individual modules + else {if ($3 ~ /OldInstance-/) {globalFound="false" ; ipmiFound="true"} +# If it's not an OldInstance or Global or Blank, then print + else {if (NF > 3) {print ("MSHW;" $0); + if ($4 != 1) {ipmiFound="true";} + } + } + } + } +END {if (ipmiFound == "false" && globalFound != "false") {print globalFound} + if (ipmiFound == "true" ) {print "MSHW;ipmiFound;99;ipmiFound;0;"} + } \ No newline at end of file diff --git a/src/it/metricshub-connectors/src/main/connector/hardware/VMwareESXi-header/embeddedFile-3 b/src/it/metricshub-connectors/src/main/connector/hardware/VMwareESXi-header/embeddedFile-3 new file mode 100644 index 0000000..445775a --- /dev/null +++ b/src/it/metricshub-connectors/src/main/connector/hardware/VMwareESXi-header/embeddedFile-3 @@ -0,0 +1,28 @@ +BEGIN { FS = ";"; } +{ + BaseUnits = $1 + Caption = $2 + CurrentReading = $3 + DeviceID = $4 + HealthState = $5 + UpperThresholdNonCritical = $6 + UpperThresholdCritical = $7 + SensorType = $8 + if (length(CurrentReading) > 2 ) + { + if (UpperThresholdNonCritical == "" && UpperThresholdCritical == "") + { + print "MSHW;" BaseUnits ";" Caption ";" CurrentReading ";" DeviceID ";" HealthState ";;;" SensorType ";" + } + else + { + print "MSHW;" BaseUnits ";" Caption ";" CurrentReading ";" DeviceID ";;" UpperThresholdNonCritical ";" UpperThresholdCritical ";" SensorType ";" + } + + } + else + { + print "MSHW;" BaseUnits ";" Caption ";;" DeviceID ";" HealthState ";;;" SensorType ";" + } + +} \ No newline at end of file diff --git a/src/it/metricshub-connectors/src/main/connector/hardware/VMwareESXi-header/embeddedFile-4 b/src/it/metricshub-connectors/src/main/connector/hardware/VMwareESXi-header/embeddedFile-4 new file mode 100644 index 0000000..3a646e7 --- /dev/null +++ b/src/it/metricshub-connectors/src/main/connector/hardware/VMwareESXi-header/embeddedFile-4 @@ -0,0 +1,37 @@ +BEGIN {FS="[;]"} +#IpmisensorID contains at least 3 dots +$5 ~ /.*\..*\..*\.*/ { +IPMISensorID = $5 +HealthState = $2 +SensorName = $4 + +ID = IPMISensorID ; gsub("\.[^.]*$","",ID); + +# if we've already found a status for this ID, then try and find a common sensor name root +# We're going to assume the first 5 characters are the same to avoid a null DisplayID +# Note this will only be run if a battery has 2 or more sensors + if (ID in status) { + matchLength = 5 + for (i=5 ; i <= length(DisplayID[ID]) ; i++ ) { + if ( substr(DisplayID[ID],5,i) == substr(SensorName,5,i) ) { matchLength = i } + } + DisplayID[ID] = substr(DisplayID[ID],1,matchLength) + gsub(/:? *$/,"",DisplayID[ID]) + } + else { DisplayID[ID] = SensorName } +# See if we already have processed a valid statusInformation for this component +# i.e. there's more than one fault, do a worse than and append the sensor ID + if (ID in statusInformation) { + if (status[ID] < HealthState) {status[ID] = (HealthState);} + if (HealthState > 5) {if (statusInformation[ID]=="") {statusInformation[ID] = SensorName} + else {statusInformation[ID] = (statusInformation[ID] " - " SensorName)} + } + } +# If we haven't seen a fault, check if this sensor state and set statusInformation if there's an issue + else {if (HealthState > 0) {status[ID] = HealthState}; + if (HealthState > 5) {statusInformation[ID] = SensorName}; + } +} +END { for (ID in status) + print ("MSHW;" ID ";" DisplayID[ID] ";" status[ID]";" statusInformation[ID] ";") + } diff --git a/src/it/metricshub-connectors/src/main/connector/hardware/VMwareESXi/VMwareESXi.yaml b/src/it/metricshub-connectors/src/main/connector/hardware/VMwareESXi/VMwareESXi.yaml new file mode 100644 index 0000000..d9f00af --- /dev/null +++ b/src/it/metricshub-connectors/src/main/connector/hardware/VMwareESXi/VMwareESXi.yaml @@ -0,0 +1,25 @@ +--- +extends: +- ../Hardware/Hardware +- ../VMwareESXi-header/VMwareESXi-header +connector: + displayName: VMware ESXi 3.x + platforms: VMware ESXi + reliesOn: WBEM + version: 1.1 + information: "This connector provides hardware monitoring through the VMware ESXi implementation of SMASH, through the WBEM protocol." + detection: + connectionTypes: + - remote + - local + appliesTo: + - OOB + - Linux + supersedes: + - IpmiTool + criteria: + # Checking the version of ESXi + - type: wbem + namespace: root/cimv2 + query: SELECT MajorVersion FROM VMware_HypervisorSoftwareIdentity + expectedResult: ^3 diff --git a/src/it/metricshub-connectors/src/main/connector/hardware/VMwareESXiDisksIPMI/VMwareESXiDisksIPMI.yaml b/src/it/metricshub-connectors/src/main/connector/hardware/VMwareESXiDisksIPMI/VMwareESXiDisksIPMI.yaml new file mode 100644 index 0000000..45bbf87 --- /dev/null +++ b/src/it/metricshub-connectors/src/main/connector/hardware/VMwareESXiDisksIPMI/VMwareESXiDisksIPMI.yaml @@ -0,0 +1,130 @@ +--- +extends: +- ../Hardware/Hardware +connector: + displayName: VMware ESXi - Disks (IPMI) + platforms: VMware ESXi + reliesOn: WBEM + version: 1.1 + information: This connector provides disk monitoring through the VMware ESX4i agent's discrete IPMI sensors + detection: + connectionTypes: + - remote + - local + appliesTo: + - OOB + - Linux + criteria: + # Checking the version of ESXi + - type: wbem + namespace: root/cimv2 + query: SELECT MajorVersion FROM VMware_HypervisorSoftwareIdentity + expectedResult: "^[34567]" + # Check that there are sensors of IPMI Type 13 and SensorType 11 (Presence) + - type: wbem + namespace: root/cimv2 + query: "SELECT IpmiSensorType,SensorType,CurrentState FROM OMC_DiscreteSensor" + expectedResult: ^13;11;Assert; +monitors: + physical_disk: + discovery: + sources: + source(1): + type: wbem + query: "SELECT MonitoredDeviceID,Caption,HealthState,IpmiSensorType,SensorType,CurrentState,DeviceID FROM OMC_DiscreteSensor" + namespace: root/cimv2 + computes: + # Keep only Sensors of IPMI type 13 + # MonitoredDeviceID,Caption,HealthState,IpmiSensorType,SensorType,CurrentState,DeviceID + - type: keepOnlyMatchingLines + column: 4 + valueList: 13 + # Keep only Sensors of SensorType 11 (Presence) + # MonitoredDeviceID,Caption,HealthState,IpmiSensorType,SensorType,CurrentState,DeviceID + - type: keepOnlyMatchingLines + column: 5 + valueList: 11 + # Keep only Disks that are "Present", i.e. presence = Assert + # MonitoredDeviceID,Caption,HealthState,IpmiSensorType,SensorType,CurrentState,DeviceID + - type: keepOnlyMatchingLines + column: 6 + valueList: Assert + # Remove "Drive Present" from caption + # MonitoredDeviceID,Caption,HealthState,IpmiSensorType,SensorType,CurrentState,DeviceID + - type: replace + column: 2 + existingValue: Drive Present + newValue: "" + # Concat PD to the Device ID (to avoid numerical comparison table join in Patrol) + # MonitoredDeviceID,Caption,HealthState,IpmiSensorType,SensorType,CurrentState,DeviceID + - type: leftConcat + column: 7 + value: PD + mapping: + # The Instance table + # MonitoredDeviceID,Caption,HealthState,IpmiSensorType,SensorType,CurrentState,DeviceID + source: "${source::monitors.physical_disk.discovery.sources.source(1)}" + attributes: + id: $7 + __display_id: $2 + hw.parent.type: enclosure + name: $2 + collect: + # The DiskController collect is a "MultiInstance" collect + type: multiInstance + keys: + - id + sources: + source(1): + # Get a list of discrete sensors + # DeviceID,Caption,HealthState,IpmiSensorType + type: wbem + query: "SELECT MonitoredDeviceID,Caption,HealthState,IpmiSensorType,SensorType,DeviceID FROM OMC_DiscreteSensor" + namespace: root/cimv2 + computes: + # Keep only Sensors of IPMI type 13 + # MonitoredDeviceID,Caption,HealthState,IpmiSensorType,SensorType,DeviceID + - type: keepOnlyMatchingLines + column: 4 + valueList: 13 + # Keep only MonitoredDeviceID,Caption,HealthState so that we can use the same awk array translation as in memory.collect + # MonitoredDeviceID,Caption,HealthState,SensorType,DeviceID + - type: keepColumns + columnNumbers: "1,2,3,5,6" + # Process the discrete sensors to take worse Status and make each ID unique + # DeviceID,StatusInformation,HealthState + - type: awk + script: "${file::embeddedFile-1}" + keep: ^MSHW; + separators: ; + selectColumns: "2,3,4" + # Patrol Status comes from HealthState + # DeviceID,StatusInformation,HealthState + - type: translate + column: 3 + translationTable: "${translation::statusTranslationTable}" + # Add PD to the Identifier (see discovery) + # DeviceID,StatusInformation,HealthState + - type: leftConcat + column: 1 + value: PD + mapping: + # ValueTable = Source(1) + # DeviceID,StatusInformation,PatrolStatus + source: "${source::monitors.physical_disk.collect.sources.source(1)}" + attributes: + id: $1 + metrics: + hw.status{hw.type="physical_disk"}: $3 + legacyTextParameters: + StatusInformation: $2 +translations: + statusTranslationTable: + "0": UNKNOWN + "25": failed + "15": degraded + "5": ok + "30": failed + Default: UNKNOWN + "20": failed + "10": degraded diff --git a/src/it/metricshub-connectors/src/main/connector/hardware/VMwareESXiDisksIPMI/embeddedFile-1 b/src/it/metricshub-connectors/src/main/connector/hardware/VMwareESXiDisksIPMI/embeddedFile-1 new file mode 100644 index 0000000..51d1e0d --- /dev/null +++ b/src/it/metricshub-connectors/src/main/connector/hardware/VMwareESXiDisksIPMI/embeddedFile-1 @@ -0,0 +1,16 @@ +BEGIN {FS="[;]"} +{ID = $5; gsub("\.[^.]*$","",ID); + if ($4 == "11") { PresenceID[ID] = $5 }; + if (ID in tags2) { + if (tags3[ID] < $3) {tags3[ID] = ($3);} + if ($3 > 5) {if (tags2[ID]=="") {tags2[ID] = $2} + else {tags2[ID] = (tags2[ID] " - " $2)} + } + } + else {if ($3 > 0) {tags3[ID] = ($3)}; + if ($3 > 5) {tags2[ID] = $2}; + } +} +END { for (ID in tags3) + print ("MSHW;" PresenceID[ID] ";"tags2[ID]";"tags3[ID]";") + } \ No newline at end of file diff --git a/src/it/metricshub-connectors/src/main/connector/hardware/VMwareESXiDisksStorage/VMwareESXiDisksStorage.yaml b/src/it/metricshub-connectors/src/main/connector/hardware/VMwareESXiDisksStorage/VMwareESXiDisksStorage.yaml new file mode 100644 index 0000000..717d4c2 --- /dev/null +++ b/src/it/metricshub-connectors/src/main/connector/hardware/VMwareESXiDisksStorage/VMwareESXiDisksStorage.yaml @@ -0,0 +1,239 @@ +--- +extends: +- ../Hardware/Hardware +connector: + displayName: VMware ESXi - Disks (VMware) + platforms: VMware ESXi + reliesOn: WBEM + version: 1.1 + information: This connector provides disk monitoring through the VMware ESX4i agent's VMware classes + detection: + connectionTypes: + - remote + - local + appliesTo: + - OOB + - Linux + supersedes: + - VMwareESXiDisksIPMI + criteria: + # Checking the version of ESXi + - type: wbem + namespace: root/cimv2 + query: SELECT MajorVersion FROM VMware_HypervisorSoftwareIdentity + expectedResult: "^[34567]" + # Check that there are ESX Disks (Storage Extents) + - type: wbem + namespace: root/cimv2 + query: "SELECT DeviceID,Caption,NumberOfBlocks,BlockSize FROM VMware_StorageExtent" +monitors: + disk_controller: + discovery: + sources: + source(1): + # Source(1) = VMWare_HHRCController + # Model,HeathState,Name + # The info we wish to use in creating the Controller + type: wbem + query: "SELECT Caption,HealthState,Name FROM VMware_Controller" + namespace: root/cimv2 + computes: + # Keep only lines with a valid HealthState + # Model,HeathState,Name + - type: keepOnlyMatchingLines + column: 2 + valueList: "5,15,20,25,30" + mapping: + # The Instance table + source: "${source::monitors.disk_controller.discovery.sources.source(1)}" + attributes: + id: $3 + controller_number: $3 + model: $1 + hw.parent.type: enclosure + name: "${awk::sprintf(\"Disk Controller: %s (%s)\", $3, $1)}" + conditionalCollection: + hw.status{hw.type="disk_controller"}: $2 + collect: + # The DiskController collect is a "MultiInstance" collect + # Only ONE WBEM query will be done in order to get the information for all disk controllers + # Less WBEM queries gives faster results + type: multiInstance + keys: + - id + sources: + source(1): + # Source(1) = VMWare_HHRCController + # HeathState,Name + # The info we wish to use in creating the Controller + type: wbem + query: "SELECT HealthState,Name FROM VMware_Controller" + namespace: root/cimv2 + computes: + # HeathState,Name + - type: duplicateColumn + column: 1 + # PatrolStatus,StatusInformation,Name + - type: translate + column: 1 + translationTable: "${translation::statusTranslationTable}" + # PatrolStatus,StatusInformation,Name + - type: translate + column: 2 + translationTable: "${translation::statusInformationTranslationTable}" + mapping: + # ValueTable = Source(1) + source: "${source::monitors.disk_controller.collect.sources.source(1)}" + attributes: + id: $3 + metrics: + hw.status{hw.type="disk_controller"}: $1 + legacyTextParameters: + StatusInformation: $2 + physical_disk: + discovery: + sources: + source(1): + # Source(1) = VMware_DiskDrive + # DeviceID,Caption,NumberOfBlocks,BlockSize + # The info we wish to use in creating the Controller + type: wbem + query: "SELECT DeviceID,Caption,NumberOfBlocks,BlockSize FROM VMware_StorageExtent" + namespace: root/cimv2 + computes: + # Compute the real size of the volumes (NumberofBlocks * BlockSize) + # DeviceID,Caption,SizeBytes,BlockSize + - type: multiply + column: 3 + value: $4 + mapping: + # The Instance table + # DeviceID,Caption,SizeBytes,BlockSize + source: "${source::monitors.physical_disk.discovery.sources.source(1)}" + attributes: + id: $1 + model: $2 + hw.parent.type: enclosure + name: "${awk::sprintf(\"%s (%s)\", $1, bytes2HumanFormatBase10($3))}" + metrics: + hw.physical_disk.size: $3 + collect: + # The DiskController collect is a "MultiInstance" collect + # Only ONE WBEM query will be done in order to get the information for all disk controllers + # Less WBEM queries gives faster results + type: multiInstance + keys: + - id + sources: + source(1): + # Source(1) = VMWare_HHRCController + # DeviceID,HealthState + # The info we wish to use in creating the Controller + type: wbem + query: "SELECT DeviceID,HealthState FROM VMware_StorageExtent" + namespace: root/cimv2 + computes: + # DeviceID,HealthState,HealthState + - type: duplicateColumn + column: 2 + # DeviceID,PatrolStatus,HealthState + - type: translate + column: 2 + translationTable: "${translation::statusTranslationTable}" + # DeviceID,PatrolStatus,StatusInformation + - type: translate + column: 3 + translationTable: "${translation::statusInformationTranslationTable}" + mapping: + # ValueTable = Source(1) + # DeviceID,PatrolStatus,StatusInformation + source: "${source::monitors.physical_disk.collect.sources.source(1)}" + attributes: + id: $1 + metrics: + hw.status{hw.type="physical_disk"}: $2 + legacyTextParameters: + StatusInformation: $3 + logical_disk: + discovery: + sources: + source(1): + # Source(1) = VMware_DiskDrive + # DeviceID,Caption,NumberOfBlocks,BlockSize + # The info we wish to use in creating the Controller + type: wbem + query: "SELECT DeviceID,Caption,NumberOfBlocks,BlockSize FROM VMware_StorageVolume" + namespace: root/cimv2 + computes: + # Compute the real size of the volumes (NumberofBlocks * BlockSize) + # DeviceID,Caption,SizeBytes,BlockSize + - type: multiply + column: 3 + value: $4 + mapping: + # The Instance table + # DeviceID,Caption,SizeBytes,BlockSize + source: "${source::monitors.logical_disk.discovery.sources.source(1)}" + attributes: + id: $1 + __display_id: $2 + hw.parent.type: enclosure + name: "${awk::sprintf(\"%s (%s)\", $2, bytes2HumanFormatBase2($3))}" + metrics: + hw.logical_disk.limit: $3 + collect: + # The DiskController collect is a "MultiInstance" collect + # Only ONE WBEM query will be done in order to get the information for all disk controllers + # Less WBEM queries gives faster results + type: multiInstance + keys: + - id + sources: + source(1): + # Source(1) = VMWare_HHRCController + # DeviceID,HealthState + # The info we wish to use in creating the Controller + type: wbem + query: "SELECT DeviceID,HealthState FROM VMware_StorageVolume" + namespace: root/cimv2 + computes: + # DeviceID,HealthState,HealthState + - type: duplicateColumn + column: 2 + # DeviceID,PatrolStatus,HealthState + - type: translate + column: 2 + translationTable: "${translation::statusTranslationTable}" + # DeviceID,PatrolStatus,StatusInformation + - type: translate + column: 3 + translationTable: "${translation::statusInformationTranslationTable}" + mapping: + # ValueTable = Source(1) + # DeviceID,PatrolStatus,StatusInformation + source: "${source::monitors.logical_disk.collect.sources.source(1)}" + attributes: + id: $1 + metrics: + hw.status{hw.type="logical_disk"}: $2 + legacyTextParameters: + StatusInformation: $3 +translations: + statusInformationTranslationTable: + "0": Unknown + "25": Critical Failure + "15": Minor Failure + "5": "" + "30": Non-Recoverable Error + Default: Unknown Status + "20": Major Failure + "10": Degraded + statusTranslationTable: + "0": UNKNOWN + "25": failed + "15": degraded + "5": ok + "30": failed + Default: UNKNOWN + "20": failed + "10": degraded diff --git a/src/it/metricshub-connectors/src/main/connector/hardware/Virsh/Virsh.yaml b/src/it/metricshub-connectors/src/main/connector/hardware/Virsh/Virsh.yaml new file mode 100644 index 0000000..e7c0ffa --- /dev/null +++ b/src/it/metricshub-connectors/src/main/connector/hardware/Virsh/Virsh.yaml @@ -0,0 +1,69 @@ +--- +extends: +- ../Hardware/Hardware +connector: + displayName: "KVM, QEMU, Xen and Hypervisors (virsh)" + platforms: "KVM, QEMU, Xen, Hypervisors" + reliesOn: libvirt API + version: 1.0 + information: This connector provides VM monitoring through the virsh command. The host requires the libvirt API installed. + detection: + connectionTypes: + - remote + - local + appliesTo: + - Linux + criteria: + - type: productRequirements + kmVersion: 11.3.00 + - type: osCommand + commandLine: virsh list + expectedResult: Name +monitors: + vm: + discovery: + sources: + source(1): + type: osCommand + commandLine: "virsh list | awk '{print $$1}' | grep -oIE \"[0-9]*\" | while read word; do virsh dominfo ${word} ; done" + computes: + - type: awk + script: "${file::embeddedFile-1}" + mapping: + source: "${source::monitors.vm.discovery.sources.source(1)}" + attributes: + id: $1 + __display_id: $1 + vm.host.name: $1 + info: $4 + hw.parent.type: enclosure + name: "${awk::sprintf(\"%s (%s)\", $1, $1)}" + conditionalCollection: + hw.power{hw.type="vm"}: 0 + collect: + type: multiInstance + keys: + - id + sources: + source(1): + type: osCommand + commandLine: "virsh list | awk '{print $$1}' | grep -oIE \"[0-9]*\" | while read word; do virsh dominfo ${word} ; done" + computes: + - type: awk + script: "${file::embeddedFile-1}" + - type: translate + column: 2 + translationTable: "${translation::stateTranslationTable}" + mapping: + source: "${source::monitors.vm.collect.sources.source(1)}" + attributes: + id: $1 + metrics: + hw.power_state{hw.type="vm"}: $2 + hw.vm.power_ratio: computePowerShareRatio($3) +translations: + stateTranslationTable: + running: "on" + paused: "suspended" + default: "off" + suspended: "suspended" diff --git a/src/it/metricshub-connectors/src/main/connector/hardware/Virsh/embeddedFile-1 b/src/it/metricshub-connectors/src/main/connector/hardware/Virsh/embeddedFile-1 new file mode 100644 index 0000000..8fb3e50 --- /dev/null +++ b/src/it/metricshub-connectors/src/main/connector/hardware/Virsh/embeddedFile-1 @@ -0,0 +1,10 @@ +BEGIN { FS = ": "; vmName = ""; UUID = ""; powerState = ""; powerShare = ""} + +/Name/ {vmName = $2; gsub(/ /,"",vmName)} +/UUID/ {uuid = $2; gsub(/ /,"",uuid)} +/State/ {powerState = $2; gsub(/ /,"",powerState)} +/CPU\(s\)/ { + powerShare = $2; gsub(/ /,"",powerShare); + print vmName ";" powerState ";" powerShare ";UUID: " uuid; + vmName = ""; powerState = ""; powerShare = ""; uuid = ""; +} diff --git a/src/it/metricshub-connectors/src/main/connector/hardware/WBEMGenDiskNT/WBEMGenDiskNT.yaml b/src/it/metricshub-connectors/src/main/connector/hardware/WBEMGenDiskNT/WBEMGenDiskNT.yaml new file mode 100644 index 0000000..1115691 --- /dev/null +++ b/src/it/metricshub-connectors/src/main/connector/hardware/WBEMGenDiskNT/WBEMGenDiskNT.yaml @@ -0,0 +1,236 @@ +--- +extends: +- ../Hardware/Hardware +connector: + displayName: WMI - Disks + platforms: Any system + reliesOn: WMI + version: 1.1 + information: This connector provides monitoring of the S.M.A.R.T.-enabled disks that are directly handled by Windows (and WBEM through the WMI service). It tries to exclude disks that are actually logical disks exposed by some RAID controllers. + detection: + connectionTypes: + - remote + - local + appliesTo: + - NT + criteria: + - type: deviceType + keep: + - NT + - type: service + name: WINMGMT + - type: wmi + namespace: root\WMI + query: SELECT InstanceName FROM MSStorageDriver_FailurePredictStatus WHERE Active=TRUE + expectedResult: "\\(^[^M]...\\)\\|\\(^.[^P]..\\)\\|\\(^..[^I].\\)\\|\\(^...[^O]\\)" +monitors: + disk_controller: + discovery: + sources: + source(1): + # Source(1) = list of the disks that supports S.M.A.R.T. and that are directly + # exposed through the WMI WBEM provider + # InstanceName; + type: wmi + namespace: root\WMI + query: SELECT InstanceName FROM MSStorageDriver_FailurePredictStatus WHERE Active=TRUE + source(2): + # Source(2) = table that translate the InstanceName of the WMI provider into a PnPDeviceID + # PnPDeviceID;InstanceNameList + type: wmi + namespace: root\WMI + query: "SELECT InstanceName,InstanceNameList FROM MSWMI_PnPInstanceNames" + computes: + # Extract the first instance name within the instancenamelist + # PnPDeviceID;InstanceName + - type: extract + column: 2 + subColumn: 1 + subSeparators: '|' + source(3): + # Source(3) = jointure of source(1) and source(2) in order to have the PnPDeviceID + # of the disks that supports S.M.A.R.T. + # InstanceName;PnPDeviceID;InstanceName + type: tableJoin + leftTable: "${source::monitors.disk_controller.discovery.sources.source(1)}" + rightTable: "${source::monitors.disk_controller.discovery.sources.source(2)}" + leftKeyColumn: 1 + rightKeyColumn: 2 + source(4): + # Source(4) = Table that links the PnP Devices (who controls who) + # Antecedent;Dependent (that will contain ControllerPnPDeviceID;DiskPnPDeviceID) + type: wmi + query: "SELECT Antecedent,Dependent FROM CIM_ControlledBy" + computes: + - type: extract + column: 1 + subColumn: 2 + subSeparators: '"' + - type: extract + column: 2 + subColumn: 2 + subSeparators: '"' + - type: replace + column: 1 + existingValue: \\ + newValue: \ + - type: replace + column: 2 + existingValue: \\ + newValue: \ + source(5): + # Source(5) = jointure of source(4) with source(3) + # ControllerPnPDeviceID;DiskPnPDeviceID;DiskInstanceName;DiskPnPDeviceID;DiskInstanceName + type: tableJoin + leftTable: "${source::monitors.disk_controller.discovery.sources.source(4)}" + rightTable: "${source::monitors.disk_controller.discovery.sources.source(3)}" + leftKeyColumn: 2 + rightKeyColumn: 2 + computes: + # Exclude disks that have a PNP ID that begins with MPIO (Multipath IO Disks can only occur in external Disks Bays), or contain PROD_LOGICAL_VOLUME (HP Raid Logical Disks) + # ControllerPnPDeviceID;DiskPnPDeviceID;DiskInstanceName;DiskPnPDeviceID;DiskInstanceName + - type: excludeMatchingLines + column: 2 + regExp: \(^MPIO\)\|\(PROD_LOGICAL_VOLUME\) + source(6): + # Source(6) = the CIM_ControllerTable (more information about them) + # Name;PnPDeviceID + type: wmi + query: "SELECT Name,PNPDeviceID FROM CIM_Controller" + computes: + # Remove HBA controllers (Emulex, QLogic, etc.) + # Name;PnPDeviceID + - type: excludeMatchingLines + column: 1 + regExp: \(Emulex\)\|\(QLogic\)\|\(EMS Device Bus\) + source(7): + # Source(7) = jointure of source(6) and source(5) (will contain only one line per disk controller) + # ControllerName;ControllerPnPDeviceID;ControllerPnPDeviceID;DiskPnPDeviceID;DiskInstanceName;DiskInstanceName;DiskPnPDeviceID + type: tableJoin + leftTable: "${source::monitors.disk_controller.discovery.sources.source(6)}" + rightTable: "${source::monitors.disk_controller.discovery.sources.source(5)}" + leftKeyColumn: 2 + rightKeyColumn: 1 + mapping: + # Pfew... that's end, folks! here is the InstanceTable: Source(7) + source: "${source::monitors.disk_controller.discovery.sources.source(7)}" + attributes: + id: $2 + controller_number: $2 + vendor: "" + model: $1 + hw.parent.type: enclosure + name: "${awk::sprintf(\"Disk Controller: %s (%s %s)\", $2, \"\", $1)}" + physical_disk: + discovery: + sources: + source(1): + # Source(1) = the Win32_DiskDrive table (more information about those disks) + # DiskIndex;DiskManufacturer;DiskModel;DiskPNPDeviceID;DiskSize; + type: wmi + query: "SELECT Index,Manufacturer,Model,PNPDeviceID,Size FROM Win32_DiskDrive" + source(2): + # Source(2) = jointure of DiskController.Discovery.Source(5) with Source(1) + # ControllerPnPDeviceID;DiskPnPDeviceID;DiskInstanceName;DiskPnPDeviceID;DiskInstanceName;DiskIndex;DiskManufacturer;DiskModel;DiskPNPDeviceID;DiskSize; + type: tableJoin + leftTable: "${source::monitors.disk_controller.discovery.sources.source(5)}" + rightTable: "${source::monitors.physical_disk.discovery.sources.source(1)}" + leftKeyColumn: 2 + rightKeyColumn: 4 + source(3): + # Source(3) = jointure of Source(2) with DiskController.Discovery.Source(6) to keep only + # disks attached to actual disk controllers that are not HBAs + # ControllerPnPDeviceID;DiskPnPDeviceID;DiskInstanceName;DiskPnPDeviceID;DiskInstanceName;DiskIndex;DiskManufacturer;DiskModel;DiskPNPDeviceID;DiskSize;ControllerName;ControllerPnPDeviceID; + type: tableJoin + leftTable: "${source::monitors.physical_disk.discovery.sources.source(2)}" + rightTable: "${source::monitors.disk_controller.discovery.sources.source(6)}" + leftKeyColumn: 1 + rightKeyColumn: 2 + mapping: + # The instance table... + source: "${source::monitors.physical_disk.discovery.sources.source(3)}" + attributes: + id: $3 + __display_id: $6 + vendor: $8 + hw.parent.type: disk_controller + hw.parent.id: $1 + name: "${awk::sprintf(\"%s (%s - %s)\", $6, $8, bytes2HumanFormatBase10($10))}" + metrics: + hw.physical_disk.size: $10 + collect: + # This a multi-instance collect (please minimize the number of wbem queries...) + type: multiInstance + keys: + - id + sources: + source(1): + # Source(1) = the Win32_DiskDrive table + # PnPDeviceID;Status + type: wmi + query: "SELECT PnPDeviceID,Status FROM Win32_DiskDrive" + source(2): + # Source(2) = jointure of Source(1) with DiskController.Discovery.Source(2) + # in order to link PnPDeviceID with InstanceName in the WMI namespace + # PnPDeviceID;Status;PnPDeviceID;InstanceName + type: tableJoin + leftTable: "${source::monitors.physical_disk.collect.sources.source(1)}" + rightTable: "${source::monitors.disk_controller.discovery.sources.source(2)}" + leftKeyColumn: 1 + rightKeyColumn: 1 + source(3): + # Source(3) = the list of the disks that supports S.M.A.R.T. and that are directly + # exposed through the WMI WBEM provider + # InstanceName;PredictFailure + type: wmi + namespace: root\WMI + query: "SELECT InstanceName,PredictFailure FROM MSStorageDriver_FailurePredictStatus WHERE Active=TRUE" + source(4): + # Source(4) = jointure of source(2) with source(3) (to have all information + # in the same table + # PnPDeviceID;Status;PnPDeviceID;InstanceName;InstanceName;PredictFailure + type: tableJoin + leftTable: "${source::monitors.physical_disk.collect.sources.source(2)}" + rightTable: "${source::monitors.physical_disk.collect.sources.source(3)}" + leftKeyColumn: 4 + rightKeyColumn: 1 + computes: + # Duplicate the Status column (to be translated two times) + # PnPDeviceID;Status;PatrolStatus;InformationStatus;PnPDeviceID;InstanceName;InstanceName;PredictFailure + - type: duplicateColumn + column: 2 + - type: duplicateColumn + column: 2 + # Translate the WBEM Status field into PATROL status + - type: translate + column: 3 + translationTable: "${translation::PhysicalDiskStatusTranslationTable}" + # Translate the WBEM Status field into a more readable string + - type: translate + column: 4 + translationTable: "${translation::PhysicalDiskInformationStatusTranslationTable}" + mapping: + # And here is the ValueTable + source: "${source::monitors.physical_disk.collect.sources.source(4)}" + attributes: + id: $6 + metrics: + hw.status{hw.type="physical_disk"}: $3 + hw.status{hw.type="physical_disk", state="predicted_failure"}: boolean($8) + legacyTextParameters: + StatusInformation: $4 +translations: + PhysicalDiskInformationStatusTranslationTable: + Degraded: Degraded + Error: Error + Service: Service + Pred Fail: Predicted Failure + OK: "" + Default: Unknown + PhysicalDiskStatusTranslationTable: + Degraded: degraded + Error: failed + Service: degraded + Pred Fail: degraded + OK: ok + Default: UNKNWON diff --git a/src/it/metricshub-connectors/src/main/connector/hardware/WBEMGenHBA/WBEMGenHBA.yaml b/src/it/metricshub-connectors/src/main/connector/hardware/WBEMGenHBA/WBEMGenHBA.yaml new file mode 100644 index 0000000..087c037 --- /dev/null +++ b/src/it/metricshub-connectors/src/main/connector/hardware/WBEMGenHBA/WBEMGenHBA.yaml @@ -0,0 +1,247 @@ +--- +extends: +- ../Hardware/Hardware +connector: + displayName: WMI - HBA + platforms: Any system + reliesOn: WMI + version: 0.9 + information: This connector provides the monitoring of HBA cards on all Windows-based systems through the WMI layer (root/WMI namespace). + detection: + connectionTypes: + - remote + - local + appliesTo: + - NT + supersedes: + - CpqHBA + - SMISHBA + criteria: + # OS must be Windows NT-derivative + - type: deviceType + keep: + - NT + # WMI must be available + - type: service + name: WINMGMT + # The root\wmi namespace must have the MSFC_FCAdapterHBAAttributes + - type: wmi + query: SELECT InstanceName FROM MSFC_FCAdapterHBAAttributes + namespace: root\wmi +monitors: + network: + discovery: + sources: + source(1): + # Source(1) = MSFC_FibrePortHBAAttributes class (FC Ports) + # Active;AdapterID;UniquePortId;PortSpeed;PortWWN-Decimal;PortWWN-Decimal;PortType; + type: wmi + namespace: root\wmi + query: "SELECT Active,InstanceName,Attributes.PortWWN,Attributes.PortSpeed,Attributes.PortWWN,Attributes.PortWWN,Attributes.PortType FROM MSFC_FibrePortHBAAttributes" + computes: + # Keep Only Active Ports + # Active;AdapterID;UniquePortId;PortSpeed;PortWWN-Decimal;PortWWN-Decimal;PortType; + - type: keepOnlyMatchingLines + column: 1 + valueList: "True" + # Translate PortType into a more readable string + # Active;AdapterID;UniquePortId;PortSpeed;PortWWN-Decimal;PortWWN-Decimal;PortType; + - type: translate + column: 7 + translationTable: "${translation::PortTypeTranslationTable}" + # and Remove the array pipes from the port WWN + # Active;AdapterID;UniquePortId;PortSpeed;PortWWN-Decimal;PortWWN-Decimal;PortType; + - type: replace + column: 5 + existingValue: '|' + newValue: . + # Convert Speed from Gbps to Mbps (multiply by 1000) + # Active;AdapterID;UniquePortId;PortSpeed;PortWWN-Decimal;PortWWN-Decimal;PortType; + - type: multiply + column: 4 + value: 1000 + # Convert PortWWN to Hex + # NOTE - This script is column specific (See Column Variable), so watchout if you change the queries or duplicate columns. + # Active;AdapterID;UniquePortId;PortSpeed;PortWWN-Decimal;PortWWN-Hex;PortType; + - type: awk + script: "${file::embeddedFile-1}" + keep: ^MSHW; + separators: ; + selectColumns: "2,3,4,5,6,7,8" + source(2): + # Source(2) = MSFC_FCAdapterHBAAttributes class (FC Adapters) + # AdapterID;Manufacturer;Model;Serial + type: wmi + namespace: root\wmi + query: "SELECT InstanceName,Manufacturer,ModelDescription,SerialNumber FROM MSFC_FCAdapterHBAAttributes" + computes: + # Keep only the first word of the Manufacturer name (to avoid stuff like "Corporations, Inc. and blah blah blah) + # AdapterID;Manufacturer;Model;Serial + - type: extract + column: 2 + subColumn: 1 + subSeparators: ' ' + source(3): + # Source(3) = Table joint of Source(1) and Source(2) + # Active;AdapterID;UniquePortId;PortSpeed;PortWWN-Decimal;PortWWN-Hex;PortType;AdapterID;Manufacturer;Model;Serial + type: tableJoin + leftTable: "${source::monitors.network.discovery.sources.source(1)}" + rightTable: "${source::monitors.network.discovery.sources.source(2)}" + leftKeyColumn: 2 + rightKeyColumn: 1 + source(4): + # Source (4) = MSFC_FibrePortHBAStatistics + # InstanceName;TxFrames;RxFrames;ErrorCount + type: wmi + namespace: root\wmi + query: "SELECT InstanceName,statistics.TxFrames,statistics.RxFrames,statistics.ErrorFrames FROM MSFC_FibrePortHBAStatistics" + computes: + # Remove all values of -1 from Tx,Rx and ErrorCount to deactivate these parameters when they are not collected + # InstanceName;TxFrames;RxFrames;ErrorCount + - type: replace + column: 2 + existingValue: -1 + newValue: "" + - type: replace + column: 3 + existingValue: -1 + newValue: "" + - type: replace + column: 4 + existingValue: -1 + newValue: "" + source(5): + # Source(5) = Table joint of Source(3) and Source(4) + # Active;AdapterID;UniquePortId;PortSpeed;PortWWN-Decimal;PortWWN-Hex;PortType;AdapterID;Manufacturer;Model;Serial;InstanceName;TxFrames;RxFrames;ErrorCount; + type: tableJoin + leftTable: "${source::monitors.network.discovery.sources.source(3)}" + rightTable: "${source::monitors.network.discovery.sources.source(4)}" + leftKeyColumn: 2 + rightKeyColumn: 1 + mapping: + # Changed from UniquePortID to PortWWN as device ID as it seems this UniqueID is not fixed. + # InstanceTable = Source(3) + # Active;AdapterID;UniquePortId;PortSpeed;PortWWN-Decimal;PortWWN-Hex;PortType;AdapterID;Manufacturer;Model;Serial;InstanceName;TxFrames;RxFrames;ErrorCount; + source: "${source::monitors.network.discovery.sources.source(5)}" + attributes: + id: $5 + vendor: $9 + model: $10 + bandwidth: $4 + physical_address: $6 + physical_address_type: WWN + device_type: $7 + hw.parent.type: enclosure + name: "${awk::sprintf(\"%s (%s - %s - %s)\", $5, $7, $9, $10)}" + conditionalCollection: + hw.errors{hw.type="network"}: $15 + hw.network.packets{direction="transmit"}: $13 + hw.network.packets{direction="receive"}: $14 + collect: + # Collect type is multi-instance + type: multiInstance + keys: + - id + sources: + source(1): + # Source (1) = MSFC_FibrePortHBAAttributes + # AdapterID;PortState;PortSpeed;PortWWN-Decimal; + type: wmi + namespace: root\wmi + query: "SELECT InstanceName,Attributes.PortState,Attributes.PortSpeed,Attributes.PortWWN FROM MSFC_FibrePortHBAAttributes" + computes: + # Duplicate Port State Twice + # AdapterID;PortState;PortSpeed;PortWWN-Decimal; + - type: duplicateColumn + column: 2 + - type: duplicateColumn + column: 2 + # Translate PortState to Patrol Status + # AdapterID;Status;PortState;PortState;PortSpeed;PortWWN-Decimal; + - type: translate + column: 2 + translationTable: "${translation::PortStateToStatusTranslationTable}" + # Translate PortState to Patrol Status + # AdapterID;Status;StatusInformation;PortState;PortSpeed;PortWWN-Decimal; + - type: translate + column: 3 + translationTable: "${translation::PortStateToStatusInformationTranslationTable}" + # Translate PortState to Link Status + # AdapterID;Status;StatusInformation;LinkStatus;PortSpeed;PortWWN-Decimal; + - type: translate + column: 4 + translationTable: "${translation::PortStateToLinkStatusTranslationTable}" + # Convert Speed from Gbps to Mbps (multiply by 1000) + # AdapterID;Status;StatusInformation;LinkStatus;PortSpeed;PortWWN-Decimal; + - type: multiply + column: 5 + value: 1000 + # Add a FC_ to the Port ID to prevent it from being identified as a Integer + # Remove the array pipes from the port WWN + # AdapterID;Status;StatusInformation;LinkStatus;PortSpeed;PortWWN-Decimal; + - type: replace + column: 6 + existingValue: '|' + newValue: . + source(2): + # Source (2) = MSFC_FibrePortHBAStatistics + # InstanceName;TxFrames;RxFrames;ErrorCount + type: wmi + namespace: root\wmi + query: "SELECT InstanceName,statistics.TxFrames,statistics.RxFrames,statistics.ErrorFrames FROM MSFC_FibrePortHBAStatistics" + source(3): + # Source (3) = Table Joint of 1 & 2 + # AdapterID;Status;StatusInformation;LinkStatus;PortSpeed;PortWWN-Decimal;InstanceName;TxFrames;RxFrames;ErrorCount; + type: tableJoin + leftTable: "${source::monitors.network.collect.sources.source(1)}" + rightTable: "${source::monitors.network.collect.sources.source(2)}" + leftKeyColumn: 1 + rightKeyColumn: 1 + mapping: + # INSTANCE TABLE + # AdapterID;Status;StatusInformation;LinkStatus;PortSpeed;PortWWN-Decimal;InstanceName;TxFrames;RxFrames;ErrorCount; + source: "${source::monitors.network.collect.sources.source(3)}" + attributes: + id: $6 + metrics: + hw.status{hw.type="network"}: $2 + hw.network.up: legacyLinkStatus($4) + hw.network.bandwidth.limit: megaBit2Bit($5) + hw.errors{hw.type="network"}: $10 + hw.network.packets{direction="transmit"}: $8 + hw.network.packets{direction="receive"}: $9 + legacyTextParameters: + StatusInformation: $3 +translations: + PortStateToLinkStatusTranslationTable: + "2": ok + "6": degraded + Default: UNKNOWN + PortStateToStatusInformationTranslationTable: + "1": Unknown + "2": "" + "3": User Offline + "4": Bypassed + "5": In diagnostics mode + "6": Link Down + "7": Port Error + "8": Loopback + Default: UNKNOWN + PortTypeTranslationTable: + "5": HBA FC Port N + "6": HBA FC Port NL + "7": HBA FC Port F/NL + "8": HBA FC Port F + "9": HBA FC Port E + Default: HBA FC Port + "10": HBA FC Port G + PortStateToStatusTranslationTable: + "1": UNKNOWN + "2": ok + "3": degraded + "4": ok + "5": degraded + "6": ok + "7": failed + "8": degraded + Default: UNKNOWN diff --git a/src/it/metricshub-connectors/src/main/connector/hardware/WBEMGenHBA/embeddedFile-1 b/src/it/metricshub-connectors/src/main/connector/hardware/WBEMGenHBA/embeddedFile-1 new file mode 100644 index 0000000..8f8065c --- /dev/null +++ b/src/it/metricshub-connectors/src/main/connector/hardware/WBEMGenHBA/embeddedFile-1 @@ -0,0 +1,20 @@ +BEGIN { + FS = "[;]" + Column=6 +} +{ + n = split($Column, PortWWN, "|"); + printf "MSHW;" + for (i = 1 ; i <= NF-1 ; i++) { + if (i == Column) { + for (j = 1 ; j <= n-1 ; j++) { + printf "%02X", PortWWN[j] + } + printf ";" + } else { + printf $i + printf ";" + } + } + print "" +} \ No newline at end of file diff --git a/src/it/metricshub-connectors/src/main/connector/hardware/WBEMGenLUN/WBEMGenLUN.yaml b/src/it/metricshub-connectors/src/main/connector/hardware/WBEMGenLUN/WBEMGenLUN.yaml new file mode 100644 index 0000000..e3a2412 --- /dev/null +++ b/src/it/metricshub-connectors/src/main/connector/hardware/WBEMGenLUN/WBEMGenLUN.yaml @@ -0,0 +1,201 @@ +--- +extends: +- ../Hardware/Hardware +connector: + displayName: WMI - LUN + platforms: Any system + reliesOn: WMI + version: 0.9 + information: This connector provides the monitoring of LUNs on all Windows-based systems through the WMI layer (root/WMI namespace). + detection: + connectionTypes: + - remote + - local + appliesTo: + - NT + criteria: + # OS must be Windows NT-derivative + - type: deviceType + keep: + - NT + # WMI must be available + - type: service + name: WINMGMT + # The root\wmi namespace must have the MPIO_DISK_INFO + - type: wmi + query: "SELECT NumberDrives,DriveInfo.Name FROM MPIO_DISK_INFO" + namespace: root\wmi + expectedResult: "^[1-9][0-9]*;[^;].*;$" +monitors: + lun: + discovery: + sources: + source(1): + # Source (1) = MPIO_DISK_INFO + # LunIdArray;LunPathCount;LunNaaID; + type: wmi + namespace: root\wmi + query: "SELECT DriveInfo.Name,DriveInfo.NumberPaths,DriveInfo.SerialNumber FROM MPIO_DISK_INFO" + computes: + # Take the list of volumes and associated them to the MPIO disk. + # MPIOID;LunPathCount;NAAID; + - type: awk + script: "${file::embeddedFile-1}" + keep: ^MSHW; + separators: ; + selectColumns: "2,3,4" + source(2): + # Source (2) = Win32_LogicalDisk + # Win32_LogicalDisk;PartitionLetter;PartitionName; + type: wmi + namespace: root\cimv2 + query: "SELECT __PATH,Name,VolumeName FROM Win32_LogicalDisk" + source(3): + # Source (3) = Win32_LogicalDiskToPartition + # Win32_DiskPartition;Win32_LogicalDisk; + type: wmi + namespace: root\cimv2 + query: "SELECT Antecedent,Dependent FROM Win32_LogicalDiskToPartition" + source(4): + # Source (4) = Win32_LogicalDiskToPartition + # Win32_DiskDrive;Win32_DiskPartition; + type: wmi + namespace: root\cimv2 + query: "SELECT Antecedent,Dependent FROM Win32_DiskDriveToDiskPartition" + source(5): + # Source 5 = Win32_DiskDrive + # Win32_DiskDrive;PhysicalDiskName;PnPAddress; + type: wmi + namespace: root\cimv2 + query: "SELECT __PATH,Name,PNPDeviceID FROM Win32_DiskDrive" + computes: + # Add a _0 to the Win32_DiskDrive PNPAddress so that it matches + # Win32_DiskDrive;PhysicalDiskName;PnPAddress; + - type: rightConcat + column: 3 + value: _0 + source(6): + # Source 6 = MPIO_GET_DESCRIPTOR + # LunId(WithDevice);PnPAddress; + type: wmi + namespace: root\wmi + query: "SELECT DeviceName,InstanceName FROM MPIO_GET_DESCRIPTOR" + computes: + # To upper the PnPAddress + # LunId(WithDevice);PnPAddress; + - type: awk + script: "${file::embeddedFile-3}" + keep: ^MSHW; + separators: ; + selectColumns: "2,3" + source(7): + # Source 7 = 2 Joined to 3 with Win32_LogicalDisk + # Win32_LogicalDisk;PartitionLetter;PartitionName;Win32_DiskPartition;Win32_LogicalDisk; + type: tableJoin + leftTable: "${source::monitors.lun.discovery.sources.source(2)}" + rightTable: "${source::monitors.lun.discovery.sources.source(3)}" + leftKeyColumn: 1 + rightKeyColumn: 2 + keyType: Wbem + source(8): + # Source 8 = 7 Joined to 4 with Win32_DiskPartition + # 1 # 2 # 3 # 4 # 5 # 6 # 7 + # Win32_LogicalDisk;PartitionLetter;PartitionName;Win32_DiskPartition;Win32_LogicalDisk;Win32_DiskDrive;Win32_DiskPartition; + type: tableJoin + leftTable: "${source::monitors.lun.discovery.sources.source(7)}" + rightTable: "${source::monitors.lun.discovery.sources.source(4)}" + leftKeyColumn: 4 + rightKeyColumn: 2 + keyType: Wbem + source(9): + # Source 9 = 8 Joined to 5 with Win32_DiskDrive + # 1 # 2 # 3 # 4 # 5 # 6 # 7 # 8 # 9 10 + # Win32_LogicalDisk;PartitionLetter;PartitionName;Win32_DiskPartition;Win32_LogicalDisk;Win32_DiskDrive;Win32_DiskPartition;Win32_DiskDrive;PhysicalDiskName;PnPAddress; + type: tableJoin + leftTable: "${source::monitors.lun.discovery.sources.source(8)}" + rightTable: "${source::monitors.lun.discovery.sources.source(5)}" + leftKeyColumn: 6 + rightKeyColumn: 1 + keyType: Wbem + source(10): + # Source 10 = 9 Joined to 5 with PnPAddress + # 1 # 2 # 3 # 4 # 5 # 6 # 7 # 8 # 9 10 11 # 12 + # Win32_LogicalDisk;PartitionLetter;PartitionName;Win32_DiskPartition;Win32_LogicalDisk;Win32_DiskDrive;Win32_DiskPartition;Win32_DiskDrive;PhysicalDiskName;PnPAddress;LunId(WithDevice);PnPAddress; + type: tableJoin + leftTable: "${source::monitors.lun.discovery.sources.source(9)}" + rightTable: "${source::monitors.lun.discovery.sources.source(6)}" + leftKeyColumn: 10 + rightKeyColumn: 2 + computes: + # Trim down to what we need + # FROM 1 # 2 3 # 4 # 5 # 6 # 7 # 8 # 9 10 11 12 + # Win32_LogicalDisk;PartitionLetter;PartitionName;Win32_DiskPartition;Win32_LogicalDisk;Win32_DiskDrive;Win32_DiskPartition;Win32_DiskDrive;PhysicalDiskName;PnPAddress;LunId(WithDevice);PnPAddress; + # TO + # PartitionLetter;PartitionName;LunId(WithDevice); + - type: keepColumns + columnNumbers: "2,3,11" + # Take the list of volumes and associated them to the MPIO disk. + # MPIOID;VolumeList; + - type: awk + script: "${file::embeddedFile-2}" + keep: ^MSHW; + separators: ; + selectColumns: "2,3" + source(11): + # Join the MPIO Discovered list with the Volume List + # MPIOID;LunPathCount;NAAID;MPIOID;VolumeList; + type: tableJoin + leftTable: "${source::monitors.lun.discovery.sources.source(1)}" + rightTable: "${source::monitors.lun.discovery.sources.source(10)}" + leftKeyColumn: 1 + rightKeyColumn: 1 + defaultRightLine: ;No Volumes; + computes: + # Right concat " - " to the first MPIO ID + # MPIOID;LunPathCount;NAAID;MPIOID;VolumeList; + - type: rightConcat + column: 1 + value: ' - ' + # Right concat the volume list to the first MPIO ID + # MPIOID-VolumeList;LunPathCount;NAAID;MPIOID;VolumeList; + - type: rightConcat + column: 1 + value: $5 + mapping: + # InstanceTable = Source(1) + # MPIOID-VolumeList;LunPathCount;NAAID;MPIOID;VolumeList; + source: "${source::monitors.lun.discovery.sources.source(11)}" + attributes: + id: $3 + __display_id: $3 + local_device_name: $1 + hw.parent.type: enclosure + name: "${awk::sprintf(\"%s (%s)\", $3, $1)}" + collect: + # Collect type is multi-instance + type: multiInstance + keys: + - id + sources: + source(1): + # Source (1) = MPIO_DISK_INFO + # LunIdArray;LunPathCount;LunNaaID; + type: wmi + namespace: root\wmi + query: "SELECT DriveInfo.Name,DriveInfo.NumberPaths,DriveInfo.SerialNumber FROM MPIO_DISK_INFO" + computes: + # Take the list of volumes and associated them to the MPIO disk. + # MPIOID;LunPathCount;NAAID; + - type: awk + script: "${file::embeddedFile-1}" + keep: ^MSHW; + separators: ; + selectColumns: "2,3,4" + mapping: + # ValueTable = Source(1) + # LUNID;NumberPaths + source: "${source::monitors.lun.collect.sources.source(1)}" + attributes: + id: $3 + metrics: + hw.lun.paths: $2 diff --git a/src/it/metricshub-connectors/src/main/connector/hardware/WBEMGenLUN/embeddedFile-1 b/src/it/metricshub-connectors/src/main/connector/hardware/WBEMGenLUN/embeddedFile-1 new file mode 100644 index 0000000..b9b28c0 --- /dev/null +++ b/src/it/metricshub-connectors/src/main/connector/hardware/WBEMGenLUN/embeddedFile-1 @@ -0,0 +1,9 @@ +BEGIN {FS="[;]"} + {split($1,LunIdArray,"[|]") ; + split($2,LunPathCountArray,"[|]") ; + split($3,LunNaaIDArray,"[|]") ; + } + +END {for (MPIOID in LunIdArray) + if ( LunIdArray[MPIOID] != "" ) {print ("MSHW;" LunIdArray[MPIOID] ";" LunPathCountArray[MPIOID] ";" "naa." LunNaaIDArray[MPIOID] ";" ) } + } \ No newline at end of file diff --git a/src/it/metricshub-connectors/src/main/connector/hardware/WBEMGenLUN/embeddedFile-2 b/src/it/metricshub-connectors/src/main/connector/hardware/WBEMGenLUN/embeddedFile-2 new file mode 100644 index 0000000..763b346 --- /dev/null +++ b/src/it/metricshub-connectors/src/main/connector/hardware/WBEMGenLUN/embeddedFile-2 @@ -0,0 +1,10 @@ +BEGIN {FS="[;]"} +$3 ~ /.Device.MPIO/ { DriveLetter = $1 + DriveName = $2 + MPIOID = $3 ; gsub (".*MPIO","MPIO ",MPIOID) + MPIOInfo[MPIOID] = MPIOInfo[MPIOID] DriveLetter " (" DriveName ") " + } +END { for (MPIOID in MPIOInfo) { + if ( MPIOInfo[MPIOID] != "" ) {print ("MSHW;" MPIOID ";" MPIOInfo[MPIOID] ";" ) } + } + } \ No newline at end of file diff --git a/src/it/metricshub-connectors/src/main/connector/hardware/WBEMGenLUN/embeddedFile-3 b/src/it/metricshub-connectors/src/main/connector/hardware/WBEMGenLUN/embeddedFile-3 new file mode 100644 index 0000000..dc97c1c --- /dev/null +++ b/src/it/metricshub-connectors/src/main/connector/hardware/WBEMGenLUN/embeddedFile-3 @@ -0,0 +1,2 @@ +BEGIN {FS="[;]"} +$1 ~ /.Device.MPIO/ { print ( "MSHW;" $1 ";" toupper($2) ";") } \ No newline at end of file diff --git a/src/it/metricshub-connectors/src/main/connector/hardware/WBEMGenNetwork/WBEMGenNetwork.yaml b/src/it/metricshub-connectors/src/main/connector/hardware/WBEMGenNetwork/WBEMGenNetwork.yaml new file mode 100644 index 0000000..4f53feb --- /dev/null +++ b/src/it/metricshub-connectors/src/main/connector/hardware/WBEMGenNetwork/WBEMGenNetwork.yaml @@ -0,0 +1,314 @@ +--- +extends: +- ../Hardware/Hardware +connector: + displayName: WMI - Network + platforms: Any system + reliesOn: WMI + version: 1.3 + information: This connector provides the monitoring of network cards on all Windows-based systems through the WMI layer (root/WMI namespace). + detection: + connectionTypes: + - remote + - local + appliesTo: + - NT + criteria: + # OS must be Windows NT-derivative + - type: deviceType + keep: + - NT + # WMI must be available + - type: service + name: WINMGMT + # The root\wmi namespace must have the MSNdis_MediaConnectStatus + - type: wmi + query: SELECT InstanceName FROM MSNdis_MediaConnectStatus + namespace: root\wmi + # There must be at least one Ethernet network adapter + - type: wmi + query: SELECT AdapterType FROM Win32_NetworkAdapter + namespace: root\cimv2 + expectedResult: ^Ethernet 802\.3;$ +monitors: + network: + discovery: + sources: + source(1): + # Source(1) = Win32_NetworkAdapter WMI class + # AdapterType;MACAddress;Name;PNPDeviceID; + type: wmi + namespace: root\cimv2 + query: "SELECT AdapterType,MACAddress,Name,PNPDeviceID FROM Win32_NetworkAdapter" + computes: + # Keep only Ethernet cards + # AdapterType;MACAddress;Name;PNPDeviceID; + - type: keepOnlyMatchingLines + column: 1 + regExp: ^Ethernet 802\.3$ + # Avoid any network interface whose PnPDeviceID looks like ROOT\something, + # which means that it's actually not a real device + # AdapterType;MACAddress;Name;PNPDeviceID; + - type: excludeMatchingLines + column: 4 + regExp: ^ROOT\\ + # Avoid any network interface whose PnPDeviceID looks like COMPOSITEBUS\something, + # These are virtual ports + # AdapterType;MACAddress;Name;PNPDeviceID; + - type: excludeMatchingLines + column: 4 + regExp: ^COMPOSITEBUS\\ + # Avoid any network interface whose PnPDeviceID looks like ...\MSRRAS\..., + # These are WAN ports (not physical) + # AdapterType;MACAddress;Name;PNPDeviceID; + - type: excludeMatchingLines + column: 4 + regExp: \\MSRRAS\\ + # Avoid any network interface whose name contains "Virtual" + # These are obviously virtual + # AdapterType;MACAddress;Name;PNPDeviceID; + - type: excludeMatchingLines + column: 3 + regExp: Virtual + # Keep only the network card name and PNPDeviceID (which will be referred to in the root/WMI namespace) + # MACAddress;Name;PNPDeviceID; + - type: keepColumns + columnNumbers: "2,3,4" + source(2): + # Source(2) = Win32_PnPEntity WMI class + # MSNdisID;PNPDeviceID; + type: wmi + namespace: root\cimv2 + query: "SELECT Name,PNPDeviceID FROM Win32_PnPEntity" + source(3): + # Source(3) = Table joint of Source(1) and Source(2) + # MACAddress;Name;PNPDeviceID;MSNdisID;PNPDeviceID; + type: tableJoin + leftTable: "${source::monitors.network.discovery.sources.source(1)}" + rightTable: "${source::monitors.network.discovery.sources.source(2)}" + leftKeyColumn: 3 + rightKeyColumn: 2 + computes: + # Add "PnPDeviceId: " to the PnPDeviceId column + # MACAddress;Name;PNPDeviceID;MSNdisID;PNPDeviceID; + - type: leftConcat + column: 3 + value: "PnP Device Id: " + mapping: + # InstanceTable = Source(3) + source: "${source::monitors.network.discovery.sources.source(3)}" + attributes: + id: $4 + model: $2 + physical_address: $1 + physical_address_type: MAC + info: $3 + hw.parent.type: enclosure + name: "${awk::sprintf(\"%s (%s)\", $4, $2)}" + collect: + # Collect type is multi-instance + type: multiInstance + keys: + - id + sources: + source(1): + # Status + # DeviceID;HardwareStatus + type: wmi + namespace: root\wmi + query: "SELECT InstanceName,NdisHardwareStatus FROM MSNdis_HardwareStatus" + computes: + # DeviceID;HardwareStatus;HardwareStatus + - type: duplicateColumn + column: 2 + # DeviceID;PATROLStatus;HardwareStatus + - type: translate + column: 2 + translationTable: "${translation::GenericStatusTranslationTable}" + # DeviceID;PATROLStatus;StatusInformation + - type: translate + column: 3 + translationTable: "${translation::GenericStatusInformationTranslationTable}" + source(2): + # LinkStatus + # DeviceID;MediaConnectStatus + type: wmi + namespace: root\wmi + query: "SELECT InstanceName,NdisMediaConnectStatus FROM MSNdis_MediaConnectStatus" + computes: + # DeviceID;LinkStatus + - type: translate + column: 2 + translationTable: "${translation::GenericLinkStatusTranslationTable}" + source(3): + # Transmitted errors + # DeviceID;TransmitError + type: wmi + namespace: root\wmi + query: "SELECT InstanceName,NdisTransmitsError FROM MSNdis_TransmitsError" + source(4): + # Received errors + # DeviceID;ReceiveError + type: wmi + namespace: root\wmi + query: "SELECT InstanceName,NdisReceiveError FROM MSNdis_ReceiveError" + source(5): + # TransmitsOK + # DeviceID;TransmitsOK + type: wmi + namespace: root\wmi + query: "SELECT InstanceName,NdisTransmitsOK FROM MSNdis_TransmitsOK" + source(6): + # ReceivesOK + # DeviceID;ReceivesOK + type: wmi + namespace: root\wmi + query: "SELECT InstanceName,NdisReceivesOK FROM MSNdis_ReceivesOK" + source(7): + # Source(7) = Source(3) + Source(4) + # DeviceID;TransmitError;DeviceID;ReceiveError + type: tableJoin + leftTable: "${source::monitors.network.collect.sources.source(3)}" + rightTable: "${source::monitors.network.collect.sources.source(4)}" + leftKeyColumn: 1 + rightKeyColumn: 1 + source(8): + # Source(8) = Source(5) + Source(6) + # DeviceID;TransmitsOK;DeviceID;ReceivesOK + type: tableJoin + leftTable: "${source::monitors.network.collect.sources.source(5)}" + rightTable: "${source::monitors.network.collect.sources.source(6)}" + leftKeyColumn: 1 + rightKeyColumn: 1 + source(9): + # Source(9) = Source(8) + Source(7) + # DeviceID;TransmitsOK;DeviceID;ReceivesOK;DeviceID;TransmitError;DeviceID;ReceiveError; + type: tableJoin + leftTable: "${source::monitors.network.collect.sources.source(8)}" + rightTable: "${source::monitors.network.collect.sources.source(7)}" + leftKeyColumn: 1 + rightKeyColumn: 1 + defaultRightLine: ;0;;0; + computes: + # Computes TransmittedPackets ( = TransmitErrors + TransmitsOK ) + # DeviceID;TransmittedPackets;DeviceID;ReceivesOK;DeviceID;TransmitError;DeviceID;ReceiveError + - type: add + column: 2 + value: $6 + # Computes ReceivedPackets ( = ReceivedErrors + ReceivesOK) + # DeviceID;TransmittedPackets;DeviceID;ReceivedPackets;DeviceID;TransmitError;DeviceID;ReceiveError; + - type: add + column: 4 + value: $8 + # Computes totalErrors ( = TransmitError + ReceiveError ) + # DeviceID;TransmittedPackets;DeviceID;ReceivedPackets;DeviceID;TotalError;DeviceID;ReceiveError; + - type: add + column: 6 + value: $8 + source(10): + # Join LinkStatus and Status tables + # DeviceID;LinkStatus;DeviceID;PATROLStatus;StatusInformation + type: tableJoin + leftTable: "${source::monitors.network.collect.sources.source(2)}" + rightTable: "${source::monitors.network.collect.sources.source(1)}" + leftKeyColumn: 1 + rightKeyColumn: 1 + defaultRightLine: ;;; + source(11): + # Link Speed + # DeviceID;LinkSpeed + type: wmi + namespace: root\wmi + query: "SELECT InstanceName,NdisLinkSpeed FROM MSNdis_LinkSpeed" + computes: + # Convert LinkSpeed into Mbp/s + # DeviceID;LinkSpeed + - type: divide + column: 2 + value: 10000 + source(12): + # Join Status/LinkStatus with LinkSpeed + # DeviceID;LinkStatus;DeviceID;PATROLStatus;StatusInformation;DeviceID;LinkSpeed; + type: tableJoin + leftTable: "${source::monitors.network.collect.sources.source(10)}" + rightTable: "${source::monitors.network.collect.sources.source(11)}" + leftKeyColumn: 1 + rightKeyColumn: 1 + source(13): + # Join Source(12) + Source(9) + # DeviceID;LinkStatus;DeviceID;PATROLStatus;StatusInformation;DeviceID;LinkSpeed;DeviceID;TransmittedPackets;DeviceID;ReceivedPackets;DeviceID;TotalError;DeviceID;ReceiveError; + type: tableJoin + leftTable: "${source::monitors.network.collect.sources.source(12)}" + rightTable: "${source::monitors.network.collect.sources.source(9)}" + leftKeyColumn: 1 + rightKeyColumn: 1 + defaultRightLine: ;;;;;;;; + computes: + # Now replace some "special" chars in one of the DeviceID columns with underscores + # as it looks like the perfmon instances can't have '#' or '/' + # This allow us to make proper table join with Win32_PerfRawData_Tcpip_NetworkInterface + - type: replace + column: 8 + existingValue: '#' + newValue: _ + - type: replace + column: 8 + existingValue: / + newValue: _ + source(14): + # Traffic in bytes (through performance counters) + # Name;BytesReceived;BytesTransmitted + type: wmi + namespace: root\cimv2 + query: "SELECT Name,BytesReceivedPersec,BytesSentPersec FROM Win32_PerfRawData_Tcpip_NetworkInterface" + computes: + # Replace [] with () (weird difference between these 2 WMI classes) + - type: replace + column: 1 + existingValue: "[" + newValue: ( + - type: replace + column: 1 + existingValue: "]" + newValue: ) + source(15): + # Final Join: Source(13) + Source(14) + # DeviceID;LinkStatus;DeviceID;PATROLStatus;StatusInformation;DeviceID;LinkSpeed;DeviceID;TransmittedPackets;DeviceID;ReceivedPackets;DeviceID;TotalError;DeviceID;ReceiveError;Name;BytesReceived;BytesTransmitted + type: tableJoin + leftTable: "${source::monitors.network.collect.sources.source(13)}" + rightTable: "${source::monitors.network.collect.sources.source(14)}" + leftKeyColumn: 8 + rightKeyColumn: 1 + defaultRightLine: ;;; + mapping: + # And here is the ValueTable (at last!) + source: "${source::monitors.network.collect.sources.source(15)}" + attributes: + id: $1 + metrics: + hw.status{hw.type="network"}: $4 + hw.network.up: legacyLinkStatus($2) + hw.network.bandwidth.limit: megaBit2Bit($7) + hw.errors{hw.type="network"}: $13 + hw.network.packets{direction="transmit"}: $9 + hw.network.packets{direction="receive"}: $11 + hw.network.io{direction="receive"}: $17 + hw.network.io{direction="transmit"}: $18 + legacyTextParameters: + StatusInformation: $5 +translations: + GenericStatusTranslationTable: + "0": ok + "1": ok + "2": ok + "3": degraded + "4": failed + GenericStatusInformationTranslationTable: + "0": "" + "1": Initializing + "2": Reset + "3": Closing + "4": Not-ready + GenericLinkStatusTranslationTable: + "0": ok + "1": degraded diff --git a/src/it/metricshub-connectors/src/main/connector/hardware/WBEMGenericDiskNT/WBEMGenericDiskNT.yaml b/src/it/metricshub-connectors/src/main/connector/hardware/WBEMGenericDiskNT/WBEMGenericDiskNT.yaml new file mode 100644 index 0000000..57b8e31 --- /dev/null +++ b/src/it/metricshub-connectors/src/main/connector/hardware/WBEMGenericDiskNT/WBEMGenericDiskNT.yaml @@ -0,0 +1 @@ +--- {} diff --git a/src/it/metricshub-connectors/src/main/connector/hardware/WBEMGenericNT/WBEMGenericNT.yaml b/src/it/metricshub-connectors/src/main/connector/hardware/WBEMGenericNT/WBEMGenericNT.yaml new file mode 100644 index 0000000..57b8e31 --- /dev/null +++ b/src/it/metricshub-connectors/src/main/connector/hardware/WBEMGenericNT/WBEMGenericNT.yaml @@ -0,0 +1 @@ +--- {} diff --git a/src/it/metricshub-connectors/src/main/connector/hardware/WinStorageSpaces/WinStorageSpaces.yaml b/src/it/metricshub-connectors/src/main/connector/hardware/WinStorageSpaces/WinStorageSpaces.yaml new file mode 100644 index 0000000..4ded269 --- /dev/null +++ b/src/it/metricshub-connectors/src/main/connector/hardware/WinStorageSpaces/WinStorageSpaces.yaml @@ -0,0 +1,544 @@ +--- +extends: +- ../Hardware/Hardware +connector: + displayName: Windows Storage Spaces (WMI) + platforms: Any system + reliesOn: Windows Storage Spaces + information: "This connector provides monitoring physical disks through Windows Storage Management's WMI provider. It supports all disk types, including disks on NVMe bus and NVDIMM. When available, temperature sensors are also discovered and monitored." + detection: + connectionTypes: + - remote + - local + appliesTo: + - NT + supersedes: + - WBEMGenDiskNT + - DiskPart + criteria: + # Must be Windows + - type: deviceType + keep: + - NT + # Must be Hardware Sentry KM 11.3 at least + - type: productRequirements + kmVersion: 11.3.00 + # Having something in MSFT_Volume is enough to consider that the WMI provider + # for Windows Storage Spaces is working + - type: wmi + namespace: root\Microsoft\Windows\Storage + query: SELECT ObjectId FROM MSFT_Volume +monitors: + disk_controller: + discovery: + sources: + source(1): + # Source(1) = MSFT_PhysicalDisk + # __PATH;BusType;DeviceId;FirmwareVersion;MediaType;Model;SerialNumber;Size;SpindleSpeed; + type: wmi + namespace: root\Microsoft\Windows\Storage + query: "SELECT __PATH,BusType,DeviceId,FirmwareVersion,MediaType,Model,SerialNumber,Size,SpindleSpeed FROM MSFT_PhysicalDisk" + computes: + # Filter on MediaType: Keep only actual disks (HDD, SDD and SCM) + # __PATH;BusType;DeviceId;FirmwareVersion;MediaType;Model;SerialNumber;Size;SpindleSpeed; + - type: keepOnlyMatchingLines + column: 5 + valueList: "3,4,5" + # Filter on BusType: Keep only disks on bus that are on an actual physical disk bus (SCSI, ATA, SSA, SAS, SATA, NVMe) + # Note: We exclude 6 (FC) because modern systems no longer use FC to attach disks, and that allows us to easily + # exclude LUNs over HBAs. + # __PATH;BusType;DeviceId;FirmwareVersion;MediaType;Model;SerialNumber;Size;SpindleSpeed; + - type: keepOnlyMatchingLines + column: 2 + valueList: "1,3,5,10,11,17" + # Translate BusType + # __PATH;BusType;DeviceId;FirmwareVersion;MediaType;Model;SerialNumber;Size;SpindleSpeed; + - type: translate + column: 2 + translationTable: "${translation::BustypeTranslationTable}" + # Merge all info into a proper label for the "Model" field + # __PATH;DeviceID;Model;FirmwareVersion;SerialNumber;Size + - type: awk + script: "${file::embeddedFile-1}" + keep: ^MSHW; + separators: ; + selectColumns: "2,3,4,5,6,7" + source(2): + # Source(2) = Win32_DiskDrive + # SerialNumber;PnPDeviceID; + type: wmi + query: "SELECT __PATH,SerialNumber,PnPDeviceID FROM Win32_DiskDrive" + namespace: root\cimv2 + computes: + # Remove extraneous spaces + # __PATH;SerialNumber;PnPDeviceID; + - type: replace + column: 2 + existingValue: ' ' + newValue: "" + source(3): + # Source(3) = jointure of source(1) and source(2) on SerialNumber in order to + # have the PnPDeviceID of the disk instances from MSFT_PhysicalDisk + # __PATH_MSFT_DiskDrive;DeviceID;Model;FirmwareVersion;SerialNumber;Size;__PATH_Win32_DiskDrive;SerialNumber;PnPDeviceID; + type: tableJoin + leftTable: "${source::monitors.disk_controller.discovery.sources.source(1)}" + rightTable: "${source::monitors.disk_controller.discovery.sources.source(2)}" + leftKeyColumn: 5 + rightKeyColumn: 2 + source(4): + # Source(4) = Win32_PnpDevice + # __PATH_Device;__PATH_PnP; + type: wmi + query: "SELECT SameElement,SystemElement FROM Win32_PnpDevice" + namespace: root\cimv2 + source(5): + # Source(5) = Join Source(3) and Source(4) + # __PATH_MSFT_DiskDrive;DeviceID;Model;FirmwareVersion;SerialNumber;Size;__PATH_Win32_DiskDrive;SerialNumber;PnPDeviceID;__PATH_Win32_DiskDrive;__PATH_PnP; + type: tableJoin + leftTable: "${source::monitors.disk_controller.discovery.sources.source(3)}" + rightTable: "${source::monitors.disk_controller.discovery.sources.source(4)}" + leftKeyColumn: 7 + rightKeyColumn: 1 + keyType: Wbem + source(6): + # Source(6) = Table that links the PnP Devices (who controls who) + # __PATH_ControllerPnPDeviceID;__PATH_DiskPnPDeviceID; + type: wmi + query: "SELECT Antecedent,Dependent FROM CIM_ControlledBy" + namespace: root\cimv2 + source(7): + # Source(7) = jointure of PnPDeviceID source(3) with Dependant source(4) + # __PATH_MSFT_DiskDrive;DeviceID;Model;FirmwareVersion;SerialNumber;Size;__PATH_Win32_DiskDrive;SerialNumber;PnPDeviceID;__PATH_Win32_DiskDrive;__PATH_PnP;__PATH_Controller;__PATH_DiskPnPDeviceID; + type: tableJoin + leftTable: "${source::monitors.disk_controller.discovery.sources.source(5)}" + rightTable: "${source::monitors.disk_controller.discovery.sources.source(6)}" + leftKeyColumn: 11 + rightKeyColumn: 2 + keyType: Wbem + computes: + # Keep only relevant columns (remove temporary garbage) + # __PATH_MSFT_DiskDrive;DeviceID;Model;FirmwareVersion;SerialNumber;Size;PnPDeviceID;__PATH_Controller; + - type: keepColumns + columnNumbers: "1,2,3,4,5,6,9,12" + source(8): + # Source(8) = CIM_Controller + # __PATH;Name; + type: wmi + query: "SELECT __PATH,Name FROM CIM_Controller" + namespace: root\cimv2 + source(9): + # Source(9) = jointure of source(8) and source(7) + # This filters out controllers that don't have disks attached to them) + # __PATH_Controller;ControllerName;__PATH_MSFT_DiskDrive;DeviceID;Model;FirmwareVersion;SerialNumber;Size;PnPDeviceID;__PATH_Controller; + type: tableJoin + leftTable: "${source::monitors.disk_controller.discovery.sources.source(8)}" + rightTable: "${source::monitors.disk_controller.discovery.sources.source(7)}" + leftKeyColumn: 1 + rightKeyColumn: 8 + keyType: Wbem + source(10): + # Source(10) = ASSOCIATORS of each MSFT_PhysicalDisk, from the MSFT_StorageReliabilityCounter table + # DeviceId;ReadErrors;Wear;WriteErrors;Temperature;TemperatureMax; + type: wmi + executeForEachEntryOf: + source: "${source::monitors.disk_controller.discovery.sources.source(7)}" + concatMethod: list + namespace: root\Microsoft\Windows\Storage + query: "SELECT DeviceId,ReadErrorsTotal,Wear,WriteErrorsTotal,Temperature,TemperatureMax FROM ASSOCIATORS OF {$1} WHERE ResultClass = MSFT_StorageReliabilityCounter" + mapping: + # InstanceTable: Source(9) (not Source(10)!) + # __PATH_Controller;ControllerName;__PATH_MSFT_DiskDrive;DeviceID;Model;FirmwareVersion;SerialNumber;Size;PnPDeviceID;__PATH_Controller; + source: "${source::monitors.disk_controller.discovery.sources.source(9)}" + attributes: + id: $1 + model: $2 + hw.parent.type: enclosure + name: "${awk::sprintf(\"Disk Controller: %s (%s)\", $1, $2)}" + physical_disk: + discovery: + sources: + source(1): + # Source(1) = jointure of DiskController.Discovery.Source(7) and DiskController.Discovery.Source(8) + # __PATH_MSFT_DiskDrive;DeviceID;Model;FirmwareVersion;SerialNumber;Size;PnPDeviceID;__PATH_Controller;__PATH_Controller;ControllerName; + type: tableJoin + leftTable: "${source::monitors.disk_controller.discovery.sources.source(7)}" + rightTable: "${source::monitors.disk_controller.discovery.sources.source(8)}" + leftKeyColumn: 8 + rightKeyColumn: 1 + keyType: Wbem + computes: + # Keep only the necessary columns + # __PATH_MSFT_DiskDrive;DeviceID;Model;FirmwareVersion;SerialNumber;Size;__PATH_Controller; + - type: keepColumns + columnNumbers: "1,2,3,4,5,6,8" + source(2): + # Source(2) = Table joint of Source(1) with DiskController.Discovery.Source(10) + # __PATH_MSFT_DiskDrive;DeviceID;Model;FirmwareVersion;SerialNumber;Size;__PATH_Controller;DeviceId;ReadErrors;Wear;WriteErrors;Temperature;TemperatureMax; + type: tableJoin + leftTable: "${source::monitors.physical_disk.discovery.sources.source(1)}" + rightTable: "${source::monitors.disk_controller.discovery.sources.source(10)}" + leftKeyColumn: 2 + rightKeyColumn: 1 + defaultRightLine: ;;;;;; + mapping: + # The instance table = Source(2) + # __PATH_MSFT_DiskDrive;DeviceID;Model;FirmwareVersion;SerialNumber;Size;__PATH_Controller;DeviceId;ReadErrors;Wear;WriteErrors;Temperature;TemperatureMax; + source: "${source::monitors.physical_disk.discovery.sources.source(2)}" + attributes: + id: $2 + vendor: $3 + firmware_version: $4 + serial_number: $5 + hw.parent.type: disk_controller + hw.parent.id: $7 + name: "${awk::sprintf(\"%s (%s - %s)\", $2, $3, bytes2HumanFormatBase10($6))}" + metrics: + hw.physical_disk.size: $6 + conditionalCollection: + hw.errors{hw.type="physical_disk"}: $9 + hw.physical_disk.endurance_utilization{state="remaining"}: $10 + collect: + # Collect + type: multiInstance + keys: + - id + sources: + source(1): + # Source(1) = the MSFT_PhysicalDisk table + # __PATH;DeviceId;OperationalStatus + type: wmi + namespace: root\Microsoft\Windows\Storage + query: "SELECT __PATH,DeviceId,OperationalStatus FROM MSFT_PhysicalDisk" + computes: + # Duplicate the OperationalStatus column, twice + # __PATH;DeviceId;OperationalStatus;OperationalStatus;OperationalStatus + - type: duplicateColumn + column: 3 + - type: duplicateColumn + column: 3 + # Translate the OperationalStatus field into an array of statuses + # __PATH;DeviceId;StatusArray;OperationalStatus;OperationalStatus + - type: arrayTranslate + column: 3 + translationTable: "${translation::OperationalStatusTranslationTable}" + # Convert the array of status to a simple status (keep the worse one) + - type: convert + column: 3 + conversion: array2SimpleStatus + # Translate the 2nd OperationalStatus field into a more readable string + # __PATH;DeviceId;Status;StatusInformation;OperationalStatus + - type: arrayTranslate + column: 4 + translationTable: "${translation::OperationalStatusInformationTranslationTable}" + resultSeparator: ' - ' + # Translate the 3rd OperationalStatus field into the PredictiveFailure parameter + # __PATH;DeviceId;Status;StatusInformation;PredictedFailure + - type: translate + column: 5 + translationTable: "${translation::PredictedFailureTranslationTable}" + source(2): + # Source(2) = ASSOCIATORS of each MSFT_PhysicalDisk, from the MSFT_StorageReliabilityCounter table + # Use DiskController.Discovery.Source(7) for the list of instances instead of PhysicalDisk.Collect.Source(1) + # because non real physical disks have been filtered out from it, thus preventing unnecessary WMI queries + # DeviceId;ReadErrors;Wear;WriteErrors; + type: wmi + executeForEachEntryOf: + source: "${source::monitors.disk_controller.discovery.sources.source(7)}" + concatMethod: list + namespace: root\Microsoft\Windows\Storage + query: "SELECT DeviceId,ReadErrorsTotal,Wear,WriteErrorsTotal FROM ASSOCIATORS OF {$1} WHERE ResultClass = MSFT_StorageReliabilityCounter" + computes: + # TotalErrors = Read + Write + # DeviceId;TotalErrors;Wear;WriteErrors; + - type: add + column: 2 + value: $4 + # Convert Wear into EnduranceRemaing (Endurance Remaining = 100 - Wear) + # DeviceId;TotalErrors;EnduranceRemaing;WriteErrors; + - type: multiply + column: 3 + value: -1 + - type: add + column: 3 + value: 100 + source(3): + # Source(3) = Table join of Source(1) and Source(2) + # __PATH;DeviceId;Status;StatusInformation;OperationalStatus;DeviceId;TotalErrors;EnduranceRemaing;WriteErrors; + type: tableJoin + leftTable: "${source::monitors.physical_disk.collect.sources.source(1)}" + rightTable: "${source::monitors.physical_disk.collect.sources.source(2)}" + leftKeyColumn: 2 + rightKeyColumn: 1 + defaultRightLine: ;;;; + mapping: + # ValueTable = Source(3) + # __PATH;DeviceId;Status;StatusInformation;OperationalStatus;DeviceId;TotalErrors;EnduranceRemaing;WriteErrors; + source: "${source::monitors.physical_disk.collect.sources.source(3)}" + attributes: + id: $2 + metrics: + hw.status{hw.type="physical_disk"}: $3 + hw.status{hw.type="physical_disk", state="predicted_failure"}: boolean($5) + hw.errors{hw.type="physical_disk"}: $7 + hw.physical_disk.endurance_utilization{state="remaining"}: percent2Ratio($8) + legacyTextParameters: + StatusInformation: $4 + logical_disk: + discovery: + sources: + source(1): + # Source(1) = MSFT_Volume + # ObjectId;DriveLetterNumber;DriveType;FileSystem;Name;Size; + type: wmi + query: "SELECT ObjectId,DriveLetter,DriveType,FileSystem,FileSystemLabel,Size FROM MSFT_Volume" + namespace: root\Microsoft\Windows\Storage + computes: + # Keep only drive type 3 ("Fixed Drive") + # ObjectId;DriveLetterNumber;DriveType;FileSystem;Name;Size; + - type: keepOnlyMatchingLines + column: 3 + valueList: 3 + # Transform DriveLetterNumber into a letter, and change ObjectId for DriveLetter is possible + # DeviceID;Label;Filesystem;Size; + - type: awk + script: "${file::embeddedFile-2}" + keep: ^MSHW; + separators: ; + selectColumns: "2,3,4,5" + mapping: + # InstanceTable = Source(1) + source: "${source::monitors.logical_disk.discovery.sources.source(1)}" + attributes: + id: $1 + __display_id: $2 + type: Volume + info: $3 + hw.parent.type: enclosure + name: "${awk::sprintf(\"%s (%s)\", $2, bytes2HumanFormatBase2($4))}" + metrics: + hw.logical_disk.limit: $4 + collect: + # Collect + type: multiInstance + keys: + - id + sources: + source(1): + # Source(1) = MSFT_Volume + # ObjectId;DriveLetter;HealthStatus;OperationalStatus;SizeRemaning; + type: wmi + query: "SELECT ObjectId,DriveLetter,HealthStatus,SizeRemaining FROM MSFT_Volume" + namespace: root\Microsoft\Windows\Storage + computes: + # Duplicate the HealthStatus column + # ObjectId;DriveLetter;HealthStatus;HealthStatus;SizeRemaning; + - type: duplicateColumn + column: 3 + # Translate the first HealthStatus field into a simple status + # ObjectId;DriveLetter;Status;OperationalStatus;SizeRemaning; + - type: translate + column: 3 + translationTable: "${translation::LogicalDiskHealthStatusTranslationTable}" + # Translate the OperationalStatus field into detailed status information + # ObjectId;DriveLetter;Status;StatusInformation;SizeRemaning; + - type: translate + column: 4 + translationTable: "${translation::LogicalDiskHealthStatusInformationTranslationTable}" + # Convert ObjectId and DriveLetterNumber into a DeviceId + # DeviceID;Status;StatusInformation;SizeRemaning; + - type: awk + script: "${file::embeddedFile-3}" + keep: ^MSHW; + separators: ; + selectColumns: "2,3,4,5" + mapping: + # ValueTable = Source(1) + source: "${source::monitors.logical_disk.collect.sources.source(1)}" + attributes: + id: $1 + metrics: + hw.status{hw.type="logical_disk"}: $2 + hw.logical_disk.usage{state="free"}: $4 + hw.logical_disk.usage{state="used"}: collectAllocatedSpace() + legacyTextParameters: + StatusInformation: $3 + temperature: + discovery: + sources: + source(1): + # Source(1) = ASSOCIATORS of each MSFT_PhysicalDisk, from the MSFT_StorageReliabilityCounter table + # DeviceId;ReadErrors;Wear;WriteErrors;Temperature;TemperatureMax; + type: copy + from: "${source::monitors.disk_controller.discovery.sources.source(10)}" + computes: + # Remove lines where temperature = 0 + # DeviceId;ReadErrors;Wear;WriteErrors;Temperature;TemperatureMax; + - type: excludeMatchingLines + column: 5 + valueList: 0 + source(2): + # Source(2) = Table join of Source(1) with PhysicalDisk.Discovery.Source(1) + # DeviceId;ReadErrors;Wear;WriteErrors;Temperature;TemperatureMax;__PATH_MSFT_DiskDrive;DeviceID;Model;FirmwareVersion;SerialNumber;Size;PnPDeviceID;__PATH_Controller;__PATH_Controller;ControllerName; + type: tableJoin + leftTable: "${source::monitors.temperature.discovery.sources.source(1)}" + rightTable: "${source::monitors.physical_disk.discovery.sources.source(1)}" + leftKeyColumn: 1 + rightKeyColumn: 2 + computes: + # Keep only the necessary columns + # DeviceId;TemperatureMax;Model; + - type: keepColumns + columnNumbers: "1,6,9" + # Assess Warning and Alarm thresholds, based on TemperatureMax + # (which is often 0, in which case we set thresholds based on the type of the disk) + # DeviceId;TemperatureType;WarningThreshold;AlarmThreshold; + - type: awk + script: "${file::embeddedFile-4}" + keep: ^MSHW; + separators: ; + selectColumns: "2,3,4,5" + mapping: + # InstanceTable = Source(2) + source: "${source::monitors.temperature.discovery.sources.source(2)}" + attributes: + id: $1 + sensor_location: $2 + hw.parent.type: enclosure + name: "${awk::sprintf(\"%s (%s)\", $1, $2)}" + metrics: + hw.temperature.limit{limit_type="high.degraded"}: $3 + hw.temperature.limit{limit_type="high.critical"}: $4 + collect: + # Collect + type: multiInstance + keys: + - id + sources: + source(1): + # Source(1) = ASSOCIATORS of each MSFT_PhysicalDisk, from the MSFT_StorageReliabilityCounter table + # DeviceId;ReadErrors;Wear;WriteErrors;Temperature;TemperatureMax; + type: wmi + executeForEachEntryOf: + source: "${source::monitors.physical_disk.discovery.sources.source(1)}" + concatMethod: list + namespace: root\Microsoft\Windows\Storage + query: "SELECT DeviceId,Temperature FROM ASSOCIATORS OF {$1} WHERE ResultClass = MSFT_StorageReliabilityCounter" + mapping: + # ValueTable = Source(1) + source: "${source::monitors.temperature.collect.sources.source(1)}" + attributes: + id: $1 + metrics: + hw.temperature: $2 +translations: + PredictedFailureTranslationTable: + "53285": "True" + "5": "True" + Default: "False" + OperationalStatusInformationTranslationTable: + "53261": Scan Needed + "53286": Abnormal Latency + "53262": Spot Fix Needed + "53263": Full Repair Needed + "53285": Threshold Exceeded + "53269": Removing From Pool + "10": Stopped + "11": In Service + "12": No Contact + "13": Lost Communication + "14": Aborted + "15": Dormant + "16": Supporting Entity in Error + "17": Completed + "18": Power Mode + "19": Relocating + "53271": Updating Firmware + "53272": Device Hardware Error + "53270": In Maintenance Mode + "53253": Split + "1": Other + "53254": Stale Metadata + "53276": Starting Maintenance Mode + "2": "" + "53273": Not Usable + "3": Degraded + "53252": Failed Media + "53274": Transient Error + "4": Stressed + "5": Predictive Failure + "6": Error + "53255": IO Error + "53277": Stopping Maintenance Mode + "7": Non-Recoverable Error + "53256": Unrecognized Metadata + "8": Starting + "9": Stopping + LogicalDiskHealthStatusInformationTranslationTable: + "0": "" + "1": Scan Needed + "2": Spot Fix Needed + "3": Full Repair Needed + Default: Unknown + LogicalDiskHealthStatusTranslationTable: + "0": ok + "1": degraded + "2": degraded + "3": failed + Default: UNKNOWN + BustypeTranslationTable: + "11": SATA + "12": Secure Digital + "13": MMC - Multimedia Card + "14": Other + "15": File-Backed Virtual + "16": Storage Spaces + "17": NVMe + "18": Other + "0": Unknown + "1": SCSI + "2": ATAPI + "3": ATA + "4": IEEE 1394 + "5": SSA + "6": Fibre Channel + "7": USB + "8": RAID + "9": iSCSI + "10": SAS + OperationalStatusTranslationTable: + "53261": ok + "53286": degraded + "53262": ok + "53263": degraded + "53285": degraded + "53269": ok + "10": ok + "11": degraded + "12": failed + "13": failed + "14": failed + "15": ok + "16": failed + "17": ok + "18": ok + "19": ok + "53271": degraded + "53272": failed + "53270": degraded + "53253": degraded + "53254": degraded + "53276": degraded + "2": ok + "53273": failed + "3": degraded + "53252": failed + "53274": failed + "4": degraded + "5": ok + "6": failed + "53255": failed + "53277": degraded + "7": failed + "53256": degraded + "8": ok + "9": ok diff --git a/src/it/metricshub-connectors/src/main/connector/hardware/WinStorageSpaces/embeddedFile-1 b/src/it/metricshub-connectors/src/main/connector/hardware/WinStorageSpaces/embeddedFile-1 new file mode 100644 index 0000000..8f2c548 --- /dev/null +++ b/src/it/metricshub-connectors/src/main/connector/hardware/WinStorageSpaces/embeddedFile-1 @@ -0,0 +1,36 @@ +BEGIN { FS = ";" } +{ + __Path = $1; + busType = $2; + deviceId = $3; + firmwareVersion = $4; + mediaType = $5; + model = $6 + serialNumber = $7 + size = $8 + speed = $9 + + # Media Type + if (mediaType == 3) { + # HDD + if (speed == 15000) { + model = model " - 15K" + } else if (speed == 10000) { + model = model " - 10K" + } else if (speed > 1000 && speed < 100000) { + model = model " - " speed + } + } else if (mediaType == 4) { + # SDD + model = model " - SSD" + } else if (mediaType == 5) { + # SCM (fancy NVDIMM-N) + model = model " - NVDIMM" + } + + # Bus Type + model = model " - " busType + + # Output + print "MSHW;" __Path ";" deviceId ";" model ";" firmwareVersion ";" serialNumber ";" size +} \ No newline at end of file diff --git a/src/it/metricshub-connectors/src/main/connector/hardware/WinStorageSpaces/embeddedFile-2 b/src/it/metricshub-connectors/src/main/connector/hardware/WinStorageSpaces/embeddedFile-2 new file mode 100644 index 0000000..c42dc43 --- /dev/null +++ b/src/it/metricshub-connectors/src/main/connector/hardware/WinStorageSpaces/embeddedFile-2 @@ -0,0 +1,22 @@ +BEGIN { + FS = ";"; + alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; +} +{ + if ($2 > 64 && $2 < 91) { + deviceId = substr(alphabet, int($2) - 64, 1) ":"; + label = deviceId + } else { + deviceId = $1; + label = "" + } + if ($5 != "") { + if (label != "") { + label = label " "; + } + label = label $5; + } + filesystem = $4; + size = $6; + print "MSHW;" deviceId ";" label ";File System: " filesystem ";" size; +} \ No newline at end of file diff --git a/src/it/metricshub-connectors/src/main/connector/hardware/WinStorageSpaces/embeddedFile-3 b/src/it/metricshub-connectors/src/main/connector/hardware/WinStorageSpaces/embeddedFile-3 new file mode 100644 index 0000000..90bab06 --- /dev/null +++ b/src/it/metricshub-connectors/src/main/connector/hardware/WinStorageSpaces/embeddedFile-3 @@ -0,0 +1,12 @@ +BEGIN { + FS = ";"; + alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; +} +{ + if ($2 > 64 && $2 < 91) { + deviceId = substr(alphabet, int($2) - 64, 1) ":"; + } else { + deviceId = $1; + } + print "MSHW;" deviceId ";" $3 ";" $4 ";" $5; +} \ No newline at end of file diff --git a/src/it/metricshub-connectors/src/main/connector/hardware/WinStorageSpaces/embeddedFile-4 b/src/it/metricshub-connectors/src/main/connector/hardware/WinStorageSpaces/embeddedFile-4 new file mode 100644 index 0000000..39ce6c6 --- /dev/null +++ b/src/it/metricshub-connectors/src/main/connector/hardware/WinStorageSpaces/embeddedFile-4 @@ -0,0 +1,16 @@ +BEGIN { FS = ";" } +{ + warningThreshold = ""; + alarmThreshold = $2; + model = $3; + if (alarmThreshold == "" || alarmThreshold == 0) { + if (tolower(model) ~ /ssd|nvm/) { + warningThreshold = 65; + alarmThreshold = 70; + } else { + warningThreshold = 41; + alarmThreshold = 50; + } + } + print "MSHW;" $1 ";" model ";" warningThreshold ";" alarmThreshold +} \ No newline at end of file diff --git a/src/it/metricshub-connectors/src/main/connector/hardware/lmsensors/embeddedFile-1 b/src/it/metricshub-connectors/src/main/connector/hardware/lmsensors/embeddedFile-1 new file mode 100644 index 0000000..124baf4 --- /dev/null +++ b/src/it/metricshub-connectors/src/main/connector/hardware/lmsensors/embeddedFile-1 @@ -0,0 +1,8 @@ +BEGIN { + FS=":" ; Manufacturer="" ; Type="" +} +{ +if ( $1 ~ /Manufacturer/ ) { Manufacturer = $2; } +if ($1 ~ /Product Name/ ) { Type = $2 } +if ($1 ~ /Serial Number/ ) {SN = $2 ; print ("MSHW;"Manufacturer";"Type";"SN";" ); } +} \ No newline at end of file diff --git a/src/it/metricshub-connectors/src/main/connector/hardware/lmsensors/embeddedFile-2 b/src/it/metricshub-connectors/src/main/connector/hardware/lmsensors/embeddedFile-2 new file mode 100644 index 0000000..afb1d82 --- /dev/null +++ b/src/it/metricshub-connectors/src/main/connector/hardware/lmsensors/embeddedFile-2 @@ -0,0 +1,8 @@ +BEGIN { + FS=":" ; Vendor="" ; Version =""; Date="" +} +{ +if ( $1 ~ /Vendor/ ) { Vendor = $2; } +if ($1 ~ /Version/ ) { Version = $2 ; } +if ($1 ~ /Release Date/ ) { Date = $2 ; print("MSHW;""Vendor:"Vendor " Version:" Version " Date:" Date) } +} \ No newline at end of file diff --git a/src/it/metricshub-connectors/src/main/connector/hardware/lmsensors/embeddedFile-3 b/src/it/metricshub-connectors/src/main/connector/hardware/lmsensors/embeddedFile-3 new file mode 100644 index 0000000..9e7b77b --- /dev/null +++ b/src/it/metricshub-connectors/src/main/connector/hardware/lmsensors/embeddedFile-3 @@ -0,0 +1,24 @@ +BEGIN { + Name="" ; Type="" ; Sensor=""; Value="";Max="";Critical=""; TEMP=0 +} +{ +if(NF == 1 && $0 !~ /:/) {Name = $1} +if(NF == 1 && $0 ~ /:/ && TEMP == 1 ) {print ("MSHW;"Name"-"Type";"Type";"Value";"Max";"Critical";"); Value = ""; Max = ""; Critical =""; TEMP =0 } +if(NF == 1 && $0 ~ /:/ && TEMP == 0) {Type = $0; gsub(/:/,"",Type)} +if(NF == 2 && $2 ~ /:/ && TEMP == 1) {print ("MSHW;"Name"-"Type";"Type";"Value";"Max";"Critical";"); Value = ""; Max = ""; Critical = ""; TEMP=0} +if(NF == 2 && $2 ~ /:/ && TEMP == 0) {Type = $0; gsub(/:/,"",Type); gsub(" ","",Type)} +if(NF == 2 && $1 ~ /temp/) { Sensor = $1 ; gsub(/:/,"",Sensor) ; + if ($1 ~ /temp[0-9]_input:/ || $1 ~ /temp[0-9][0-9]_input:/) { Value = $2; TEMP=1 } + if ($1 ~ /temp[0-9]_max:/ || $1 ~ /temp[0-9][0-9]_max:/) { Max = $2; if (Max <=0) {Max=""}} + if ($1 ~ /temp[0-9]_crit:/ || $1 ~ /temp[0-9][0-9]_crit:/&& length($1) == 11 ) { Critical = $2 ; if (Critical <=0) {Critical = ""}; + if ( Max == Value && Value == Critical) { Max = ""; Critical = ""} + if ( Max == Critical && Max > 0) { Max = Max - Critical/10 } + if ( Max == 0 && Critical > 0 ) { Max = Critical - Critical/10 } + print ("MSHW;"Name"-"Type";"Type";"Value";"Max";"Critical";"); Value = ""; Max = ""; Critical ="" ; TEMP=0} + } +if(NF == 1 && TEMP ==1 && $0 !~ /temp/) {print ("MSHW;"Name"-"Type";"Type";"Value";"Max";"Critical";"); Value = ""; Max = ""; Critical =""; TEMP=0} +if(NF == 0 && TEMP ==1 && $0 !~ /temp/) {print ("MSHW;"Name"-"Type";"Type";"Value";"Max";"Critical";"); Value = ""; Max = ""; Critical =""; TEMP=0} +if(NF >= 3 && TEMP ==1 && $0 !~ /temp/) {print ("MSHW;"Name"-"Type";"Type";"Value";"Max";"Critical";"); Value = ""; Max = ""; Critical =""; TEMP=0} +if(NF >= 3 && $0 !~ /temp/) {Type = $0; gsub(/:/,"",Type)} +} +END { if (TEMP == 1) {print ("MSHW;"Name"-"Type";"Type";"Value";"Max";"Critical";"); Value = ""; Max = ""; Critical =""; TEMP=0} } \ No newline at end of file diff --git a/src/it/metricshub-connectors/src/main/connector/hardware/lmsensors/embeddedFile-4 b/src/it/metricshub-connectors/src/main/connector/hardware/lmsensors/embeddedFile-4 new file mode 100644 index 0000000..7f723b9 --- /dev/null +++ b/src/it/metricshub-connectors/src/main/connector/hardware/lmsensors/embeddedFile-4 @@ -0,0 +1,12 @@ +BEGIN { + FS=" " ; Name="" ; Type="" ; Unit=""; Value=""; Lower=""; Upper="" +} +{ +if(NF == 1 && $0 !~ /:/) {Name = $1} + +if ($3 ~ /V/ ) {Value= $2 ; Unit= $3 ; if (Unit == "V") { Value=Value*1000} + Lower=$6; gsub(/\+/,"",Lower); if (Lower == 0) {Lower=""}; + Upper=$10; gsub(/\+/,"",Upper); if (Upper == 0) {Upper=""} else {Upper = Upper*1000}; + {Type = $1; gsub(/:/,"",Type)}; + print ("MSHW;"Name"-"Type";"Type";" Value ";"Lower";"Upper";")} +} \ No newline at end of file diff --git a/src/it/metricshub-connectors/src/main/connector/hardware/lmsensors/embeddedFile-5 b/src/it/metricshub-connectors/src/main/connector/hardware/lmsensors/embeddedFile-5 new file mode 100644 index 0000000..36db35d --- /dev/null +++ b/src/it/metricshub-connectors/src/main/connector/hardware/lmsensors/embeddedFile-5 @@ -0,0 +1,16 @@ +BEGIN { + + DName="" ; FName="" ; Value=""; Min=""; Max=""; Alarm="" +} +{ +if(NF == 1 && $1 !~ /:/) {DName = $1} +if(NF == 1 && $1 ~ /fan/ && $1 ~ /:/) {FName = $1 ; gsub(/:/,"",FName)} +if(NF == 2) { + if ($1 ~ /fan[0-9]_input/) { Value = $2 ; if (Value <= 0) {Value=""}} + if ($1 ~ /fan[0-9]_min/ ) { Min = $2 ; if (Min <= 0) {Min=""}} + if ($1 ~ /fan[0-9]_max/ ) { Max = $2 ; if (Max <= 0) {Max=""}} + if ($1 ~ /fan[0-9]_alarm/) { Alarm = $2 ; if (Alarm <= 0) {Alarm =""}} + if ($1 ~ /fan[0-9]_pulses/ ) { + print ("MSHW;"DName"-"FName";"Value";"Min";"Max";"Alarm";")} + } +} \ No newline at end of file diff --git a/src/it/metricshub-connectors/src/main/connector/hardware/lmsensors/lmsensors.yaml b/src/it/metricshub-connectors/src/main/connector/hardware/lmsensors/lmsensors.yaml new file mode 100644 index 0000000..082b2f8 --- /dev/null +++ b/src/it/metricshub-connectors/src/main/connector/hardware/lmsensors/lmsensors.yaml @@ -0,0 +1,211 @@ +--- +extends: +- ../Hardware/Hardware +connector: + displayName: lm_sensors + platforms: Any system + reliesOn: lm_sensors + information: "Provides the monitoring environment sensors on Linux, through the sensors command." + version: 1.1 + detection: + connectionTypes: + - remote + - local + appliesTo: + - Linux + criteria: + - type: deviceType + keep: + - Linux + - type: osCommand + commandLine: "%{SUDO:/usr/bin/sensors} /usr/bin/sensors -h" + expectedResult: "Usage: sensors " + errorMessage: lm_sensors is not installed on this server. +sudoCommands: +- /usr/sbin/dmidecode +- /usr/bin/sensors +monitors: + enclosure: + discovery: + sources: + source(1): + # Discovery Enclosure + type: osCommand + commandLine: "%{SUDO:/usr/sbin/dmidecode} /usr/sbin/dmidecode -t system" + computes: + # '"MSHW;"Manufacturer";"Type";"SN";"' + - type: awk + script: "${file::embeddedFile-1}" + keep: ^MSHW; + separators: ; + source(2): + type: osCommand + commandLine: "%{SUDO:/usr/sbin/dmidecode} /usr/sbin/dmidecode -t bios" + computes: + # '"MSHW;"Bios Info"' + - type: awk + script: "${file::embeddedFile-2}" + keep: ^MSHW; + separators: ; + source(3): + # '"MSHW;"Manufacturer";"Type";"SN";"' + type: tableJoin + leftTable: "${source::monitors.enclosure.discovery.sources.source(1)}" + rightTable: "${source::monitors.enclosure.discovery.sources.source(2)}" + leftKeyColumn: 1 + rightKeyColumn: 1 + mapping: + # Enclosure Instance Table + # "MSHW;"Name";"Type";"SN";""MSHW;"Bios Info" + source: "${source::monitors.enclosure.discovery.sources.source(3)}" + attributes: + id: Computer + __display_id: $3 + vendor: $2 + type: Computer + serial_number: $4 + bios_version: $6 + name: "${awk::sprintf(\"Computer: %s (%s)\", $3, $2)}" + temperature: + discovery: + sources: + source(1): + # Discovery Temperature + type: osCommand + commandLine: "%{SUDO:/usr/bin/sensors} /usr/bin/sensors -u -A" + computes: + - type: awk + script: "${file::embeddedFile-3}" + keep: ^MSHW; + separators: ; + mapping: + # Temperature Instance Table + # "MSHW;"Name";"Type";"Value";"Max";"Critical";" + source: "${source::monitors.temperature.discovery.sources.source(1)}" + attributes: + id: $2 + __display_id: $2 + hw.parent.type: enclosure + hw.parent.id: Computer + name: $2 + metrics: + hw.temperature.limit{limit_type="high.degraded"}: $5 + hw.temperature.limit{limit_type="high.critical"}: $6 + collect: + # Collection Type + type: multiInstance + keys: + - id + sources: + source(1): + type: osCommand + commandLine: "%{SUDO:/usr/bin/sensors} /usr/bin/sensors -u -A" + computes: + - type: awk + script: "${file::embeddedFile-3}" + keep: ^MSHW; + separators: ; + mapping: + # Temperature Value Table + # "MSHW;"Name";"Type";"Value";"Max";"Critical";" + source: "${source::monitors.temperature.collect.sources.source(1)}" + attributes: + id: $2 + metrics: + hw.temperature: $4 + voltage: + discovery: + sources: + source(1): + # Discovery Voltage + type: osCommand + commandLine: "%{SUDO:/usr/bin/sensors} /usr/bin/sensors -A" + computes: + - type: awk + script: "${file::embeddedFile-4}" + keep: ^MSHW; + separators: ; + mapping: + # Voltage Instance Table + # "MSHW;"Name";"Type";"Value";Lower";"Upper";" + source: "${source::monitors.voltage.discovery.sources.source(1)}" + attributes: + id: $2 + __display_id: $2 + sensor_location: Planar + hw.parent.type: enclosure + hw.parent.id: Computer + name: "${awk::sprintf(\"%s (%s)\", $2, \"Planar\")}" + metrics: + hw.voltage.limit{limit_type="low.critical"}: $5 + hw.voltage.limit{limit_type="high.degraded"}: $6 + collect: + # Collection Type + type: multiInstance + keys: + - id + sources: + source(1): + type: osCommand + commandLine: "%{SUDO:/usr/bin/sensors} /usr/bin/sensors -A" + computes: + - type: awk + script: "${file::embeddedFile-4}" + keep: ^MSHW; + separators: ; + mapping: + # Voltage Value Table + # "MSHW;"Name";"Type";"Value"; + source: "${source::monitors.voltage.collect.sources.source(1)}" + attributes: + id: $2 + metrics: + hw.voltage: $4 + fan: + discovery: + sources: + source(1): + # Discovery FAN + type: osCommand + commandLine: "%{SUDO:/usr/bin/sensors} /usr/bin/sensors -u -A" + computes: + - type: awk + script: "${file::embeddedFile-5}" + keep: ^MSHW; + separators: ; + mapping: + # FAN Instance Table + # ""MSHW;"DName"-"FName";"Value";"Min";"Max";"Alarm";" + source: "${source::monitors.fan.discovery.sources.source(1)}" + attributes: + id: $2 + __display_id: $2 + sensor_location: Planar + hw.parent.type: enclosure + hw.parent.id: Computer + name: "${awk::sprintf(\"%s (%s)\", $2, \"Planar\")}" + metrics: + hw.fan.speed.limit{limit_type="low.degraded"}: $5 + hw.fan.speed.limit{limit_type="low.critical"}: $6 + collect: + # Collection Type + type: multiInstance + keys: + - id + sources: + source(1): + type: osCommand + commandLine: "%{SUDO:/usr/bin/sensors} /usr/bin/sensors -u -A" + computes: + - type: awk + script: "${file::embeddedFile-5}" + keep: ^MSHW; + separators: ; + mapping: + # Voltage Value Table + # "MSHW;"Name";"Type";"Value"; + source: "${source::monitors.fan.collect.sources.source(1)}" + attributes: + id: $2 + metrics: + hw.fan.speed: $3 diff --git a/src/it/metricshub-connectors/src/site/markdown/index.md b/src/it/metricshub-connectors/src/site/markdown/index.md new file mode 100644 index 0000000..cf643eb --- /dev/null +++ b/src/it/metricshub-connectors/src/site/markdown/index.md @@ -0,0 +1,3 @@ +# MetricsHub Connectors + +This is the repository for the MetricsHub Connectors (IT). It is separated between hardware and system connectors. \ No newline at end of file diff --git a/src/it/metricshub-connectors/src/site/resources/images/sentry-logo-179x75px.png b/src/it/metricshub-connectors/src/site/resources/images/sentry-logo-179x75px.png new file mode 100644 index 0000000000000000000000000000000000000000..42e53f78134d10c1e5f527a9fb12e081e3066d05 GIT binary patch literal 4321 zcmV<75FYP|P)Px#1ZP1_K>z@;j|==^1poj532;bRa{vGi!vFvd!vV){sAK>D5OzsKK~#8N?VSnq z9n&4h^Z#3d*kdb22t_xw)KY3s(Nb;2PKqvCL8a=bql*;B9;vFODme~{YDC0VYOgI4 zOAuR>)Rv?v2_lI|($DwKZzl8Jn|U+y-kbMwn)^BDd+xrxdH3D9bC-ENJRXn7UBtT^eWEarrz6lPyXMcIzdgDE&Bk2_(ug5g zm$WQBp7ui{+#21Z))+L+VvuYuI@Ypt;Jp-n-$$P_FOR1@XmHEX*oLA{U8`&W(u!j& zO9$?C2ztQ6XQ5NE`X1X;4m7S!X=Kl-!8HSUTwqx`aBqX&vEVol;Qtwm=JB)!8r4oT zuIWJ=YaH@<24gA9Hf+cj6F#AY~a%xC||w)chDEnis%FC@+9U+%o&)8P@hfFHs-9yRO)<} z*&`_TDvarraWa}8_@+Zyn(90ltA<^3$n!=Q5Q99whOtrL%FUhR^&hi4$Y))QozZOP z5ull-xkyu~Z&K%%(WeTCLt1^0dj&2UU02r_^pZJiApW;7dIzrLYt>S?h+9$UhnP)6 zKI1Xm4@)NQ(JsKiJr!MshM-r>+ktyB+Q3z+1(4UiDDiXQuOZJa7PSV#2L>_gL%fG9 zNd@>nW#MO=S6zs@G;v*BF)JEh2>yRoco9gmttF4ZZ(j>egm5tqZDL6x2-XAqxFDvB zD|_N|4`E+Ib1k0O(}K!@a@L}dq_J#6kk??#S|4Uhq=gg*_Lor9Lu2oWsvG;$7|U6d zh9M9AK%=Qd?3!88LuWKIgLNj_+>%s)e@hy(!2UDyPK9WTOk)lq-rHzROBSI#Q8YP# z{b&oS0RK@IMR9I6?{c7wZVI_GD6Tb--#0CL<$rqM8$eip3yQ$6FNzwxzY_F$>(YQR-(RN@inDg1EOTjlljr^R5f=;*I%1 z^NzspSPNG8WQ^!xJ_Ns2EX;xZd<#<8lDMA?LR{EyEhslNUr0SDV`Yqr>9bZqzVYSl z>1Kv(XJ!QP^^2_-R`5WfF?TbohI-KJ8CZLKgevO6L&!TAKS3K}Kat`xAJCeGGE|=t ze6fkcnnelf`d70GwUe4*A93EtI4N*Bj_0$vhf}_{{)BIB_SdkVD|-=&Hs+sFNX;#% zS;+ItW)%;Z8FD7&u4i`j0J=3@N}8IwZkm& zp!`c^QWrIhJ8)}r)*=h_i}%FyOf~R%t|#vOX4jAP0^I7%&XON?&5WqCh|ij|78=pA zD7r-SyTt^MPitNmu4$8^a>e^QojSjw8$0?>E@vu zWEa@=tkTq!aV8YfKPayCkoPAT+7jx(o$JXt4btbC>#c#LtC9BAAeV*UHx2zB)xF&l z-m=*YSOuw(=*x9re-uS?`gnD!F>8kBjhLockizr;r#bq%5bca0#X|7YGeX1rZ;A{v z5_^+Yz%6AdKFb;Alv`imNOLyAA!ZhWa;KrMQ?{muqT3{Xz0nC+$8xc?H>BL2!=Owp zNE}3YkC=OVjGJ&TEqAA&9?KwY+!wpVp$zx-5CaUPEaf_Pqw7@?;%+RfK$0QLJ ze@2MFzoKV0Z~3o^UNC>(TG978=)+MngCUcCA}9y;hJ31#KS` zSuT|G913R?RPG_D@K#cie1d8w?i+#ZG_T$dJs;%m@>jzudupy?Y7qB&gii{BT>iRE zcd9Wzh8{Pk(5}Y9cMI~^EJ*J1eA>JlChss~OEXJ_TaWfaMDvTdZ@{S8Z;%D)^km&o zow=wha&4m?hP>u;>$?Z*Zuox?`1a5qjJEFDqaHd^wio5T7x?rz4SU7D?>3@68HZ4Q zIDNS@_6yD4G4eL%n)UV)If(}4mQ(ox7og+0P?KFqG4#X84P~rExu;-#4SUT(1@%zR z_b;>Sx>d|9v;%%rFJkON{bo`J4Wl&YU;eE*0qUah?lIKGo!6u~$8r4z$lfV38#8s) zMJu}Ns$sY&x?Nqvh3RE(?TkB4_JS1ZIr*HLv_A)_k?O2sHL8#6v57m6cvEC=H#DNh zFb<>+bE(4;C~t?(6yJQrdK)bfRrdT^08#)wvcaNo>D`iD7l zL7G*~ninlh%rW}_eoRN<)@N7aT^k% ztOwUacg{i7<2iHp!hSQKFWkP_R`UL;-Iv;Cj^Uz2!wn3L5p%zSfT)Qlz|)|Cyo)hR zcFNO|xE3s4rVjUGzK-7y6oy?5=ya=7Dun+KV^WILqcsKfy&B_})F<~fDFZ&IrsEE! z8neu;?YP(BZu`-;yp6eL!*$tkQFJl1)ajK) z({&h3Ln?P`Vos=+#>^K5uFk>rcpf9#vP!-7AlZURUpg;5RHBL!oml%Jh+FFW}sJ5@A4_? z=(scstEaZDF4p92%uTP(G{M7Wid<*?X!M%YFEYON{2!33G4rd}J2Be+%co48hIU7k zu8o3p5e{vn1yCiIstXe+mThk=WF^Tep%#g%$j~F z_qcZqF0}T#o=>?4BX^6(y#>auvghFTFkKNSL#u!zFyBS*V9#}Og?KtqtaFi?-kT^B z_f@Fw0~zu3(4-gIjE_ktoGZ@8h2XzkEMB#07Pgc>{x>0=kcq&p!w09D(}CZb=qaRE z3M4`rtu|_^GS?5Cyv{~Cd9U4u+P*p*rLI{JwzJ|_X|x5m4l<2)IJFw`)|Y=bx*X?` zm=%-mxOG17*&ud(h$kA|moW_8idILPqZiPL7Muv-yQB9hS_g60LORQ~3DOs*A4KYl zYKW^%OjFQdNDWom7XQR~ef;%f3*%{VLc7&McVdr;{~$Dmw9!K>?!%EbPR0GSQ1p~J z6CwNvq`l{v`;`T8wUYZ6r26fK9!7t+;2H?q2kD@zu4$-fCuPeZQ=I^Lix1Lr$XELz z5_>?x)MGpx#LR}cPgv4IkW{_ct4Pyw4*b+OQ+px8RwlmQ=~1)c$MQQ5sb@-SoMB^5 z+!9(3(p-)*=a`A3gSa1~sK>>wbA>85>dmq1R88vCnALR*(n*HeM;nkx zqwTNSN}RaEjxaT5-PszNJ<2_9>hA8=OhVypjd@^5pi?H_4O{_yAT1&P80&b#cMN>% zLwpS_&I??{uwjYzJ!oT-cExq$HyLSzd?Lj8HpUF%Tpsu=#PZXD1HG;wX|G2deb$vt zdbL4AUuF_~Z#sy3A9AlZpt0!Su7+$wk=vJzpZ1;XM4hjdI|=Hf)mR-^xEO7OyYL>e&zIr3mebiYedG?bB_I?=8VAk86sLfti^O>}=dL7C;kADEy@o_W;yE>YIY1hlp zLW<(*b!gHEl#4=**}j~UxY&gsuCm|*Zin^`pFXO?s6a!O4YcswYvW_R| z1kw8V%kO%W2yyfvt8w!C*!8-sH_`ss*T&33vr+1hZlUbHe739yx=F;_Ag0{!LMc2w zL+08n-*E3&B3f$Jl5@lG-mauc+}n)5Zs%$UH7`x6 zhVrxn&B?r42A@aE;_ChN0-qfy<6flR@o$(qFR33>y&h1Trrtp6!PUcSjIYB215p-w zg@ewIoz!2hUMnMm&QKQAa1=7j$)QwYw=@k>2ZQ9$s9Juvr zF1>mpbxxh|=m8K@4~}{baq9M0!rJoy7>8fF79?~gtorRkZHGF%g|R=s;HLh_$I@#o zIEo9Z_rYn?jHcyM8w_dmAfwx_zECyXv(P}K-E4Zzu9nmXp^igcHH6cVtLW4_e&aDT zq`AgfwMlO*EN}dc_V33XNU;bA39ycHPpW_NT*p)CQUsx zQ;^=*s|5;oDx}@;p*7pED{9H!5%SaZ(EC9&PWzaAuR)p1;>6bzxMnuAvrywhJxJ*m zeh*5$O{zSkzr7e?9Yk)p_8J=Kv`O_Fpi-f%BQZviMz6_okk7HWhoQtb2obK&G@JU} z8~7}QFpZyk5ti7q6TUb3&I;>@Ti51rbQfuKz@Y-_r*XYHhxj$qbBrEf%ZCc;Ri#s? zL|dWEiKv}t6FrLqG^!0y#TzWkgtGK+Rz;6Rc|7@`arLB~or78UfB*7A-g@VprUpB4 zjLWmQL1X(o8jT9Syd@2?(%X*oWUL8wkEgxR@bq^M+H9juI1NEs{a?w&$p4w8CzF=# z0Z4;}sF@DU4(P8Q^ma})%w9a + + + + org.sentrysoftware.maven + sentry-maven-skin + 6.2.00 + + + + true + metricshub, connector, hardware, system + + + + images/sentry-logo-179x75px.png + https://metricshub.com + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/it/metricshub-connectors/verify.groovy b/src/it/metricshub-connectors/verify.groovy new file mode 100644 index 0000000..9cdac14 --- /dev/null +++ b/src/it/metricshub-connectors/verify.groovy @@ -0,0 +1,262 @@ +// Verify that the files have been created properly +assert new File(basedir, "target/site/index.html").isFile() + +// +// Site checks for the Otel version +// + +// Main page: metricshub-connector-reference.html +File htmlFile = new File(basedir, "target/site/metricshub-connector-reference.html") +assert htmlFile.exists() : "Main metricshub-connector-reference.html page must be created" +String htmlText = htmlFile.text +assert htmlText.indexOf("MIB2Switch") > -1 : "metricshub-connector-reference: MIB2Switch must be listed" +assert htmlText.indexOf("GenericSwitchEnclosure") > -1 : "metricshub-connector-reference: GenericSwitchEnclosure must be listed" +assert htmlText.indexOf("GenericUPS") > -1 : "metricshub-connector-reference: GenericUPS must be listed" +assert htmlText.indexOf("HyperV") > -1 : "metricshub-connector-reference: HyperV must be listed" +assert htmlText.indexOf("IpmiTool") > -1 : "metricshub-connector-reference: IpmiTool must be listed" +assert htmlText.indexOf("Virsh") > -1 : "metricshub-connector-reference: Virsh must be listed" +assert htmlText.indexOf("LibreHardwareMonitor") > -1 : "metricshub-connector-reference: LibreHardwareMonitor must be listed" +assert htmlText.indexOf("LinuxMultipath") > -1 : "metricshub-connector-reference: LinuxMultipath must be listed" +assert htmlText.indexOf("LinuxIfConfigNetwork") > -1 : "metricshub-connector-reference: LinuxIfConfigNetwork must be listed" +assert htmlText.indexOf("LinuxIPNetwork") > -1 : "metricshub-connector-reference: LinuxIPNetwork must be listed" +assert htmlText.indexOf("lmsensors") > -1 : "metricshub-connector-reference: lmsensors must be listed" +assert htmlText.indexOf("MIB2") > -1 : "metricshub-connector-reference: MIB2 must be listed" +assert htmlText.indexOf("MIB2Linux") > -1 : "metricshub-connector-reference: MIB2Linux must be listed" +assert htmlText.indexOf("MIB2Solaris") > -1 : "metricshub-connector-reference: MIB2Solaris must be listed" +assert htmlText.indexOf("MIB2NT") > -1 : "metricshub-connector-reference: MIB2NT must be listed" +assert htmlText.indexOf("NvidiaSmi") > -1 : "metricshub-connector-reference: NvidiaSmi must be listed" +assert htmlText.indexOf("VMwareESXiDisksIPMI") > -1 : "metricshub-connector-reference: VMwareESXiDisksIPMI must be listed" +assert htmlText.indexOf("VMwareESXiDisksStorage") > -1 : "metricshub-connector-reference: VMwareESXiDisksStorage must be listed" +assert htmlText.indexOf("VMwareESXVMSNMP") > -1 : "metricshub-connector-reference: VMwareESXVMSNMP must be listed" +assert htmlText.indexOf("VMwareESXi") > -1 : "metricshub-connector-reference: VMwareESXi must be listed" +assert htmlText.indexOf("VMwareESX4i") > -1 : "metricshub-connector-reference: VMwareESX4i must be listed" +assert htmlText.indexOf("VMwareESX5iLUNesxcli") > -1 : "metricshub-connector-reference: VMwareESX5iLUNesxcli must be listed" +assert htmlText.indexOf("VMwareESX5iLUNSSH") > -1 : "metricshub-connector-reference: VMwareESX5iLUNSSH must be listed" +assert htmlText.indexOf("DiskPart") > -1 : "metricshub-connector-reference: DiskPart must be listed" +assert htmlText.indexOf("WinStorageSpaces") > -1 : "metricshub-connector-reference: WinStorageSpaces must be listed" +assert htmlText.indexOf("GenBatteryNT") > -1 : "metricshub-connector-reference: GenBatteryNT must be listed" +assert htmlText.indexOf("WBEMGenDiskNT") > -1 : "metricshub-connector-reference: WBEMGenDiskNT must be listed" +assert htmlText.indexOf("WBEMGenHBA") > -1 : "metricshub-connector-reference: WBEMGenHBA must be listed" +assert htmlText.indexOf("WBEMGenLUN") > -1 : "metricshub-connector-reference: WBEMGenLUN must be listed" +assert htmlText.indexOf("WBEMGenNetwork") > -1 : "metricshub-connector-reference: WBEMGenNetwork must be listed" + +// Check generated reference files +String directoryPath = 'target/site/connectors' +String [] fileNamesToCheck = [ + 'diskpart.html', + 'genbatterynt.html', + 'genericswitchenclosure.html', + 'genericups.html', + 'hyperv.html', + 'ipmitool.html', + 'librehardwaremonitor.html', + 'linuxifconfignetwork.html', + 'linuxipnetwork.html', + 'linuxmultipath.html', + 'lmsensors.html', + 'mib2.html', + 'mib2linux.html', + 'mib2nt.html', + 'mib2solaris.html', + 'mib2switch.html', + 'nvidiasmi.html', + 'virsh.html', + 'vmwareesx4i.html', + 'vmwareesx5ilunesxcli.html', + 'vmwareesx5ilunssh.html', + 'vmwareesxi.html', + 'vmwareesxidisksipmi.html', + 'vmwareesxidisksstorage.html', + 'vmwareesxvmsnmp.html', + 'wbemgendisknt.html', + 'wbemgenhba.html', + 'wbemgenlun.html', + 'wbemgennetwork.html', + 'winstoragespaces.html' +] + +fileNamesToCheck.each { fileName -> + File file = new File(basedir, "$directoryPath/$fileName") + + assert file.exists() : "File $fileName does not exist in the $directoryPath directory" + assert htmlText.indexOf("href=\"connectors/$fileName\"") > -1 : "metricshub-connector-reference: href=connectors/$fileName must be listed" +} + + +// platform-requirements.html +htmlFile = new File(basedir, "target/site/platform-requirements.html") +assert htmlFile.exists() : "platform-requirements.html must have been created" +htmlText = htmlFile.text +assert htmlText.indexOf("IPMI") > -1 : "platform-requirements: 'IPMI' must be listed" +assert htmlText.indexOf("MIB-2 Standard SNMP Agent - Network Interfaces") > -1 : "platform-requirements: 'MIB-2 Standard SNMP Agent - Network Interfaces' must be listed" +assert htmlText.indexOf("KVM, QEMU, Xen and Hypervisors (virsh)") > -1 : "platform-requirements: 'KVM, QEMU, Xen and Hypervisors (virsh)' must be listed" +assert htmlText.indexOf("lm_sensors") > -1 : "platform-requirements: 'lm_sensors' must be listed" +assert htmlText.indexOf("Linux - Network (ifconfig)") > -1 : "platform-requirements: 'Linux - Network (ifconfig)' must be listed" +assert htmlText.indexOf("Linux - Network (ip)") > -1 : "platform-requirements: 'Linux - Network (ip)' must be listed" +assert htmlText.indexOf("Linux - Multipath") > -1 : "platform-requirements: 'Linux - Multipath' must be listed" +assert htmlText.indexOf("Nvidia-Smi") > -1 : "platform-requirements: 'Nvidia-Smi' must be listed" +assert htmlText.indexOf("MIB-2 Standard SNMP Agent - Network Interfaces - Linux") > -1 : "platform-requirements: 'MIB-2 Standard SNMP Agent - Network Interfaces - Linux' must be listed" +assert htmlText.indexOf("Hyper-V") > -1 : "platform-requirements: 'Hyper-V' must be listed" +assert htmlText.indexOf("WMI - Battery") > -1 : "platform-requirements: 'WMI - Battery' must be listed" +assert htmlText.indexOf("WMI - Network") > -1 : "platform-requirements: 'WMI - Network' must be listed" +assert htmlText.indexOf("WMI - LUN") > -1 : "platform-requirements: 'WMI - LUN' must be listed" +assert htmlText.indexOf("Windows Storage Spaces (WMI)") > -1 : "platform-requirements: 'Windows Storage Spaces (WMI)' must be listed" +assert htmlText.indexOf("WMI - Disks") > -1 : "platform-requirements: 'WMI - Disks' must be listed" +assert htmlText.indexOf("WMI - HBA") > -1 : "platform-requirements: 'WMI - HBA' must be listed" +assert htmlText.indexOf("Windows - DiskPart") > -1 : "platform-requirements: 'Windows - DiskPart' must be listed" +assert htmlText.indexOf("MIB-2 Standard SNMP Agent - Network Interfaces - Windows") > -1 : "platform-requirements: 'MIB-2 Standard SNMP Agent - Network Interfaces - Windows' must be listed" +assert htmlText.indexOf("Libre Hardware Monitor") > -1 : "platform-requirements: 'Libre Hardware Monitor' must be listed" +assert htmlText.indexOf("Ethernet Switch with Sensors (SNMP)") > -1 : "platform-requirements: 'Ethernet Switch with Sensors (SNMP)' must be listed" +assert htmlText.indexOf("Generic Ethernet Switch") > -1 : "platform-requirements: 'Generic Ethernet Switch' must be listed" +assert htmlText.indexOf("MIB-2 Standard SNMP Agent - Network Interfaces - Solaris") > -1 : "platform-requirements: 'MIB-2 Standard SNMP Agent - Network Interfaces - Solaris' must be listed" +assert htmlText.indexOf("Generic UPS") > -1 : "platform-requirements: 'Generic UPS' must be listed" +assert htmlText.indexOf("VMware ESXi 5 LUN (esxcli)") > -1 : "platform-requirements: 'VMware ESXi 5 LUN (esxcli)' must be listed" +assert htmlText.indexOf("VMware ESXi 5 LUN (SSH)") > -1 : "platform-requirements: 'VMware ESXi 5 LUN (SSH)' must be listed" +assert htmlText.indexOf("VMware ESXi 3.x") > -1 : "platform-requirements: 'VMware ESXi 3.x' must be listed" +assert htmlText.indexOf("VMware ESXi - Disks (VMware)") > -1 : "platform-requirements: 'VMware ESXi - Disks (VMware)' must be listed" +assert htmlText.indexOf("VMware ESXi 4.x") > -1 : "platform-requirements: 'VMware ESXi 4.x' must be listed" +assert htmlText.indexOf("VMware ESXi - Disks (IPMI)") > -1 : "platform-requirements: 'VMware ESXi - Disks (IPMI)' must be listed" +assert htmlText.indexOf("VMware ESXi - Virtual Machines (SNMP)") > -1 : "platform-requirements: 'VMware ESXi - Virtual Machines (SNMP)' must be listed" + +// IpmiTool +htmlText = new File(basedir, "target/site/connectors/ipmitool.html").text +assert htmlText.indexOf("ipmi:") > -1 && htmlText.indexOf("wmi:") > -1 && htmlText.indexOf("ssh:") > -1 : "IpmiTool: Examples must list ipmi, wmi and ssh" +assert htmlText =~ /metricshub.*-t management.*-f IpmiTool.*--ipmi/ : "IpmiTool: CLI must specify -t management -f IpmiTool --ipmi" + +// IpmiTool Enclosure +assert htmlText.indexOf('enclosure') > -1 : 'IpmiTool: the enclosure monitor must be listed' +assert htmlText.indexOf('hw.enclosure.energy') > -1 : 'IpmiTool: the hw.enclosure.energy metric must be listed for the enclosure monitor' +assert htmlText.indexOf('hw.enclosure.power') > -1 : 'IpmiTool: the hw.enclosure.power metric must be listed for the enclosure monitor' +assert htmlText.indexOf('hw.status{hw.type="enclosure", state="degraded|failed|ok"}') > -1 : 'IpmiTool: the hw.status{hw.type="enclosure", state="degraded|failed|ok"} metric must be listed for the enclosure monitor' +assert htmlText.indexOf('hw.status{hw.type="enclosure", state="present"}') > -1 : 'IpmiTool: the hw.status{hw.type="enclosure", state="present"} metric must be listed for the enclosure monitor' + +// IpmiTool Fan +assert htmlText.indexOf('fan') > -1 : 'IpmiTool: the fan monitor must be listed' +assert htmlText.indexOf('hw.fan.speed') > -1 : 'IpmiTool: the hw.fan.speed metric must be listed for the fan monitor' +assert htmlText.indexOf('hw.fan.speed.limit{limit_type="low.critical"}') > -1 : 'IpmiTool: the hw.fan.speed.limit{limit_type="low.critical"} metric must be listed for the fan monitor' +assert htmlText.indexOf('hw.fan.speed.limit{limit_type="low.degraded"}') > -1 : 'IpmiTool: the hw.fan.speed.limit{limit_type="low.degraded"} metric must be listed for the fan monitor' +assert htmlText.indexOf('hw.status{hw.type="fan", state="degraded|failed|ok"}') > -1 : 'IpmiTool: the hw.status{hw.type="fan", state="degraded|failed|ok"} metric must be listed for the fan monitor' +assert htmlText.indexOf('hw.status{hw.type="fan", state="present"}') > -1 : 'IpmiTool: the hw.status{hw.type="fan", state="present"} metric must be listed for the fan monitor' + +// IpmiTool Temperature +assert htmlText.indexOf('temperature') > -1 : 'IpmiTool: the temperature monitor must be listed' +assert htmlText.indexOf('hw.status{hw.type="temperature", state="present"}') > -1 : 'IpmiTool: the hw.status{hw.type="temperature", state="present"} metric must be listed for the temperature monitor' +assert htmlText.indexOf('hw.temperature') > -1 : 'IpmiTool: the hw.temperature metric must be listed for the temperature monitor' +assert htmlText.indexOf('hw.temperature.limit{limit_type="high.critical"}') > -1 : 'IpmiTool: the hw.temperature.limit{limit_type="high.critical"} metric must be listed for the temperature monitor' +assert htmlText.indexOf('hw.temperature.limit{limit_type="high.degraded"}') > -1 : 'IpmiTool: the hw.temperature.limit{limit_type="high.degraded"} metric must be listed for the temperature monitor' + +// IpmiTool Voltage +assert htmlText.indexOf('voltage') > -1 : 'IpmiTool: the voltage monitor must be listed' +assert htmlText.indexOf('hw.status{hw.type="voltage", state="present"}') > -1 : 'IpmiTool: the hw.status{hw.type="voltage", state="present"} metric must be listed for the voltage monitor' +assert htmlText.indexOf('hw.voltage') > -1 : 'IpmiTool: the hw.voltage metric must be listed for the voltage monitor' +assert htmlText.indexOf('hw.voltage.limit{limit_type="high.degraded"}') > -1 : 'IpmiTool: the hw.voltage.limit{limit_type="high.degraded"} metric must be listed for the voltage monitor' +assert htmlText.indexOf('hw.voltage.limit{limit_type="low.critical"}') > -1 : 'IpmiTool: the hw.voltage.limit{limit_type="low.critical"} metric must be listed for the voltage monitor' + +// IpmiTool Power Supply +assert htmlText.indexOf('power_supply') > -1 : 'IpmiTool: the power_supply monitor must be listed' +assert htmlText.indexOf('hw.status{hw.type="power_supply", state="degraded|failed|ok"}') > -1 : 'IpmiTool: the hw.status{hw.type="power_supply", state="degraded|failed|ok"} metric must be listed for the Power Supply monitor' +assert htmlText.indexOf('hw.status{hw.type="power_supply", state="present"}') > -1 : 'IpmiTool: the hw.status{hw.type="power_supply", state="present"} metric must be listed for the Power Supply monitor' + +// IpmiTool CPU +assert htmlText.indexOf('cpu') > -1 : 'IpmiTool: the cpu monitor must be listed' +assert htmlText.indexOf('hw.status{hw.type="cpu", state="degraded|failed|ok"}') > -1 : 'IpmiTool: the hw.status{hw.type="cpu", state="degraded|failed|ok"} metric must be listed for the CPU monitor' +assert htmlText.indexOf('hw.status{hw.type="cpu", state="present"}') > -1 : 'IpmiTool: the hw.status{hw.type="cpu", state="present"} metric must be listed for the CPU monitor' + +// IpmiTool Memory +assert htmlText.indexOf('memory') > -1 : 'IpmiTool: the memory monitor must be listed' +assert htmlText.indexOf('hw.memory.limit') > -1 : 'IpmiTool: the hw.memory.limit metric must be listed for the Memory monitor' +assert htmlText.indexOf('hw.status{hw.type="memory", state="degraded|failed|ok"}') > -1 : 'IpmiTool: the hw.status{hw.type="memory", state="degraded|failed|ok"} metric must be listed for the Memory monitor' +assert htmlText.indexOf('hw.status{hw.type="memory", state="present"}') > -1 : 'IpmiTool: the hw.status{hw.type="memory", state="present"} metric must be listed for the Memory monitor' + +// IpmiTool Physical Disk +assert htmlText.indexOf('physical_disk') > -1 : 'IpmiTool: the physical_disk monitor must be listed' +assert htmlText.indexOf('hw.status{hw.type="physical_disk", state="degraded|failed|ok"}') > -1 : 'IpmiTool: the hw.status{hw.type="physical_disk", state="degraded|failed|ok"} metric must be listed for the Physical Disk monitor' +assert htmlText.indexOf('hw.status{hw.type="physical_disk", state="present"}') > -1 : 'IpmiTool: the hw.status{hw.type="physical_disk", state="present"} metric must be listed for the Physical Disk monitor' + +// IpmiTool LED +assert htmlText.indexOf('led') > -1 : 'IpmiTool: the led monitor must be listed' +assert htmlText.indexOf('hw.status{hw.type="led", state="degraded|failed|ok"}') > -1 : 'IpmiTool: the hw.status{hw.type="led", state="degraded|failed|ok"} metric must be listed for the LED monitor' +assert htmlText.indexOf('hw.status{hw.type="led", state="present"}') > -1 : 'IpmiTool: the hw.status{hw.type="led", state="present"} metric must be listed for the LED monitor' + +// IpmiTool Other Device +assert htmlText.indexOf('other_device') > -1 : 'IpmiTool: the other_device monitor must be listed' +assert htmlText.indexOf('hw.status{hw.type="other_device", state="degraded|failed|ok"}') > -1 : 'IpmiTool: the hw.status{hw.type="other_device", state="degraded|failed|ok"} metric must be listed for the Other Device monitor' +assert htmlText.indexOf('hw.status{hw.type="other_device", state="present"}') > -1 : 'IpmiTool: the hw.status{hw.type="other_device", state="present"} metric must be listed for the Other Device monitor' + +// IpmiTool Blade +assert htmlText.indexOf('blade') > -1 : 'IpmiTool: the blade monitor must be listed' +assert htmlText.indexOf('hw.status{hw.type="blade", state="degraded|failed|ok"}') > -1 : 'IpmiTool: the hw.status{hw.type="blade", state="degraded|failed|ok"} metric must be listed for the Blade monitor' +assert htmlText.indexOf('hw.status{hw.type="blade", state="present"}') > -1 : 'IpmiTool: the hw.status{hw.type="blade", state="present"} metric must be listed for the Blade monitor' + +// IpmiTool Battery +assert htmlText.indexOf('battery') > -1 : 'IpmiTool: the battery monitor must be listed' +assert htmlText.indexOf('hw.status{hw.type="battery", state="degraded|failed|ok"}') > -1 : 'IpmiTool: the hw.status{hw.type="battery", state="degraded|failed|ok"} metric must be listed for the Battery monitor' +assert htmlText.indexOf('hw.status{hw.type="battery", state="present"}') > -1 : 'IpmiTool: the hw.status{hw.type="battery", state="present"} metric must be listed for the Battery monitor' + +// MIB2NT +htmlText = new File(basedir, "target/site/connectors/mib2nt.html").text +assert htmlText.indexOf("This connector supersedes") > - 1 : "MIB2NT: Page must indicate which connectors are superseded" +assert htmlText.indexOf('href="wbemgennetwork.html"') > -1 : "MIB2NT: Path to superseded connector page must be listed" +assert htmlText.indexOf("SNMP") > -1 : "MIB2NT: SNMP Technology must be listed" +assert htmlText.indexOf("Microsoft Windows") > - 1 : "MIB2NT: OS type must have been properly translated" +assert htmlText.indexOf("
  • ") == -1 : "MIB2NT: No empty list item must be found" +assert htmlText.indexOf("Metrics") > -1 : "MIB2NT: The Metrics section must be listed" + +// WBEMGenNetwork +htmlText = new File(basedir, "target/site/connectors/wbemgennetwork.html").text +assert htmlText.indexOf("This connector is superseded by") > - 1 : "WBEMGenNetwork: Page must indicate this connector is superseded by another one" +assert htmlText.indexOf('href="mib2nt.html"') > -1 : "WBEMGenNetwork: Path to superseding connector page must be present" +assert htmlText.indexOf("wmi:") > -1 : "WBEMGenNetwork: Example must list wmi" +assert htmlText =~ /metricshub.*-f WBEMGenNetwork.*--wmi/ : "WBEMGetNetwork: CLI must specify WBEMGenNetwork and --wmi" + +// lmsensors +htmlText = new File(basedir, "target/site/connectors/lmsensors.html").text +assert htmlText =~ /metricshub.*-t linux.*--ssh.*--sudo-command-list/ : "lmsensors: CLI must specify linux and ssh and use --sudo-command-list" + +// MIB2 +htmlText = new File(basedir, "target/site/connectors/mib2.html").text +assert htmlText.indexOf('

    Description

    ') > - 1 : "MIB2: Page must indicate 'Description' as HTML H3 element" +assert htmlText.indexOf("This connector discovers the enclosure and Ethernet ports of a system equipped with an MIB-2 standard SNMP Agent.") > - 1 : "MIB2: Page must indicate a description" +assert htmlText.indexOf('

    Target

    ') > - 1 : "MIB2: Page must indicate 'Target' as HTML H3 element" +assert htmlText.indexOf("Typical platform:") > - 1 : "MIB2: 'Typical platform:' must be present" +assert htmlText.indexOf("Any system with SNMP") > - 1 : "MIB2: typical platform text must be present" +assert htmlText.indexOf("Operating systems:") > - 1 : "MIB2 'Operating systems:' must be present" +assert htmlText.indexOf("Network Device") > -1 : "MIB2: operating system 'Network Device' must be present" +assert htmlText.indexOf("Out-Of-Band") > -1 : "MIB2: operating system 'Out-Of-Band' must be present" +assert htmlText.indexOf("HP-UX") > -1 : "MIB2: operating system 'HP-UX' must be present" +assert htmlText.indexOf("Storage System") > -1 : "MIB2: operating system 'Storage System' must be present" +assert htmlText.indexOf("HP OpenVMS") > -1 : "MIB2: operating system 'HP OpenVMS' must be present" +assert htmlText.indexOf("HP Tru64") > -1 : "MIB2: operating system 'HP Tru64' must be present" +assert htmlText.indexOf('

    Prerequisites

    ') > - 1 : "MIB2: Page must indicate 'Prerequisites' as HTML H3 element" +assert htmlText.indexOf("Leverages:") > - 1 : "MIB2 'Leverages:' must be present" +assert htmlText.indexOf("MIB-2 Standard SNMP Agent") > - 1 : "MIB2 leverages text must be present" +assert htmlText.indexOf("Technology and protocols:") > - 1 : "MIB2 'Technology and protocols:' must be present" +assert htmlText.indexOf("SNMP") > - 1 : "MIB2 SNMP protocol must be present" +assert htmlText.indexOf('

    Examples

    ') > - 1 : "MIB2: Page must indicate 'Examples' as HTML H3 element" +assert htmlText.indexOf('

    CLI

    ') > - 1 : "MIB2: Page must indicate 'CLI' as HTML H4 element" +assert htmlText.indexOf("metricshub HOSTNAME -t management -f MIB2 --snmp v2c --community public") > - 1 : "MIB2: Page must indicate the expected CLI example" +assert htmlText.indexOf('

    metricshub.yaml

    ') > - 1 : "MIB2: Page must indicate 'metricshub.yaml' as HTML H4 element" +assert htmlText.indexOf("snmp:") > - 1 : "MIB2: 'snmp:' yaml section must be present" +assert htmlText.indexOf("v2c") > - 1 : "MIB2: version 'v2c' must be present in the yaml configuration example" +assert htmlText.indexOf('

    Connector Activation Criteria

    ') > - 1 : "MIB2: Page must indicate 'Connector Activation Criteria' as HTML H3 element" +assert htmlText.indexOf("1.3.6.1.2.1.2.2.1") > - 1 : "MIB2: Page must indicate OID '1.3.6.1.2.1.2.2.1'" +assert htmlText.indexOf('

    Metrics

    ') > - 1 : "MIB2: Page must indicate 'Metrics' as HTML H3 element" + +// MIB2 Network Metrics +assert htmlText.indexOf('hw.errors{hw.type="network"}') > -1 : 'MIB2: the hw.errors{hw.type="network"} metric must be listed for the Network monitor' +assert htmlText.indexOf('hw.network.bandwidth.limit') > -1 : "MIB2: the hw.network.bandwidth.limit metric must be listed for the Network monitor" +assert htmlText.indexOf('hw.network.io{direction="receive"}') > -1 : 'MIB2: the hw.network.io{direction="receive"} metric must be listed for the Network monitor' +assert htmlText.indexOf('hw.network.io{direction="transmit"}') > -1 : 'MIB2: the hw.network.io{direction="transmit"} metric must be listed for the Network monitor' +assert htmlText.indexOf('hw.network.packets{direction="receive"}') > -1 : 'MIB2: the hw.network.packets{direction="receive"} metric must be listed for the Network monitor' +assert htmlText.indexOf('hw.network.packets{direction="transmit"}') > -1 : 'MIB2: the hw.network.packets{direction="transmit"} metric must be listed for the Network monitor' +assert htmlText.indexOf('hw.network.up') > -1 : "MIB2: the hw.network.up metric must be listed for the Network monitor" +assert htmlText.indexOf('hw.status{hw.type="network", state="degraded|failed|ok"}') > -1 : 'MIB2: the hw.status{hw.type="network", state="degraded|failed|ok"} metric must be listed for the Network monitor' +assert htmlText.indexOf('hw.status{hw.type="network", state="present"}') > -1 : 'MIB2: the hw.status{hw.type="network", state="present"} metric must be listed for the Network monitor' + +// MIB2 Network Attributes +assert htmlText.indexOf('device_type') > -1 : "MIB2: the 'device_type' attribute must be listed for the Network monitor" +assert htmlText.indexOf('hw.parent.type') > -1 : "MIB2: the 'hw.parent.type' attribute must be listed for the Network monitor" +assert htmlText.indexOf('id') > -1 : "MIB2: the 'id' attribute must be listed for the Network monitor" +assert htmlText.indexOf('name') > -1 : "MIB2: the 'name' attribute must be listed for the Network monitor" +assert htmlText.indexOf('physical_address') > -1 : "MIB2: the 'physical_address' attribute must be listed for the Network monitor" +assert htmlText.indexOf('physical_address_type') > -1 : "MIB2: the 'physical_address_type' attribute must be listed for the Network monitor" diff --git a/src/it/settings.xml b/src/it/settings.xml new file mode 100644 index 0000000..2665974 --- /dev/null +++ b/src/it/settings.xml @@ -0,0 +1,40 @@ + + + + + + + it-repo + + true + + + + 1local.central + @localRepositoryUrl@ + + true + + + true + + + + + + 1local.central + @localRepositoryUrl@ + + true + + + true + + + + + + \ No newline at end of file diff --git a/src/main/java/lombok.config b/src/main/java/lombok.config new file mode 100644 index 0000000..cd76c71 --- /dev/null +++ b/src/main/java/lombok.config @@ -0,0 +1,2 @@ +lombok.nonNull.exceptionType = IllegalArgumentException +lombok.addLombokGeneratedAnnotation = true \ No newline at end of file diff --git a/src/main/java/org/sentrysoftware/maven/metricshub/connector/AbstractConnectorReport.java b/src/main/java/org/sentrysoftware/maven/metricshub/connector/AbstractConnectorReport.java new file mode 100644 index 0000000..1d9ae15 --- /dev/null +++ b/src/main/java/org/sentrysoftware/maven/metricshub/connector/AbstractConnectorReport.java @@ -0,0 +1,125 @@ +package org.sentrysoftware.maven.metricshub.connector; + +/*- + * ╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲ + * MetricsHub Connector Maven Plugin + * ჻჻჻჻჻჻ + * Copyright (C) 2023 Sentry Software + * ჻჻჻჻჻჻ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱ + */ + +import com.fasterxml.jackson.databind.JsonNode; +import java.io.File; +import java.io.IOException; +import java.util.Locale; +import java.util.Map; +import org.apache.maven.doxia.sink.Sink; +import org.apache.maven.plugin.logging.Log; +import org.apache.maven.plugins.annotations.Parameter; +import org.apache.maven.reporting.AbstractMavenReport; +import org.apache.maven.reporting.MavenReportException; +import org.sentrysoftware.maven.metricshub.connector.parser.ConnectorLibraryParser; + +/** + * An abstract base class for Maven reports related to connectors. + */ +public abstract class AbstractConnectorReport extends AbstractMavenReport { + + /** + * Where all the .yaml files are located. Only the .yaml files will actually be parsed. + */ + @Parameter(defaultValue = "${project.basedir}/src/main/connector", property = "sourceDirectory", required = true) + protected File sourceDirectory; + + protected Log logger; + + protected Map connectors; + + @Override + protected void executeReport(Locale locale) throws MavenReportException { + // Get and set the logger + logger = getLog(); + + // Is this an existing directory? + if (!sourceDirectory.exists()) { + final String message = String.format("sourceDirectory '%s' does not exist", sourceDirectory); + logger.error(message); + throw new MavenReportException(message); + } + + if (!sourceDirectory.isDirectory()) { + final String message = String.format("sourceDirectory '%s' is not a directory", sourceDirectory); + logger.error(message); + throw new MavenReportException(message); + } + + // Need to create outputDirectory? + if (!outputDirectory.exists() && !outputDirectory.mkdirs()) { + final String message = "Could not create outputDirectory: " + outputDirectory.getAbsolutePath(); + logger.error(message); + throw new MavenReportException(message); + } + + // Parse the connector library + connectors = parseConnectors(); + + // Produce the report + doReport(); + } + + /** + * Performs the main logic of generating the report. Subclasses should implement this method to define + * the specific report generation logic. + */ + protected abstract void doReport() throws MavenReportException; + + /** + * Parses the connector library located at the specified source directory and + * returns a mapping of connector identifiers to their corresponding JsonNodes. + * + * @return A {@code Map} containing connector names as keys and their associated + * {@link JsonNode} objects as values. + * @throws MavenReportException If an error occurs during the parsing process, + * including IO errors or parsing failures. + */ + protected Map parseConnectors() throws MavenReportException { + try { + return new ConnectorLibraryParser().parse(sourceDirectory.toPath()); + } catch (IOException e) { + final String message = String.format( + "An error occurred during the parsing of the connector library at %s. Details: %s", + sourceDirectory.getAbsolutePath(), + e.getMessage() + ); + logger.error(message); + throw new MavenReportException(message, e); + } + } + + /** + * Retrieves the main Doxia sink. + * + * @return The main Doxia sink. + * @throws MavenReportException If an error occurs while retrieving the sink. + */ + protected Sink getMainSink() throws MavenReportException { + final Sink mainSink = getSink(); + if (mainSink == null) { + logger.error("Could not get the Doxia sink"); + throw new MavenReportException("Could not get the Doxia sink"); + } + return mainSink; + } +} diff --git a/src/main/java/org/sentrysoftware/maven/metricshub/connector/Constants.java b/src/main/java/org/sentrysoftware/maven/metricshub/connector/Constants.java new file mode 100644 index 0000000..b6947b2 --- /dev/null +++ b/src/main/java/org/sentrysoftware/maven/metricshub/connector/Constants.java @@ -0,0 +1,49 @@ +package org.sentrysoftware.maven.metricshub.connector; + +/*- + * ╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲ + * MetricsHub Connector Maven Plugin + * ჻჻჻჻჻჻ + * Copyright (C) 2023 Sentry Software + * ჻჻჻჻჻჻ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱ + */ + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.json.JsonMapper; +import com.fasterxml.jackson.dataformat.yaml.YAMLFactory; +import lombok.AccessLevel; +import lombok.NoArgsConstructor; + +/** + * Constants for the plugin. + */ +@NoArgsConstructor(access = AccessLevel.PRIVATE) +public class Constants { + + /** + * ObjectMapper provides functionality for reading the connector (YAML file) + */ + public static final ObjectMapper YAML_OBJECT_MAPPER = JsonMapper.builder(new YAMLFactory()).build(); + + /** + * Name of the subdirectory that will contain the pages for each connector + */ + public static final String CONNECTOR_SUBDIRECTORY_NAME = "connectors"; + + /** + * Empty string + */ + public static final String EMPTY = ""; +} diff --git a/src/main/java/org/sentrysoftware/maven/metricshub/connector/PlatformReport.java b/src/main/java/org/sentrysoftware/maven/metricshub/connector/PlatformReport.java new file mode 100644 index 0000000..31b7293 --- /dev/null +++ b/src/main/java/org/sentrysoftware/maven/metricshub/connector/PlatformReport.java @@ -0,0 +1,162 @@ +package org.sentrysoftware.maven.metricshub.connector; + +/*- + * ╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲ + * MetricsHub Connector Maven Plugin + * ჻჻჻჻჻჻ + * Copyright (C) 2023 Sentry Software + * ჻჻჻჻჻჻ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱ + */ + +import com.fasterxml.jackson.databind.JsonNode; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Optional; +import java.util.stream.Collectors; +import org.apache.maven.plugins.annotations.LifecyclePhase; +import org.apache.maven.plugins.annotations.Mojo; +import org.apache.maven.plugins.annotations.ResolutionScope; +import org.apache.maven.reporting.MavenReportException; +import org.sentrysoftware.maven.metricshub.connector.producer.ConnectorJsonNodeReader; +import org.sentrysoftware.maven.metricshub.connector.producer.PlatformReportProducer; +import org.sentrysoftware.maven.metricshub.connector.producer.model.common.OsType; +import org.sentrysoftware.maven.metricshub.connector.producer.model.common.TechnologyType; +import org.sentrysoftware.maven.metricshub.connector.producer.model.platform.Platform; + +/** + * Builds the Platform Requirements page (platform-requirements.html) based + * on the content of the connector files. + * + *

    + * This plugin goal is a report goal that works in the site build lifecycle. It + * simply needs to be declared in the report section of the pom.xml. + *

    + *

    + * The goal actually reads the .yaml files (Connectors) located in ${sourceDirectory} and + * produces one HTML Web page that is decorated using the specified Maven skin in + * site.xml. + *

    + * + */ +@Mojo( + name = "metricshub-connector-platforms", + aggregator = false, + defaultPhase = LifecyclePhase.SITE, + requiresDependencyResolution = ResolutionScope.RUNTIME, + requiresOnline = false, + requiresProject = true, + threadSafe = true +) +public class PlatformReport extends AbstractConnectorReport { + + @Override + public String getOutputName() { + return "platform-requirements"; + } + + @Override + public String getName(Locale locale) { + return "Supported Platform and Requirements"; + } + + @Override + public String getDescription(Locale locale) { + return "List of all supported platforms by the MetricsHub Connectors in ${project.name} ${project.version}, grouped by system type."; + } + + @Override + protected void doReport() throws MavenReportException { + // Some info + logger.info( + String.format( + "Generating %s.html for %s %s from %s", + getOutputName(), + project.getDescription(), + project.getVersion(), + sourceDirectory + ) + ); + + new PlatformReportProducer(logger).produce(getMainSink(), determineOsPlatforms()); + } + + /** + * Determine the list the platforms covered by the connectors collectively. + * + * @return {@link Map} of {@link Platform} instances indexed by OS. + */ + private Map> determineOsPlatforms() { + final Map> osPlatforms = new HashMap<>(); + + for (Entry connectorEntry : connectors.entrySet()) { + final String connectorId = connectorEntry.getKey(); + final JsonNode connector = connectorEntry.getValue(); + + // Create a new reader to fetch connector information + final ConnectorJsonNodeReader connectorJsonNodeReader = new ConnectorJsonNodeReader(connector); + + final List osList = OsType.mapToDisplayNames(connectorJsonNodeReader.getAppliesTo()); + + for (String os : osList) { + final String osToDisplay; + final String displayName = connectorJsonNodeReader.getDisplayName(); + if (os.equalsIgnoreCase(OsType.OOB.getDisplayName()) && connectorJsonNodeReader.hasBladeMonitorJob()) { + // Special case for OOB: if the connector discovers blade instances, we'll say it's a Blade Chassis + osToDisplay = "Blade Chassis"; + } else if (displayName.toLowerCase().contains("vmware")) { + // Special case for VMware + osToDisplay = "VMware ESX"; + } else { + osToDisplay = os; + } + + // If the specified osToDisplay key is not already associated with a list of platforms, + // attempt to compute its value using a new array list. + final List platformList = osPlatforms.computeIfAbsent(osToDisplay, k -> new ArrayList<>()); + + // Create a new platform and attempt to find the same platform in the platform list + final Platform newPlatform = new Platform( + connectorJsonNodeReader.getPlatformsOrDefault("N/A"), + osToDisplay, + connectorJsonNodeReader + .getTechnologies() + .stream() + .map(TechnologyType::getDisplayName) + .collect(Collectors.joining(", ")) + ); + + final Optional maybePlaform = platformList + .stream() + .filter(existingPlatform -> existingPlatform.equals(newPlatform)) + .findFirst(); + + final String reliesOn = connectorJsonNodeReader.getReliesOnOrDefault("N/A"); + + // Found? + if (maybePlaform.isPresent()) { + maybePlaform.get().addConnectorInformation(connectorId, displayName, reliesOn); + } else { + newPlatform.addConnectorInformation(connectorId, displayName, reliesOn); + platformList.add(newPlatform); + } + } + } + return osPlatforms; + } +} diff --git a/src/main/java/org/sentrysoftware/maven/metricshub/connector/ReferenceReport.java b/src/main/java/org/sentrysoftware/maven/metricshub/connector/ReferenceReport.java new file mode 100644 index 0000000..84b8e14 --- /dev/null +++ b/src/main/java/org/sentrysoftware/maven/metricshub/connector/ReferenceReport.java @@ -0,0 +1,175 @@ +package org.sentrysoftware.maven.metricshub.connector; + +/*- + * ╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲ + * MetricsHub Connector Maven Plugin + * ჻჻჻჻჻჻ + * Copyright (C) 2023 Sentry Software + * ჻჻჻჻჻჻ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱ + */ + +import static org.sentrysoftware.maven.metricshub.connector.Constants.CONNECTOR_SUBDIRECTORY_NAME; + +import com.fasterxml.jackson.databind.JsonNode; +import java.io.File; +import java.io.IOException; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.Map.Entry; +import org.apache.maven.doxia.sink.Sink; +import org.apache.maven.plugins.annotations.LifecyclePhase; +import org.apache.maven.plugins.annotations.Mojo; +import org.apache.maven.plugins.annotations.ResolutionScope; +import org.apache.maven.reporting.MavenReportException; +import org.sentrysoftware.maven.metricshub.connector.producer.ConnectorJsonNodeReader; +import org.sentrysoftware.maven.metricshub.connector.producer.ConnectorPageReferenceProducer; +import org.sentrysoftware.maven.metricshub.connector.producer.MainPageReferenceProducer; +import org.sentrysoftware.maven.metricshub.connector.producer.SinkHelper; + +/** + * This Maven report goal builds a Reference Guide for the Connector Library. + * + * It is invoked during the Maven site generation process.
    + * + * It takes the source code of the connectors as input then generates a reference guide that describes the + * connectors in detail. + *

    + * This plugin goal is a report goal that works in the site build lifecycle. It + * simply needs to be declared in the report section of the pom.xml. + *

    + *

    + * The goal actually reads the .yaml files (Connectors) located in ${sourceDirectory} and + * produces HTML Web pages that are decorated using the specified Maven skin in + * site.xml. + *

    + */ +@Mojo( + name = "metricshub-connector-reference", + aggregator = false, + defaultPhase = LifecyclePhase.SITE, + requiresDependencyResolution = ResolutionScope.RUNTIME, + requiresOnline = false, + requiresProject = true, + threadSafe = true +) +public class ReferenceReport extends AbstractConnectorReport { + + /** + * Connector reference output name + */ + public static final String CONNECTOR_REFERENCE_OUTPUT_NAME = "metricshub-connector-reference"; + + @Override + protected void doReport() throws MavenReportException { + // Subdirectory where we're going to store the pages for each connector + final File connectorSubdirectory = new File(outputDirectory, CONNECTOR_SUBDIRECTORY_NAME); + if (!connectorSubdirectory.exists() && !connectorSubdirectory.mkdirs()) { + final String message = "Could not create connectorSubdirectory: " + connectorSubdirectory.getAbsolutePath(); + logger.error(message); + throw new MavenReportException(message); + } + + // Main page + produceMainPage(connectors); + + // Connector pages + produceConnectorPages(connectors, connectorSubdirectory, buildSupersededMap(connectors)); + } + + /** + * Builds a map representing the superseded relationships between connectors. + * + * @param connectors A map of connector IDs to JsonNode objects representing connectors. + * @return A map where each key is a connector ID that is superseded by one or more connectors, + * and the associated value is a list of connectors that supersede it. + */ + private Map> buildSupersededMap(final Map connectors) { + final Map> supersededMap = new HashMap<>(); + + connectors.forEach((connectorId, connector) -> + new ConnectorJsonNodeReader(connector) + .getSupersedes() + .forEach(supersededConnectorId -> + supersededMap.computeIfAbsent(supersededConnectorId, k -> new ArrayList<>()).add(connectorId) + ) + ); + + return supersededMap; + } + + /** + * Produces individual connector pages for the Maven report + * + * @param connectors A map of connector IDs to their corresponding JSON nodes. + * @param connectorSubdirectory The subdirectory where individual connector pages are located. + * @param supersededMap A map representing the superseded relationships among connectors. + * @throws MavenReportException If an error occurs while producing the connector pages. + */ + private void produceConnectorPages( + final Map connectors, + final File connectorSubdirectory, + final Map> supersededMap + ) throws MavenReportException { + for (Entry connectorEntry : connectors.entrySet()) { + final String connectorId = connectorEntry.getKey(); + // Create a new sink! + final Sink sink; + try { + sink = getSinkFactory().createSink(connectorSubdirectory, SinkHelper.buildPageFilename(connectorId)); + } catch (IOException e) { + final String message = String.format("Could not create sink for %s in %s", connectorId, connectorSubdirectory); + logger.error(message, e); + throw new MavenReportException(message, e); + } + + ConnectorPageReferenceProducer + .builder() + .withConnectorId(connectorId) + .withConnector(connectorEntry.getValue()) + .withLogger(logger) + .build() + .produce(sink, supersededMap); + } + } + + /** + * Produces the main page for the Maven report + * + * @param connectors A map of connector IDs to their corresponding JSON nodes. + */ + private void produceMainPage(final Map connectors) throws MavenReportException { + final Sink mainSink = getMainSink(); + + new MainPageReferenceProducer(CONNECTOR_SUBDIRECTORY_NAME, logger).produce(mainSink, connectors); + } + + @Override + public String getDescription(final Locale locale) { + return "Detailed description of all discovered monitors and reported metrics in each Connector in ${project.name} ${project.version}"; + } + + @Override + public String getName(final Locale locale) { + return "Connector Reference"; + } + + @Override + public String getOutputName() { + return CONNECTOR_REFERENCE_OUTPUT_NAME; + } +} diff --git a/src/main/java/org/sentrysoftware/maven/metricshub/connector/package-info.java b/src/main/java/org/sentrysoftware/maven/metricshub/connector/package-info.java new file mode 100644 index 0000000..83fadae --- /dev/null +++ b/src/main/java/org/sentrysoftware/maven/metricshub/connector/package-info.java @@ -0,0 +1,25 @@ +/** + * Base package of the MetricsHub Connector Maven Plugin.
    + * + * See {@link org.sentrysoftware.maven.metricshub.connector.ReferenceReport} + */ +package org.sentrysoftware.maven.metricshub.connector; +/*- + * ╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲ + * MetricsHub Connector Maven Plugin + * ჻჻჻჻჻჻ + * Copyright (C) 2023 Sentry Software + * ჻჻჻჻჻჻ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱ + */ diff --git a/src/main/java/org/sentrysoftware/maven/metricshub/connector/parser/AbstractNodeProcessor.java b/src/main/java/org/sentrysoftware/maven/metricshub/connector/parser/AbstractNodeProcessor.java new file mode 100644 index 0000000..aa88dce --- /dev/null +++ b/src/main/java/org/sentrysoftware/maven/metricshub/connector/parser/AbstractNodeProcessor.java @@ -0,0 +1,64 @@ +package org.sentrysoftware.maven.metricshub.connector.parser; + +/*- + * ╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲ + * MetricsHub Connector Maven Plugin + * ჻჻჻჻჻჻ + * Copyright (C) 2023 Sentry Software + * ჻჻჻჻჻჻ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱ + */ + +import com.fasterxml.jackson.databind.JsonNode; +import java.io.IOException; +import lombok.AllArgsConstructor; + +/** + * Abstract base class for implementing a chain of responsibility pattern in processing JsonNodes. + * Each concrete subclass represents a specific processing step in the chain. + */ +@AllArgsConstructor +public abstract class AbstractNodeProcessor { + + /** + * Next node processor + */ + protected AbstractNodeProcessor next; + + /** + * Process the provided JsonNode and return the resulting {@link JsonNode}. + * + * @param node The JsonNode to be processed. + * @return An instance of {@link JsonNode} representing the result of the processing. + * @throws IOException If an I/O error occurs during the processing. + */ + public JsonNode process(final JsonNode node) throws IOException { + final JsonNode processedNode = doProcessing(node); + + if (next != null) { + return next.process(processedNode); + } + + return processedNode; + } + + /** + * Perform processing on the provided JsonNode and return the resulting {@link JsonNode}. + * + * @param node The JsonNode to be processed. + * @return An instance of {@link JsonNode} representing the result of the processing. + * @throws IOException If an I/O error occurs during the processing. + */ + protected abstract JsonNode doProcessing(JsonNode node) throws IOException; +} diff --git a/src/main/java/org/sentrysoftware/maven/metricshub/connector/parser/ConnectorLibraryParser.java b/src/main/java/org/sentrysoftware/maven/metricshub/connector/parser/ConnectorLibraryParser.java new file mode 100644 index 0000000..7f74ea3 --- /dev/null +++ b/src/main/java/org/sentrysoftware/maven/metricshub/connector/parser/ConnectorLibraryParser.java @@ -0,0 +1,116 @@ +package org.sentrysoftware.maven.metricshub.connector.parser; + +/*- + * ╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲ + * MetricsHub Connector Maven Plugin + * ჻჻჻჻჻჻ + * Copyright (C) 2023 Sentry Software + * ჻჻჻჻჻჻ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱ + */ + +import static org.sentrysoftware.maven.metricshub.connector.Constants.YAML_OBJECT_MAPPER; + +import com.fasterxml.jackson.databind.JsonNode; +import java.io.IOException; +import java.nio.file.FileVisitResult; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.SimpleFileVisitor; +import java.nio.file.attribute.BasicFileAttributes; +import java.util.HashMap; +import java.util.Map; +import lombok.Getter; +import lombok.NonNull; + +/** + * This class parses the connectors that are located under the source directory then produces + * a map of {@link JsonNode} instances that need to be used by the underlying reference guide producers. + */ +public class ConnectorLibraryParser { + + /** + * This inner class allows to visit the files contained within the connectors directory + */ + private static class ConnectorFileVisitor extends SimpleFileVisitor { + + @Getter + private final Map connectorsMap = new HashMap<>(); + + @Override + public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException { + // Skip this path if it is a directory or not a YAML file + if (Files.isDirectory(file) || !isYamlFile(file.toFile().getName())) { + return FileVisitResult.CONTINUE; + } + + final JsonNode connectorNode = YAML_OBJECT_MAPPER.readTree(file.toFile()); + if (!isConnector(connectorNode)) { + return FileVisitResult.CONTINUE; + } + + final JsonNode connector = ConnectorParser.withNodeProcessor(file.getParent()).parse(file.toFile()); + + final Path fileNamePath = file.getFileName(); + + if (fileNamePath != null) { + final String filename = fileNamePath.toString(); + connectorsMap.put(filename.substring(0, filename.lastIndexOf('.')), connector); + } + + return FileVisitResult.CONTINUE; + } + + /** + * Whether the JsonNode is a final Connector. It means that this JsonNode defines the displayName section. + * + * @param connector JsonNode that contains connector's data + * @return true if the {@link JsonNode} is a final connector, otherwise false. + */ + private boolean isConnector(final JsonNode connector) { + final JsonNode connectorNode = connector.get("connector"); + if (connectorNode != null && !connectorNode.isNull()) { + final JsonNode displayName = connectorNode.get("displayName"); + return displayName != null && !displayName.isNull(); + } + + return false; + } + + /** + * Whether the connector is a YAML file or not + * + * @param fileName The name of the file + * @return boolean value + */ + private boolean isYamlFile(final String fileName) { + return fileName.toLowerCase().endsWith(".yaml"); + } + } + + /** + * Parse connectors located under the source directory + * + * @param sourceDirectory Source directory of the connectors. + * @return Map of {@link JsonNode} instances indexed by the connector ID (connectors map: key=connector-id, value=JsonNode) + * @throws IOException if the file does not exist + */ + public Map parse(@NonNull final Path sourceDirectory) throws IOException { + final ConnectorFileVisitor fileVisitor = new ConnectorFileVisitor(); + + Files.walkFileTree(sourceDirectory, fileVisitor); + + return fileVisitor.getConnectorsMap(); + } +} diff --git a/src/main/java/org/sentrysoftware/maven/metricshub/connector/parser/ConnectorParser.java b/src/main/java/org/sentrysoftware/maven/metricshub/connector/parser/ConnectorParser.java new file mode 100644 index 0000000..4921e0c --- /dev/null +++ b/src/main/java/org/sentrysoftware/maven/metricshub/connector/parser/ConnectorParser.java @@ -0,0 +1,66 @@ +package org.sentrysoftware.maven.metricshub.connector.parser; + +/*- + * ╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲ + * MetricsHub Connector Maven Plugin + * ჻჻჻჻჻჻ + * Copyright (C) 2023 Sentry Software + * ჻჻჻჻჻჻ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱ + */ + +import static org.sentrysoftware.maven.metricshub.connector.Constants.YAML_OBJECT_MAPPER; + +import com.fasterxml.jackson.databind.JsonNode; +import java.io.File; +import java.io.IOException; +import java.nio.file.Path; +import lombok.AllArgsConstructor; + +/** + * This class parses the connector YAML file and produces the corresponding {@link JsonNode} + */ +@AllArgsConstructor +public class ConnectorParser { + + private AbstractNodeProcessor nodeProcessor; + + /** + * Parses the specified connector file. + * + * @param connectorFile The file to be parsed. + * + * @return A new {@link JsonNode} object. + * @throws IOException If an IO error occurs during deserialization or processing. + */ + public JsonNode parse(final File connectorFile) throws IOException { + final JsonNode node = YAML_OBJECT_MAPPER.readTree(connectorFile); + + if (nodeProcessor != null) { + return nodeProcessor.process(node); + } + + return node; + } + + /** + * Creates a new {@link ConnectorParser} with extends and constants. + * + * @param connectorDirectory The directory where all the connectors are located. + * @return new instance of {@link ConnectorParser} + */ + public static ConnectorParser withNodeProcessor(final Path connectorDirectory) { + return new ConnectorParser(NodeProcessorHelper.withExtendsAndConstantsProcessor(connectorDirectory)); + } +} diff --git a/src/main/java/org/sentrysoftware/maven/metricshub/connector/parser/ConstantsProcessor.java b/src/main/java/org/sentrysoftware/maven/metricshub/connector/parser/ConstantsProcessor.java new file mode 100644 index 0000000..bc9a973 --- /dev/null +++ b/src/main/java/org/sentrysoftware/maven/metricshub/connector/parser/ConstantsProcessor.java @@ -0,0 +1,94 @@ +package org.sentrysoftware.maven.metricshub.connector.parser; + +/*- + * ╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲ + * MetricsHub Connector Maven Plugin + * ჻჻჻჻჻჻ + * Copyright (C) 2023 Sentry Software + * ჻჻჻჻჻჻ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱ + */ + +import com.fasterxml.jackson.databind.JsonNode; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Objects; +import java.util.function.Predicate; +import java.util.function.UnaryOperator; + +/** + * The constant processor performs constant replacements over a {@link JsonNode} instance + */ +public class ConstantsProcessor extends AbstractNodeProcessor { + + /** + * Constructs a ConstantsProcessor without a next processor. + */ + public ConstantsProcessor() { + super(null); + } + + @Override + public JsonNode doProcessing(final JsonNode node) { + final JsonNode constantsNode = node.get("constants"); + + if (constantsNode != null && constantsNode.isObject()) { + final List constantKeys = new ArrayList<>(constantsNode.size()); + constantsNode.fieldNames().forEachRemaining(constantKeys::add); + + final Map replacements = new HashMap<>(); + for (String key : constantKeys) { + final JsonNode child = constantsNode.get(key); + replacements.put(key, child.asText()); + } + + final UnaryOperator updater = value -> performReplacements(replacements, value); + + final Predicate predicate = Objects::nonNull; + + JsonNodeUpdater.builder().withJsonNode(node).withUpdater(updater).withPredicate(predicate).build().update(); + } + + return node; + } + + /** + * Replace placeholders in the given value with corresponding values from the provided + * key-value pairs in the replacements {@link Map}. + * + * @param replacements Key-value pairs representing placeholders and their replacement values. + *
    Example: { $constants.query1=MyQuery1, $constants.query2=MyQuery2 } + * @param value The string to be replaced. + * @return A new {@link String} with the placeholders replaced. + */ + private String performReplacements(final Map replacements, String value) { + if (value == null || value.isEmpty()) { + return value; + } + + // Loop over each placeholder and perform replacement + for (final Entry entry : replacements.entrySet()) { + final String key = entry.getKey(); + if (value.contains(key)) { + value = value.replace(key, entry.getValue()); + } + } + + // return the new value + return value; + } +} diff --git a/src/main/java/org/sentrysoftware/maven/metricshub/connector/parser/ExtendsProcessor.java b/src/main/java/org/sentrysoftware/maven/metricshub/connector/parser/ExtendsProcessor.java new file mode 100644 index 0000000..0cb2174 --- /dev/null +++ b/src/main/java/org/sentrysoftware/maven/metricshub/connector/parser/ExtendsProcessor.java @@ -0,0 +1,171 @@ +package org.sentrysoftware.maven.metricshub.connector.parser; + +/*- + * ╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲ + * MetricsHub Connector Maven Plugin + * ჻჻჻჻჻჻ + * Copyright (C) 2023 Sentry Software + * ჻჻჻჻჻჻ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱ + */ + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ArrayNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import java.io.IOException; +import java.nio.file.Path; +import java.util.Iterator; +import org.sentrysoftware.maven.metricshub.connector.Constants; + +/** + * The {@code ExtendsProcessor} class performs the merging of extended connectors. + *

    + * This processor is designed to handle the merging of extended connectors specified under the "extends" section + * of a given JSON node. The merging process involves recursively combining extended connectors and the provided + * JSON node. The merging logic is implemented in the {@link #doMerge(JsonNode)} method. + *

    + * @see AbstractNodeProcessor + */ +public class ExtendsProcessor extends AbstractNodeProcessor { + + private final Path connectorDirectory; + + /** + * Constructs a new instance of ExtendsProcessor with the specified connector + * directory, YAML ObjectMapper, and optional next processor. + * + * @param connectorDirectory The directory path for connectors. + * @param next The next processor in the processing chain. + */ + public ExtendsProcessor(Path connectorDirectory, AbstractNodeProcessor next) { + super(next); + this.connectorDirectory = connectorDirectory; + } + + @Override + public JsonNode doProcessing(JsonNode node) throws IOException { + return doMerge(node); + } + + /** + * Merges extended connectors recursively.
    + * Merge logic:
    + *
      + *
    1. Merged extended connectors located under the extends section of the given node.
    2. + *
    3. Once all the extended connectors are merged, merge the given JsonNode (node) with the extended connectors that have been merged.
    4. + *
    + *
    + * A recursive merge is applied for each extended connector because it can extend another connector too. That's why doMerge + * is called for each extended connector. + * @param node {@link JsonNode} to process + * @return {@link JsonNode} instance + * @throws IOException + */ + private JsonNode doMerge(JsonNode node) throws IOException { + JsonNode extNode = node.get("extends"); + + JsonNode result = node; + if (extNode != null && extNode.isArray()) { + final ArrayNode extNodeArray = (ArrayNode) extNode; + final Iterator iter = extNodeArray.iterator(); + + JsonNode extended = null; + if (iter.hasNext()) { + extended = doMerge(getJsonNode(iter)); + while (iter.hasNext()) { + final JsonNode extendedNext = doMerge(getJsonNode(iter)); + merge(extended, extendedNext); + } + } + + extNodeArray.removeAll(); + + if (extended != null) { + result = merge(extended, node); + } + } + return result; + } + + /** + * Gets the next {@link JsonNode} from the iterator + * + * @param iterator {@link Iterator} over a collection of {@link JsonNode} + * @return {@link JsonNode} object + * @throws IOException + */ + private JsonNode getJsonNode(Iterator iterator) throws IOException { + return Constants.YAML_OBJECT_MAPPER.readTree( + connectorDirectory.resolve(iterator.next().asText() + ".yaml").toFile() + ); + } + + /** + * Merge the given mainNode and updateNode. + * Merge strategy:
    + *
      + *
    1. Arrays of objects are appended from updateNode to mainNode.
    2. + *
    3. Arrays of simple values from updateNode erase the ones in mainNode.
    4. + *
    5. updateNode object values overwrite mainNode object values.
    6. + *
    + * + * @param mainNode The main JsonNode to be merged. + * @param updateNode The JsonNode containing updates to be merged into the mainNode. + * @return {@link JsonNode} merged + */ + public static JsonNode merge(JsonNode mainNode, JsonNode updateNode) { + final Iterator fieldNames = updateNode.fieldNames(); + while (fieldNames.hasNext()) { + final String fieldName = fieldNames.next(); + final JsonNode jsonNode = mainNode.get(fieldName); + if (jsonNode != null && jsonNode.isArray() && updateNode.get(fieldName).isArray()) { + // both JSON nodes are arrays + mergeJsonArray(updateNode, fieldName, jsonNode); + } else if (jsonNode != null && jsonNode.isObject()) { + // both JSON nodes are objects, merge them + merge(jsonNode, updateNode.get(fieldName)); + } else { + if (mainNode instanceof ObjectNode) { + // overwrite field + final JsonNode value = updateNode.get(fieldName); + ((ObjectNode) mainNode).set(fieldName, value); + } + } + } + return mainNode; + } + + /** + * Handles the specific merge logic for arrays, considering the merge strategy. + * + * @param updateNode The JsonNode containing the array to be merged. + * @param fieldName The name of the field corresponding to the array in the updateNode. + * @param mainArrayNode The mainArrayNode in the main JsonNode to be merged. + */ + private static void mergeJsonArray(final JsonNode updateNode, final String fieldName, final JsonNode mainArrayNode) { + ArrayNode mainArray = (ArrayNode) mainArrayNode; + ArrayNode extendedArray = (ArrayNode) updateNode.get(fieldName); + + if (mainArray.size() != 0 && mainArray.get(0).isObject()) { + // Array of objects gets merged (appended) + for (int i = 0; i < extendedArray.size(); i++) { + mainArray.add(extendedArray.get(i)); + } + } else { + // Simple array gets overwritten + mainArray.removeAll(); + mainArray.addAll(extendedArray); + } + } +} diff --git a/src/main/java/org/sentrysoftware/maven/metricshub/connector/parser/JsonNodeUpdater.java b/src/main/java/org/sentrysoftware/maven/metricshub/connector/parser/JsonNodeUpdater.java new file mode 100644 index 0000000..1c3763b --- /dev/null +++ b/src/main/java/org/sentrysoftware/maven/metricshub/connector/parser/JsonNodeUpdater.java @@ -0,0 +1,113 @@ +package org.sentrysoftware.maven.metricshub.connector.parser; + +/*- + * ╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲ + * MetricsHub Connector Maven Plugin + * ჻჻჻჻჻჻ + * Copyright (C) 2023 Sentry Software + * ჻჻჻჻჻჻ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱ + */ + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ArrayNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import com.fasterxml.jackson.databind.node.TextNode; +import java.util.ArrayList; +import java.util.List; +import java.util.function.Predicate; +import java.util.function.UnaryOperator; +import lombok.Builder; + +/** + * This utility class traverses a JsonNode, applying updates according to an + * updater function and a predicate that determines whether the value should be + * updated. + */ +@Builder(setterPrefix = "with") +public class JsonNodeUpdater { + + private final JsonNode jsonNode; + private final UnaryOperator updater; + private final Predicate predicate; + + /** + * Traverse the current JsonNode, applying the updater to each JsonNode child + * when the predicate evaluates to true, indicating that the value should be updated. + */ + public void update() { + update(jsonNode); + } + + /** + * Traverse the current JsonNode, applying the updater to each JsonNode child + * when the predicate evaluates to true, indicating that the value should be updated. + * + * @param node the {@link JsonNode} to update + */ + private void update(final JsonNode node) { + if (node == null) { + return; + } + + if (node.isObject()) { + // Get JsonNode fields + final List fieldNames = new ArrayList<>(node.size()); + node.fieldNames().forEachRemaining(fieldNames::add); + + // Get the corresponding JsonNode for each field + for (final String fieldName : fieldNames) { + final JsonNode child = node.get(fieldName); + + // Means it wrap sub JsonNode(s) + if (child.isContainerNode()) { + update(child); + } else { + // Perform the replacement + final String oldValue = child.asText(); + // Transformation of the value is unnecessary if it lacks the placeholder + runUpdate(() -> ((ObjectNode) node).set(fieldName, new TextNode(updater.apply(oldValue))), oldValue); + } + } + } else if (node.isArray()) { + // Loop over the array and get each JsonNode element + for (int i = 0; i < node.size(); i++) { + final JsonNode child = node.get(i); + + // Means this node is a JsonNode element + if (child.isContainerNode()) { + update(child); + } else { + // Means this is a simple array node + final String oldValue = child.asText(); + // Transformation of the value is unnecessary if it lacks the placeholder + final int index = i; + runUpdate(() -> ((ArrayNode) node).set(index, new TextNode(updater.apply(oldValue))), oldValue); + } + } + } + } + + /** + * Run the update only if the value matches the replacement predicate + * + * @param update Runnable function, actually the function performing the update + * @param value Value to check + */ + private void runUpdate(final Runnable update, final String value) { + if (predicate.test(value)) { + update.run(); + } + } +} diff --git a/src/main/java/org/sentrysoftware/maven/metricshub/connector/parser/NodeProcessorHelper.java b/src/main/java/org/sentrysoftware/maven/metricshub/connector/parser/NodeProcessorHelper.java new file mode 100644 index 0000000..84bb313 --- /dev/null +++ b/src/main/java/org/sentrysoftware/maven/metricshub/connector/parser/NodeProcessorHelper.java @@ -0,0 +1,55 @@ +package org.sentrysoftware.maven.metricshub.connector.parser; + +/*- + * ╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲ + * MetricsHub Connector Maven Plugin + * ჻჻჻჻჻჻ + * Copyright (C) 2023 Sentry Software + * ჻჻჻჻჻჻ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱ + */ + +import java.nio.file.Path; +import lombok.AccessLevel; +import lombok.NoArgsConstructor; + +/** + * The {@code NodeProcessorHelper} class provides utility methods for creating instances of node processors. + *

    + * This class encapsulates static factory methods for creating various node processors, such as the + * {@link ConstantsProcessor} and the {@link ExtendsProcessor} with a {@link ConstantsProcessor} destination. + *

    + */ +@NoArgsConstructor(access = AccessLevel.PRIVATE) +public class NodeProcessorHelper { + + /** + * Creates a new {@link ConstantsProcessor} + * + * @return new {@link ConstantsProcessor} + */ + private static AbstractNodeProcessor newConstantsProcessor() { + return new ConstantsProcessor(); + } + + /** + * Create a {@link ExtendsProcessor} with {@link ConstantsProcessor} destination + * + * @param connectorDirectory Used to locate a the connector parent directory in a file system + * @return new {@link ExtendsProcessor} instance + */ + public static AbstractNodeProcessor withExtendsAndConstantsProcessor(final Path connectorDirectory) { + return new ExtendsProcessor(connectorDirectory, newConstantsProcessor()); + } +} diff --git a/src/main/java/org/sentrysoftware/maven/metricshub/connector/parser/package-info.java b/src/main/java/org/sentrysoftware/maven/metricshub/connector/parser/package-info.java new file mode 100644 index 0000000..bbf0591 --- /dev/null +++ b/src/main/java/org/sentrysoftware/maven/metricshub/connector/parser/package-info.java @@ -0,0 +1,25 @@ +/** + * The part of library responsible for parsing connectors.
    + * + * See {@link org.sentrysoftware.maven.metricshub.connector.parser.ConnectorLibraryParser}. + */ +package org.sentrysoftware.maven.metricshub.connector.parser; +/*- + * ╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲ + * MetricsHub Connector Maven Plugin + * ჻჻჻჻჻჻ + * Copyright (C) 2023 Sentry Software + * ჻჻჻჻჻჻ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱ + */ diff --git a/src/main/java/org/sentrysoftware/maven/metricshub/connector/producer/ConnectorJsonNodeReader.java b/src/main/java/org/sentrysoftware/maven/metricshub/connector/producer/ConnectorJsonNodeReader.java new file mode 100644 index 0000000..610f141 --- /dev/null +++ b/src/main/java/org/sentrysoftware/maven/metricshub/connector/producer/ConnectorJsonNodeReader.java @@ -0,0 +1,595 @@ +package org.sentrysoftware.maven.metricshub.connector.producer; + +/*- + * ╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲ + * MetricsHub Connector Maven Plugin + * ჻჻჻჻჻჻ + * Copyright (C) 2023 Sentry Software + * ჻჻჻჻჻჻ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱ + */ + +import static org.sentrysoftware.maven.metricshub.connector.producer.JsonNodeHelper.nodeToStringList; +import static org.sentrysoftware.maven.metricshub.connector.producer.JsonNodeHelper.nonNull; +import static org.sentrysoftware.maven.metricshub.connector.producer.JsonNodeHelper.nonNullTextOrDefault; +import static org.sentrysoftware.maven.metricshub.connector.producer.JsonNodeHelper.stream; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ArrayNode; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Optional; +import java.util.Set; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import java.util.stream.Collectors; +import java.util.stream.Stream; +import lombok.AllArgsConstructor; +import org.sentrysoftware.maven.metricshub.connector.Constants; +import org.sentrysoftware.maven.metricshub.connector.producer.model.common.TechnologyType; + +/** + * Implementation for reading information from a JSON structure representing + * a connector. + * + *

    + * This class provides methods to extract various fields and information from a + * {@link JsonNode} representing a connector. It serves as an utility + * for reading data associated with a connector. Various methods are available + * to retrieve specific attributes such as supersedes, detection, criteria or + * other relevant details. + *

    + *

    + * Instances of this class are typically created with a {@link JsonNode} + * containing connector information, and the provided methods can then be used + * to access and extract specific details from the JSON structure. + *

    + * + */ +@AllArgsConstructor +public class ConnectorJsonNodeReader { + + /** + * Defines a regular expression pattern for matching metric names enclosed in curly braces + */ + private static final Pattern METRIC_NAME_PATTERN = Pattern.compile("^\\s*([^\\{]*)\\{.*\\}\\s*$"); + + /** + * Defines a regular expression pattern for matching a state metric + */ + private static final Pattern STATE_METRIC_PATTERN = Pattern.compile( + "^\\s*([^\\{]*)\\{.*(\\s*state\\s*=\\s*.*)\\}\\s*$" + ); + + private final JsonNode connector; + + /** + * Retrieves the display name property of the connector located under the connector JSON node. + * + * @return The display name as a String. + */ + public String getDisplayName() { + return getConnectorSection() + .map(node -> node.get("displayName")) + .filter(JsonNodeHelper::nonNull) + .map(JsonNode::asText) + .orElse(Constants.EMPTY); + } + + /** + * Retrieves the information property of the connector, if available. + * + * @param defaultValue The default value to return if the information property is null or represents a JSON null. + * @return The information property as a String, or the specified default value if not present. + */ + public String getInformationOrDefault(final String defaultValue) { + final JsonNode information = getConnectorSection().map(node -> node.get("information")).orElse(null); + + return nonNullTextOrDefault(information, defaultValue); + } + + /** + * Retrieves the platforms property of the connector, if available. + * + * @param defaultValue The default value to return if the platforms property is null or represents a JSON null. + * @return The platforms property as a String, or the specified default value if not present. + */ + public String getPlatformsOrDefault(final String defaultValue) { + final JsonNode platforms = getConnectorSection().map(node -> node.get("platforms")).orElse(null); + + return nonNullTextOrDefault(platforms, defaultValue); + } + + /** + * Retrieves the list of supersedes values from the connector's detection property, if available. + * + * @return A list of strings representing the supersedes values, or an empty list if not present. + */ + public List getSupersedes() { + final JsonNode detection = getDetection(); + if (nonNull(detection)) { + final JsonNode supersedes = detection.get("supersedes"); + return nodeToStringList(supersedes); + } + return Collections.emptyList(); + } + + /** + * Retrieves the list of appliesTo (OS) values from the connector's detection property, if available. + * + * @return A list of strings representing the OS values, or an empty list if not present. + */ + public List getAppliesTo() { + final JsonNode detection = getDetection(); + if (nonNull(detection)) { + final JsonNode appliesTo = detection.get("appliesTo"); + return nodeToStringList(appliesTo); + } + return Collections.emptyList(); + } + + /** + * Retrieves the required MetricsHub version from the connector's detection criteria. + *

    + * This method retrieves the required MetricsHub version by extracting the detection criteria + * obtained from the connector. It looks for criteria of type "productRequirements" and extracts + * the associated engine version. If no matching criteria are found or the necessary information is + * not available, the method returns null. + *

    + * @return The required MetricsHub version, or null if the information is not available. + */ + public String getRequiredMetricsHubVersion() { + final JsonNode criteria = getDetectionCriteria(); + + // If criteria information is not available or is not an array, return null + if (!nonNull(criteria) || !criteria.isArray()) { + return null; + } + + // Filter criteria of type "productRequirements" and extract the engine version + return stream((ArrayNode) criteria) + .filter(node -> { + final JsonNode typeNode = getType(node); + return nonNull(typeNode) && "productrequirements".equalsIgnoreCase(typeNode.asText()); + }) + .map(node -> node.get("engineVersion")) + .filter(JsonNodeHelper::nonNull) + .findFirst() + .map(JsonNode::asText) + .orElse(null); + } + + /** + * Retrieves the type field value from the specified node. + * + * @param node {@link JsonNode} instance + * @return The "type" field value from the given node as a {@link JsonNode}, or {@code null} if not available. + */ + private JsonNode getType(final JsonNode node) { + return node.get("type"); + } + + /** + * Retrieves the detection criteria from the connector's detection section. + * + * @return The "criteria" field from the "detection" section as a {@link JsonNode}, or {@code null} if not available. + */ + private JsonNode getDetectionCriteria() { + final JsonNode detection = getDetection(); + + // If detection information is not available, return null + if (!nonNull(detection)) { + return null; + } + + return detection.get("criteria"); + } + + /** + * Retrieves the detection from the connector + * + * @return The "detection" field from the connector node as a {@link JsonNode}, or {@code null} if not available. + */ + private JsonNode getDetection() { + return getConnectorSection().map(node -> node.get("detection")).orElse(null); + } + + /** + * Retrieves the reliesOn property of the connector, if available. + * + * @param defaultValue The default value to return if the reliesOn property is null or represents a JSON null. + * @return The reliesOn property as a String, or the specified default value if not present. + */ + public String getReliesOnOrDefault(final String defaultValue) { + final JsonNode reliesOn = getConnectorSection().map(node -> node.get("reliesOn")).orElse(null); + + return nonNullTextOrDefault(reliesOn, defaultValue); + } + + /** + * Retrieves the "connector" section from the connector + * + * @return The "connector" field from the connector node as an Optional of {@link JsonNode}. + */ + private Optional getConnectorSection() { + return Optional.ofNullable(connector.get("connector")); + } + + /** + * Retrieves the set of {@link TechnologyType}s used by the monitor jobs in the connector. + *

    + * This method iterates through the monitors defined in the connector and collects the {@link TechnologyType}s + * associated with their discovery, collect, and simple jobs. The result is a set of unique technology types. + *

    + * @return A {@link Set} of {@link TechnologyType}s used by monitors in the connector. + */ + public Set getTechnologies() { + final Set technologies = new HashSet<>(); + getMonitors() + .ifPresent(monitors -> monitors.forEach(monitor -> collectTechnologies(technologies, getMonitorJobs(monitor)))); + + return technologies; + } + + /** + * Gets the monitors from the connector. + * + * @return The monitors as an Optional of {@link JsonNode}. + */ + public Optional getMonitors() { + return Optional.ofNullable(connector.get("monitors")); + } + + /** + * Collects technology types from the specified jobs and adds them to the provided set. + *

    + * This method processes the sources defined in each job and extracts the technology type from the "type" field. + * The detected technology types are added to the provided set of technology. + *

    + * @param technology The set of {@link TechnologyType}s to which the collected technology types are added. + * @param jobs The JSON nodes representing different jobs (e.g., discovery, collect, simple). + */ + private void collectTechnologies(final Set technologies, final JsonNode... jobs) { + Stream + .of(jobs) + .filter(JsonNodeHelper::nonNull) + .forEach(job -> { + final JsonNode sources = job.get("sources"); + if (!nonNull(sources)) { + return; + } + sources.forEach(source -> { + final JsonNode typeNode = getType(source); + if (nonNull(typeNode)) { + TechnologyType.getTechnologyType(typeNode.asText()).ifPresent(technologies::add); + } + }); + }); + } + + /** + * Retrieves a list of sudo commands configured in the connector. + *

    + * This method retrieves the sudo commands defined in the "sudoCommands" section of the connector. + * The commands are returned as a {@link List} of strings. If the "sudoCommands" section is not present or + * is not of the expected type, an empty list is returned. + *

    + * @return A {@link List} of strings representing the sudo commands set in the connector. + */ + public List getSudoCommands() { + final JsonNode sudoCommands = connector.get("sudoCommands"); + return nodeToStringList(sudoCommands); + } + + /** + * Retrieves a set of connection types specified in the connector's detection. + *

    + * This method looks for the "connectionTypes" field within the "detection" section of the connector configuration. + * If the "detection" section is present and contains the "connectionTypes" field, the method returns a {@link Set} + * of case-insensitive strings representing the connection types. If the "detection" section or the "connectionTypes" field is not + * present or is not of the expected type, an empty set is returned. + * + * @return A case-insensitive {@link Set} of strings representing the connection types (local and/or remote). + */ + public Set getConnectionTypes() { + final JsonNode detection = getDetection(); + if (nonNull(detection)) { + final JsonNode connectionTypes = detection.get("connectionTypes"); + return nodeToCaseInsensitiveSet(connectionTypes); + } + return Collections.emptySet(); + } + + /** + * Converts a {@link JsonNode} to a case-insensitive set of strings. + * + * @param node The {@link JsonNode} to convert to a case-insensitive set. + * @return A case-insensitive {@link Set} of strings representing the contents of the input node, or an empty set if the node is null + * or not of the expected types. + */ + private Set nodeToCaseInsensitiveSet(final JsonNode node) { + return nodeToStringList(node).stream().map(String::toLowerCase).collect(Collectors.toSet()); + } + + /** + * Checks if auto-detection is disabled for the connector. + *

    + * This method looks for the presence of the "disableAutoDetection" field within the "detection" section of the connector. + * If the field is present and is a boolean value, the method returns its boolean value. If the "detection" section + * or the "disableAutoDetection" field is not present or is not a boolean, the method assumes auto-detection is enabled + * and returns false. + *

    + * @return {@code true} if auto-detection is explicitly disabled, {@code false} if explicitly enabled, or {@code false} if the + * configuration is not present. + */ + public boolean isAutoDetectionDisabled() { + final JsonNode detection = getDetection(); + if (nonNull(detection)) { + final JsonNode disableAutoDetection = detection.get("disableAutoDetection"); + if (nonNull(disableAutoDetection) && disableAutoDetection.isBoolean()) { + return disableAutoDetection.asBoolean(); + } + } + return false; + } + + /** + * Retrieves the "onLastResort" configuration value from the connector's detection section. + * + * @return The "onLastResort" configuration value as a string, or {@code null} if not configured. + */ + public String getOnLastResort() { + final JsonNode detection = getDetection(); + if (nonNull(detection)) { + final JsonNode onLastResort = detection.get("onLastResort"); + if (nonNull(onLastResort)) { + return onLastResort.asText(); + } + } + return null; + } + + /** + * Retrieves the detection criteria as a list of {@link JsonNode} objects from the connector's detection section. + * + * @return A list of {@link JsonNode} objects representing the detection criteria, or an empty list if not configured. + */ + public List getCriteria() { + final JsonNode detectionCriteria = getDetectionCriteria(); + + if (nonNull(detectionCriteria) && detectionCriteria.isArray()) { + return stream((ArrayNode) detectionCriteria).collect(Collectors.toList()); + } + + return Collections.emptyList(); + } + + /** + * Retrieves the metric keys from the specified monitor node. + * + * @param monitor The monitor node as a {@link JsonNode}. + * @return A {@link Set} containing the metric keys. + */ + public Set getMonitorMetrics(final JsonNode monitor) { + final Set metricKeys = new HashSet<>(); + + collectMetrics(metricKeys, getMonitorJobs(monitor)); + + return metricKeys; + } + + /** + * Retrieves the attribute keys from the specified monitor node. + * + * @param monitor The monitor node as a {@link JsonNode}. + * @return A {@link Set} containing the attribute keys. + */ + public Set getMonitorAttributes(final JsonNode monitor) { + final Set attributeKeys = new HashSet<>(); + + collectAttributes(attributeKeys, getMonitorJobs(monitor)); + + return attributeKeys; + } + + /** + * Retrieves the monitor's jobs from the specified monitor node. + * + * @param monitor The monitor node as a {@link JsonNode}. + * @return An array containing the monitor's jobs + */ + public JsonNode[] getMonitorJobs(final JsonNode monitor) { + final JsonNode discovery = monitor.get("discovery"); + final JsonNode collect = monitor.get("collect"); + final JsonNode simple = monitor.get("simple"); + + return new JsonNode[] { discovery, collect, simple }; + } + + /** + * Collects attributes from the specified monitor jobs and adds them to the set of attribute keys. + * + * @param attributeKeys The set to which attribute keys will be added. + * @param jobs The monitor jobs as an array of {@link JsonNode}. + */ + private void collectAttributes(final Set attributeKeys, final JsonNode... jobs) { + Stream + .of(jobs) + .filter(JsonNodeHelper::nonNull) + .forEach(job -> { + final JsonNode mapping = getMapping(job); + if (!nonNull(mapping)) { + return; + } + + final JsonNode attributes = mapping.get("attributes"); + if (!nonNull(attributes)) { + return; + } + + attributes + .fieldNames() + .forEachRemaining(key -> { + if (skipKey(key)) { + return; + } + attributeKeys.add(key); + }); + }); + } + + /** + * Checks if the key should be skipped based on a specific condition. + * + * @param key The key to check. + * @return {@code true} if the key should be skipped, {@code false} otherwise. + */ + private boolean skipKey(final String key) { + return key.startsWith("__"); + } + + /** + * Collects metrics from the specified monitor jobs, manages state metrics, and adds them to the set of metric keys. + * + * @param metricKeys The set to which metric keys will be added. + * @param jobs The monitor jobs as an array of {@link JsonNode}. + */ + private void collectMetrics(final Set metricKeys, final JsonNode... jobs) { + Stream + .of(jobs) + .filter(JsonNodeHelper::nonNull) + .forEach(job -> { + final JsonNode mapping = getMapping(job); + if (!nonNull(mapping)) { + return; + } + + final JsonNode metric = mapping.get("metrics"); + if (!nonNull(metric)) { + return; + } + + metric + .fieldNames() + .forEachRemaining(key -> { + if (skipKey(key)) { + return; + } + + // Manage state metrics + final String metricName = includeStatesInMetricName(key); + + metricKeys.add(metricName); + }); + }); + } + + /** + * Includes states in the metric name if defined in the metric definitions. + * + * @param metricName The original metric name. + * @return The updated metric name with states included. + */ + private String includeStatesInMetricName(final String metricName) { + String name = metricName; + // The metric name already contains the state attribute. No need to update the name + if (STATE_METRIC_PATTERN.matcher(metricName).matches()) { + return name; + } + + final JsonNode metricDefinitions = connector.get("metrics"); + // No metric definitions? + if (!nonNull(metricDefinitions)) { + return name; + } + + final String metricNameWithoutAttributes = extractMetricName(name); + + final JsonNode metricDefinition = metricDefinitions.get(metricNameWithoutAttributes); + + // No metric definition? + if (!nonNull(metricDefinition)) { + return name; + } + + final JsonNode type = getType(metricDefinition); + + // Check the type object. Must be non null and object defining state set + if (!nonNull(metricDefinition) || !type.isObject()) { + return name; + } + + final JsonNode stateSet = type.get("stateSet"); + final List stateSetList = nodeToStringList(stateSet); + if (!stateSet.isEmpty()) { + // Include the state values in the metric name + Collections.sort(stateSetList); + final String states = String.join("|", stateSetList); + if (metricNameWithoutAttributes.equals(name)) { + name = String.format("%s{state=\"%s\"}", name, states); + } else if (name.endsWith("}")) { + name = name.replace("}", String.format(", state=\"%s\"}", states)); + } + } + + return name; + } + + /** + * This method removes attribute parts from the metric name + * + * @param name metric name with or without attributes + * + * @return metric name without attributes + */ + public static final String extractMetricName(final String name) { + // Use a Matcher to find the pattern in the input string + final Matcher matcher = METRIC_NAME_PATTERN.matcher(name); + + // If the pattern is found, replace it with an empty string; otherwise, return the original string + return matcher.find() ? matcher.replaceFirst("$1").trim() : name.trim(); + } + + /** + * Checks if there is a blade monitor job with mapping directives. + * + * @return {@code true} if there is a blade monitor job with mapping directives, otherwise {@code false}. + */ + public boolean hasBladeMonitorJob() { + final JsonNode monitors = getMonitors().orElse(null); + if (nonNull(monitors)) { + final JsonNode bladeMonitorJob = monitors.get("blade"); + if (nonNull(bladeMonitorJob)) { + final JsonNode[] bladeJobs = getMonitorJobs(bladeMonitorJob); + for (JsonNode bladeJob : bladeJobs) { + if (nonNull(bladeJob) && nonNull(getMapping(bladeJob))) { + return true; + } + } + } + } + return false; + } + + /** + * Retrieves the mapping information from the given monitor job. + * + * @param job The monitor job. + * @return The mapping information as a {@link JsonNode}, or {@code null} if not available. + */ + private JsonNode getMapping(final JsonNode job) { + return job.get("mapping"); + } +} diff --git a/src/main/java/org/sentrysoftware/maven/metricshub/connector/producer/ConnectorPageReferenceProducer.java b/src/main/java/org/sentrysoftware/maven/metricshub/connector/producer/ConnectorPageReferenceProducer.java new file mode 100644 index 0000000..2d18e10 --- /dev/null +++ b/src/main/java/org/sentrysoftware/maven/metricshub/connector/producer/ConnectorPageReferenceProducer.java @@ -0,0 +1,651 @@ +package org.sentrysoftware.maven.metricshub.connector.producer; + +/*- + * ╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲ + * MetricsHub Connector Maven Plugin + * ჻჻჻჻჻჻ + * Copyright (C) 2023 Sentry Software + * ჻჻჻჻჻჻ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱ + */ + +import static org.sentrysoftware.maven.metricshub.connector.ReferenceReport.CONNECTOR_REFERENCE_OUTPUT_NAME; + +import com.fasterxml.jackson.databind.JsonNode; +import java.text.ChoiceFormat; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Optional; +import java.util.Set; +import java.util.TreeMap; +import java.util.stream.Collectors; +import lombok.Builder; +import org.apache.maven.doxia.sink.Sink; +import org.apache.maven.plugin.logging.Log; +import org.sentrysoftware.maven.metricshub.connector.producer.model.common.OpenTelemetryHardwareType; +import org.sentrysoftware.maven.metricshub.connector.producer.model.common.OsType; +import org.sentrysoftware.maven.metricshub.connector.producer.model.common.TechnologyType; +import org.sentrysoftware.maven.metricshub.connector.producer.model.criteria.CriterionFactory; +import org.sentrysoftware.maven.metricshub.connector.producer.model.criteria.CriterionSinkProduceVisitor; + +/** + * Utility class for producing page references related to connectors. + * + */ +@Builder(setterPrefix = "with") +public class ConnectorPageReferenceProducer { + + private static final String BOOTSTRAP_MEDIUM_4_CLASS = "col-md-4"; + private static final String WMI_SECTION = " wmi:\n"; + private static final String PROTOCOLS_SECTION = " protocols:\n"; + + private final String connectorId; + private final JsonNode connector; + private final Log logger; + + /** + * Produces a page reference for the current connector and generates the corresponding sink for documentation output. + * + * @param sink The sink used for generating content. + * @param supersededMap Map of superseded connectors. + */ + public void produce(final Sink sink, final Map> supersededMap) { + Objects.requireNonNull(connectorId, () -> "connectorId cannot be null."); + Objects.requireNonNull(connector, () -> "connector cannot be null."); + Objects.requireNonNull(supersededMap, () -> "supersededMap cannot be null."); + Objects.requireNonNull(sink, () -> "sink cannot be null."); + Objects.requireNonNull(logger, () -> "logger cannot be null."); + + logger.debug("Generating " + SinkHelper.buildPageFilename(connectorId)); + + final ConnectorJsonNodeReader connectorJsonNodeReader = new ConnectorJsonNodeReader(connector); + final String displayName = connectorJsonNodeReader.getDisplayName(); + + // Create the head element of the page + sink.head(); + sink.title(); + sink.text(displayName); + sink.title_(); + sink.head_(); + + sink.body(); + + // Back to the main page + sink.paragraph(SinkHelper.setClass("small")); + sink.rawText(SinkHelper.glyphIcon("arrow-left") + " "); + sink.link(String.format("../%s.html", CONNECTOR_REFERENCE_OUTPUT_NAME)); + sink.text("Back to the list of connectors"); + sink.link_(); + sink.paragraph_(); + + // Big title + sink.section1(); + sink.sectionTitle1(); + sink.text(displayName); + sink.sectionTitle1_(); + + // Description + sink.section2(); + sink.sectionTitle2(); + sink.text("Description"); + sink.sectionTitle2_(); + + sink.paragraph(); + sink.text(connectorJsonNodeReader.getInformationOrDefault("N/A")); + sink.paragraph_(); + + produceSupersedesContent(sink, supersededMap, connectorJsonNodeReader); + + // End of the second heading element + sink.section2_(); + + // Target + sink.section2(); + sink.sectionTitle2(); + sink.text("Target"); + sink.sectionTitle2_(); + + final String platforms = connectorJsonNodeReader.getPlatformsOrDefault("N/A"); + sink.paragraph(); + sink.text("Typical "); + sink.text(new ChoiceFormat("-1#platform|0 appliesTo = connectorJsonNodeReader.getAppliesTo(); + final List osList = OsType.mapToDisplayNames(appliesTo); + sink.paragraph(); + sink.text("Operating "); + sink.text(new ChoiceFormat("1#system|1 technologies = connectorJsonNodeReader.getTechnologies(); + sink.paragraph(); + sink.text("Technology and protocols: "); + sink.bold(); + sink.text(technologies.stream().map(TechnologyType::getDisplayName).collect(Collectors.joining(", "))); + sink.bold_(); + sink.paragraph_(); + + // Sudo Commands? + final List sudoCommands = connectorJsonNodeReader.getSudoCommands(); + produceSudoCommandsContent(sink, sudoCommands); + + // Local support = false? (default is true) + final Set connectionTypes = connectorJsonNodeReader.getConnectionTypes(); + if (!connectionTypes.contains("local")) { + sink.paragraph(); + sink.text("This connector is not available for the local host (it is applicable to remote hosts only)."); + sink.paragraph_(); + } + + // Remote support = false? (default is false) + if (!connectionTypes.contains("remote")) { + sink.paragraph(); + sink.bold(); + sink.text("This connector is not available for remote hosts (it is applicable to the local host only)."); + sink.bold_(); + sink.paragraph_(); + } + + // End of the second heading element + sink.section2_(); + + // MetricsHub Example + produceMetricsHubExamplesContent(sink, osList, technologies, sudoCommands); + + // Detection criteria + sink.section2(); + sink.sectionTitle2(); + sink.text("Connector Activation Criteria"); + sink.sectionTitle2_(); + + sink.paragraph(); + sink.text("The "); + sink.bold(); + sink.text(displayName); + sink.bold_(); + + // No automatic detection + if (connectorJsonNodeReader.isAutoDetectionDisabled()) { + sink.text(" connector "); + sink.bold(); + sink.text("must be selected manually"); + sink.bold_(); + sink.text(""); + } else { + sink.text(" connector will be automatically activated"); + } + + sink.text(", and its status will be reported as OK if all the below criteria are met"); + + final String onLastResort = connectorJsonNodeReader.getOnLastResort(); + if (onLastResort != null && !onLastResort.isBlank()) { + sink.text(", and no other connector capable of discovering "); + sink.bold(); + sink.text(onLastResort); + sink.bold_(); + sink.text(" instances is activated"); + } + sink.text(":"); + sink.paragraph_(); + + // Detection Criteria + sink.list(); + connectorJsonNodeReader + .getCriteria() + .forEach(criterionNode -> + CriterionFactory + .withJsonNode(criterionNode) + .ifPresent(criterion -> criterion.accept(new CriterionSinkProduceVisitor(sink))) + ); + sink.list_(); + + // End of the second heading element + sink.section2_(); + + // Metrics + produceMetricsTable(sink, connectorJsonNodeReader); + + // End of the first heading element + sink.section1_(); + + // End of the body element + sink.body_(); + + // Close the writer + sink.close(); + } + + /** + * Produces a metrics table in the specified Sink using information from the ConnectorJsonNodeReader. + * + * @param sink The sink used for generating content. + * @param connectorJsonNodeReader The ConnectorJsonNodeReader providing connector information. + */ + private void produceMetricsTable(final Sink sink, final ConnectorJsonNodeReader connectorJsonNodeReader) { + sink.section2(); + sink.sectionTitle2(); + sink.text("Metrics"); + sink.sectionTitle2_(); + + sink.table(); + sink.tableRow(); + + sink.tableHeaderCell(SinkHelper.setClass(BOOTSTRAP_MEDIUM_4_CLASS)); + sink.text("Type"); + sink.tableHeaderCell_(); + + sink.tableHeaderCell(SinkHelper.setClass(BOOTSTRAP_MEDIUM_4_CLASS)); + sink.text("Collected Metrics"); + sink.tableHeaderCell_(); + + sink.tableHeaderCell(SinkHelper.setClass(BOOTSTRAP_MEDIUM_4_CLASS)); + sink.text("Specific Attributes"); + sink.tableHeaderCell_(); + + sink.tableRow_(); + + // For each object, create a new row + connectorJsonNodeReader + .getMonitors() + .ifPresent(monitors -> { + final Map sortedMonitors = new TreeMap<>(String.CASE_INSENSITIVE_ORDER); + + monitors + .fields() + .forEachRemaining(monitorEntry -> sortedMonitors.put(monitorEntry.getKey(), monitorEntry.getValue())); + + sortedMonitors.forEach((monitorType, monitor) -> { + sink.tableRow(); + + // Cell 1: Class + sink.tableCell(); + sink.bold(); + sink.text(monitorType); + sink.bold_(); + sink.tableCell_(); + + // Cell 2: List of metrics + sink.tableCell(); + sink.list(); + + final List metricList = new ArrayList<>(); + + // The present metric is only available on hardware monitors + if (OpenTelemetryHardwareType.isHardwareMonitorType(monitorType)) { + metricList.add(String.format("hw.status{hw.type=\"%s\", state=\"present\"}", monitorType)); + } + + metricList.addAll(connectorJsonNodeReader.getMonitorMetrics(monitor)); + + Collections.sort(metricList); + + for (String metricName : metricList) { + sink.listItem(); + sink.rawText(String.format("%s", metricName)); + sink.listItem_(); + } + + sink.list_(); + sink.tableCell_(); + + // Cell 3: List of attributes + sink.tableCell(); + sink.list(); + + connectorJsonNodeReader + .getMonitorAttributes(monitor) + .stream() + .sorted() + .forEach(attributeKey -> { + sink.listItem(); + sink.rawText(String.format("%s", attributeKey)); + sink.listItem_(); + }); + + sink.list_(); + sink.tableCell_(); + + sink.tableRow_(); + }); + }); + + sink.table_(); + sink.section2_(); + } + + /** + * Generates content to display MetricsHub examples (CLI and Agent) to execute the current connector. + * + * @param sink The sink used for generating content. + * @param osTypes OS types supported by the current connector. + * @param technology Technologies and protocols used by the current connector. + * @param sudoCommands List of sudo commands. + + */ + private void produceMetricsHubExamplesContent( + final Sink sink, + final List osTypes, + final Set technologies, + final List sudoCommands + ) { + sink.section2(); + sink.sectionTitle2(); + sink.text("Examples"); + sink.sectionTitle2_(); + + // Determine a possible host type + final String hostType = determinePossibleHostType(osTypes, technologies); + + // Build YAML config and CLI + final StringBuilder cli = new StringBuilder("metricshub HOSTNAME -t ").append(hostType); + cli.append(" -f ").append(connectorId); + final StringBuilder yamlBuilder = new StringBuilder( + "resourceGroups:\n :\n resources:\n :\n attributes:\n" + ); + yamlBuilder.append(" host.name: # Change with actual host name\n"); + yamlBuilder.append(" host.type: ").append(hostType).append("\n"); + yamlBuilder + .append(" selectConnectors: [ ") + .append(connectorId) + .append(" ] # Optional, to load only this connector\n") + .append(PROTOCOLS_SECTION); + + if (technologies.contains(TechnologyType.HTTP)) { + cli.append(" --https --http-port 443 -u USERNAME"); + yamlBuilder.append(" http:\n"); + yamlBuilder.append(" https: true\n"); + yamlBuilder.append(" port: 443 # or probably something else\n"); + appendYamlUsernameAndPassword(yamlBuilder); + } + + if (technologies.contains(TechnologyType.IPMI)) { + cli.append(" --ipmi -u USER"); + yamlBuilder.append(" ipmi:\n"); + appendYamlUsernameAndPassword(yamlBuilder); + yamlBuilder.append("\n # IPMI on Windows is accessed through WMI:\n"); + yamlBuilder.append(" :\n attributes:\n"); + yamlBuilder.append(" host.name: \n"); + yamlBuilder.append(" type: win\n"); + yamlBuilder.append(PROTOCOLS_SECTION); + yamlBuilder.append(WMI_SECTION); + appendYamlUsernameAndPassword(yamlBuilder); + yamlBuilder.append("\n # IPMI on Linux is accessed through SSH:\n"); + yamlBuilder.append(" :\n attributes:\n"); + yamlBuilder.append(" host.name: \n"); + yamlBuilder.append(" type: linux\n"); + yamlBuilder.append(PROTOCOLS_SECTION); + yamlBuilder.append(" ssh:\n"); + appendYamlUsernameAndPassword(yamlBuilder); + yamlBuilder.append(" userSudo: true\n"); + } + + if (technologies.contains(TechnologyType.SNMP)) { + cli.append(" --snmp v2c --community public"); + yamlBuilder.append(" snmp:\n"); + yamlBuilder.append(" version: v2c # Read documentation for v1, v2c and v3\n"); + yamlBuilder.append(" community: public # or probably something more secure"); + } + + if (technologies.contains(TechnologyType.OS_COMMAND)) { + if (OsType.WINDOWS.getPossibleHostType().equals(hostType)) { + cli.append(" --wmi -u USER"); + yamlBuilder.append(WMI_SECTION); + appendYamlUsernameAndPassword(yamlBuilder); + } else { + cli.append(" --ssh -u USER"); + yamlBuilder.append(" ssh:\n"); + appendYamlUsernameAndPassword(yamlBuilder); + if (!sudoCommands.isEmpty()) { + cli.append(" --sudo-command-list "); + cli.append(String.join(",", sudoCommands)); + yamlBuilder.append(" useSudo: true\n"); + yamlBuilder.append(" useSudoCommands: [ \""); + yamlBuilder.append(String.join("\", \"", sudoCommands)); + yamlBuilder.append("\" ]\n"); + } + } + } + + if (technologies.contains(TechnologyType.WMI)) { + cli.append(" --wmi -u USER"); + yamlBuilder.append(WMI_SECTION); + appendYamlUsernameAndPassword(yamlBuilder); + } + + if (technologies.contains(TechnologyType.WBEM)) { + cli.append(" --wbem -u USER"); + yamlBuilder.append(" wbem:\n"); + yamlBuilder.append(" protocol: https\n"); + yamlBuilder.append(" port: 5989\n"); + appendYamlUsernameAndPassword(yamlBuilder); + } + + // CLI + sink.section3(); + sink.sectionTitle3(); + sink.text("CLI"); + sink.sectionTitle3_(); + SinkHelper.insertCodeBlock(sink, "batch", cli.toString()); + sink.section3_(); + + // YAML Configuration + sink.section3(); + sink.sectionTitle3(); + sink.text("metricshub.yaml"); + sink.sectionTitle3_(); + SinkHelper.insertCodeBlock(sink, "yaml", yamlBuilder.toString()); + sink.section3_(); + + sink.section2_(); + } + + /** + * Determines the possible host type based on the provided list of operating system types and set of technology. + * + * @param osTypes The list of operating system types. + * @param technology The set of technology associated with the host. + * @return The determined host type. + */ + private String determinePossibleHostType(final List osTypes, final Set technologies) { + // Special case: for IPMI source, always use hostType = "oob" + if (technologies.contains(TechnologyType.IPMI)) { + return OsType.OOB.getPossibleHostType(); + } + + return osTypes + .stream() + .map(OsType::detect) + .filter(Optional::isPresent) + .map(Optional::get) + .map(OsType::getPossibleHostType) + .findFirst() + .orElse(OsType.LINUX.getPossibleHostType()); + } + + /** + * Appends a username and password configuration example to the provided YAML builder. + * + * @param yamlBuilder The {@link StringBuilder} representing the YAML configuration to which the example will be appended. + */ + private void appendYamlUsernameAndPassword(final StringBuilder yamlBuilder) { + yamlBuilder + .append(" username: # Change with actual credentials\n") + .append(" password: # Encrypted using metricshub-encrypt\n"); + } + + /** + * Produces content related to connector supersession information. + *

    + * This method generates content indicating whether the current connector is superseded by other connectors or + * if it supersedes other connectors. If the connector is superseded, a warning message is displayed along with + * a list of connectors that supersede it. If the connector supersedes others, an informational message is provided + * along with a list of connectors that it supersedes. + *

    + * @param sink The sink used for generating content. + * @param supersededMap Map of superseded connectors. + * @param connectorJsonNodeReader The reader for connector + + */ + private void produceSupersedesContent( + final Sink sink, + final Map> supersededMap, + final ConnectorJsonNodeReader connectorJsonNodeReader + ) { + // Superseded? + if (supersededMap.containsKey(connectorId)) { + final String textWarningCssClassName = "text-warning"; + sink.paragraph(SinkHelper.setClass(textWarningCssClassName)); + sink.rawText(SinkHelper.glyphIcon("warning-sign")); + sink.text(" This connector is superseded by: "); + sink.paragraph_(); + sink.list(); + supersededMap + .get(connectorId) + .forEach(supersedingConnectorId -> { + sink.listItem(SinkHelper.setClass(textWarningCssClassName)); + sink.link(SinkHelper.buildPageFilename(supersedingConnectorId), SinkHelper.setClass(textWarningCssClassName)); + sink.text(supersedingConnectorId); + sink.link_(); + sink.listItem_(); + }); + sink.list_(); + } + + // Superseding? + final List supersedes = connectorJsonNodeReader.getSupersedes(); + if (supersedes != null && !supersedes.isEmpty()) { + sink.paragraph(); + sink.rawText(SinkHelper.glyphIcon("info-sign")); + sink.text(" This connector supersedes: "); + sink.paragraph_(); + sink.list(); + supersedes + .stream() + .filter(Objects::nonNull) + .filter(value -> !value.isBlank()) + .forEach(supersededConnectorId -> { + sink.listItem(); + sink.link(SinkHelper.buildPageFilename(supersededConnectorId)); + sink.text(supersededConnectorId); + sink.link_(); + sink.listItem_(); + }); + + sink.list_(); + } + } + + /** + * Produces content related to sudo commands and privilege escalation based on the information + * retrieved from the {@code ConnectorJsonNodeReader}. + * + * @param sink The sink used for generating content. + * @param sudoCommands The list of sudo commands. + */ + private void produceSudoCommandsContent(final Sink sink, final List sudoCommands) { + // Requires root/sudo? + if (!sudoCommands.isEmpty()) { + final StringBuilder sudoersContent = new StringBuilder(); + + sink.paragraph(); + String commandWord = new ChoiceFormat("1#command|1" + sudoCommand + ""); + sink.listItem_(); + sudoersContent.append(account).append(" ALL=(root) NOPASSWD: ").append(sudoCommand).append("\n"); + } + sink.list_(); + + sink.paragraph(); + sink.text("This connector therefore needs to run as "); + sink.bold(); + sink.text("root"); + sink.bold_(); + sink.text(" or you need to configure a privilege-escalation mechanism like "); + sink.rawText("sudo"); + sink.text( + String.format(" on the managed host to allow the monitoring account to run the %s listed above.", commandWord) + ); + sink.paragraph_(); + + // /etc/sudoers sample + sink.paragraph(); + sink.text("Sample of "); + sink.bold(); + sink.text("/etc/sudoers"); + sink.bold_(); + sink.text(String.format(" to allow the above %s to be run as ", commandWord)); + sink.bold(); + sink.text("root"); + sink.bold_(); + sink.text(" by the "); + sink.bold(); + sink.text(account); + sink.bold_(); + sink.text(" account:"); + sink.lineBreak(); + SinkHelper.insertCodeBlock(sink, "bash", sudoersContent.toString()); + sink.paragraph_(); + } + } +} diff --git a/src/main/java/org/sentrysoftware/maven/metricshub/connector/producer/JsonNodeHelper.java b/src/main/java/org/sentrysoftware/maven/metricshub/connector/producer/JsonNodeHelper.java new file mode 100644 index 0000000..bbfc022 --- /dev/null +++ b/src/main/java/org/sentrysoftware/maven/metricshub/connector/producer/JsonNodeHelper.java @@ -0,0 +1,145 @@ +package org.sentrysoftware.maven.metricshub.connector.producer; + +/*- + * ╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲ + * MetricsHub Connector Maven Plugin + * ჻჻჻჻჻჻ + * Copyright (C) 2023 Sentry Software + * ჻჻჻჻჻჻ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱ + */ + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ArrayNode; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Spliterator; +import java.util.Spliterators; +import java.util.stream.Collectors; +import java.util.stream.Stream; +import java.util.stream.StreamSupport; +import lombok.AccessLevel; +import lombok.NoArgsConstructor; + +/** + * Utility class for common operations and methods with JsonNode objects. + */ +@NoArgsConstructor(access = AccessLevel.PRIVATE) +public class JsonNodeHelper { + + private static final Map BOOLEAN_MAP = new HashMap<>(); + + static { + BOOLEAN_MAP.put("0", Boolean.FALSE); + BOOLEAN_MAP.put("1", Boolean.TRUE); + BOOLEAN_MAP.put("true", Boolean.TRUE); + BOOLEAN_MAP.put("false", Boolean.FALSE); + } + + /** + * Checks if the provided JsonNode is non-null and not a JSON null node. + * + * @param node The JsonNode to be checked. + * @return {@code true} if the node is non-null and not a JSON null, {@code false} otherwise. + */ + public static boolean nonNull(final JsonNode node) { + return node != null && !node.isNull(); + } + + /** + * Converts an {@link ArrayNode} into a filtered {@link Stream} of non-null {@link JsonNode} elements. + *

    + * This method transforms the provided {@link ArrayNode} into a sequential stream of {@link JsonNode} + * objects, filtering out any null elements from the stream. The resulting stream is ordered and immutable. + *

    + * @param arrayNode The {@link ArrayNode} to be converted to a {@link Stream}. + * @return A sequential {@link Stream} of non-null {@link JsonNode} elements extracted from the given {@link ArrayNode}. + */ + public static Stream stream(final ArrayNode arrayNode) { + return StreamSupport + .stream( + Spliterators.spliterator(arrayNode.iterator(), arrayNode.size(), Spliterator.ORDERED | Spliterator.IMMUTABLE), + false + ) + .filter(Objects::nonNull); + } + + /** + * Converts a {@link JsonNode} to a list of strings. + *

    + * This method takes a {@link JsonNode} as input and converts it into a list of strings. If the input node is an array, + * the method uses {@link #convertSimpleArrayNodeToList(ArrayNode)} to perform the conversion. If the node is a single + * value node, it is converted into a singleton list containing its text representation. If the input node is null or + * not of the expected types, an empty list is returned. + *

    + * @param node The {@link JsonNode} to convert to a list of strings. + * @return A {@link List} of strings representing the contents of the input node, or an empty list if the node is null + * or not of the expected types. + */ + public static List nodeToStringList(final JsonNode node) { + if (nonNull(node)) { + if (node.isArray()) { + return convertSimpleArrayNodeToList((ArrayNode) node); + } else if (node.isValueNode()) { + return Collections.singletonList(node.asText()); + } + } + + return Collections.emptyList(); + } + + /** + * Converts a Jackson {@link ArrayNode} to a {@link List} of strings. + * + * @param arrayNode The {@code ArrayNode} to be converted to a list of strings. + * @return A {@code List} containing the text representations of the elements in the {@code ArrayNode}. + * @throws NullPointerException if {@code arrayNode} is {@code null}. + */ + public static List convertSimpleArrayNodeToList(final ArrayNode arrayNode) { + return stream(arrayNode).map(JsonNode::asText).collect(Collectors.toList()); + } + + /** + * Returns the text value of the provided JsonNode if it is non-null and not a JSON null node. + * If the node is null or represents a JSON null, the specified default value is returned. + * + * @param node The JsonNode to extract text from. + * @param defaultValue The default value to return if the node is null or represents a JSON null. + * @return The text value of the JsonNode or the specified default value if the node is null or represents a JSON null. + */ + public static String nonNullTextOrDefault(final JsonNode node, final String defaultValue) { + if (nonNull(node)) { + return node.asText(); + } + return defaultValue; + } + + /** + * Returns the boolean value of the provided JsonNode if it is non-null and not a JSON null node. + * If the node is null or represents a JSON null, the specified default value is returned. + * + * @param node The JsonNode to extract text from. + * @param defaultValue The default value to return if the node is null or represents a JSON null. + * @return The boolean value of the JsonNode or the specified default value if the node is null or represents a JSON null. + */ + public static boolean nonNullBooleanOrDefault(final JsonNode node, final boolean defaultValue) { + if (nonNull(node)) { + return BOOLEAN_MAP.getOrDefault(node.asText().trim(), defaultValue); + } + return defaultValue; + } +} diff --git a/src/main/java/org/sentrysoftware/maven/metricshub/connector/producer/MainPageReferenceProducer.java b/src/main/java/org/sentrysoftware/maven/metricshub/connector/producer/MainPageReferenceProducer.java new file mode 100644 index 0000000..b8cb935 --- /dev/null +++ b/src/main/java/org/sentrysoftware/maven/metricshub/connector/producer/MainPageReferenceProducer.java @@ -0,0 +1,158 @@ +package org.sentrysoftware.maven.metricshub.connector.producer; + +/*- + * ╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲ + * MetricsHub Connector Maven Plugin + * ჻჻჻჻჻჻ + * Copyright (C) 2023 Sentry Software + * ჻჻჻჻჻჻ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱ + */ + +import com.fasterxml.jackson.databind.JsonNode; +import java.util.Comparator; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Objects; +import lombok.AllArgsConstructor; +import org.apache.maven.doxia.sink.Sink; +import org.apache.maven.plugin.logging.Log; +import org.sentrysoftware.maven.metricshub.connector.ReferenceReport; +import org.sentrysoftware.maven.metricshub.connector.producer.model.common.OsType; + +/** + * Utility class for producing main page references. + */ +@AllArgsConstructor +public class MainPageReferenceProducer { + + private static final String BOOTSTRAP_MEDIUM_3_CLASS = "col-md-3"; + + private final String connectorSubdirectoryName; + private final Log logger; + + /** + * Produces the main page reference that lists all the connectors. + * + * @param mainSink The main sink used for generating content. + * @param connectors The map of connector identifiers to their corresponding JsonNodes. + */ + public void produce(final Sink mainSink, final Map connectors) { + Objects.requireNonNull(connectorSubdirectoryName, () -> "connectorSubdirectoryName cannot be null."); + Objects.requireNonNull(mainSink, () -> "mainSink cannot be null."); + Objects.requireNonNull(logger, () -> "logger cannot be null."); + Objects.requireNonNull(connectors, () -> "connectors cannot be null."); + + logger.debug(String.format("Generating the main page %s.html", ReferenceReport.CONNECTOR_REFERENCE_OUTPUT_NAME)); + + mainSink.head(); + mainSink.title(); + mainSink.text("Connector Library"); + mainSink.title_(); + mainSink.head_(); + + mainSink.body(); + + // Title + mainSink.section1(); + mainSink.sectionTitle1(); + mainSink.text("Connector Library"); + mainSink.sectionTitle1_(); + + // Intro + mainSink.paragraph(); + mainSink.text( + "This page lists the Connectors of ${project.name} ${project.version}." + + " Each page provides you with the details on each Connector, the targeted platform," + + " the protocol used, the discovered components and monitored attributes." + ); + mainSink.paragraph_(); + + // Table header + mainSink.table(); + mainSink.tableRow(); + mainSink.tableHeaderCell(SinkHelper.setClass(BOOTSTRAP_MEDIUM_3_CLASS)); + mainSink.text("Name"); + mainSink.tableHeaderCell_(); + mainSink.tableHeaderCell(SinkHelper.setClass(BOOTSTRAP_MEDIUM_3_CLASS)); + mainSink.text("Connector ID"); + mainSink.tableHeaderCell_(); + mainSink.tableHeaderCell(SinkHelper.setClass(BOOTSTRAP_MEDIUM_3_CLASS)); + mainSink.text("Platform"); + mainSink.tableHeaderCell_(); + mainSink.tableHeaderCell(SinkHelper.setClass(BOOTSTRAP_MEDIUM_3_CLASS)); + mainSink.text("Operating Systems"); + mainSink.tableHeaderCell_(); + mainSink.tableRow_(); + + // A comparison function which compare connectors by display name + final Comparator> comparator = (e1, e2) -> + new ConnectorJsonNodeReader(e1.getValue()) + .getDisplayName() + .toLowerCase() + .compareTo(new ConnectorJsonNodeReader(e2.getValue()).getDisplayName().toLowerCase()); + + connectors + .entrySet() + .stream() + .sorted(comparator) + .forEach(connectorEntry -> { + final JsonNode connector = connectorEntry.getValue(); + final String connectorId = connectorEntry.getKey(); + + final ConnectorJsonNodeReader connectorJsonNodeReader = new ConnectorJsonNodeReader(connector); + + // Builds the HTML page file name corresponding to the specified connector identifier + final String pageFilename = SinkHelper.buildPageFilename(connectorId); + + // Builds the HTML page path name corresponding to the specified connector page filename + final String connectorPagePath = String.format("%s/%s", connectorSubdirectoryName, pageFilename); + + // Add a row to the table in the main page + mainSink.tableRow(); + + mainSink.tableCell(); + + mainSink.link(connectorPagePath); + mainSink.text(connectorJsonNodeReader.getDisplayName()); + mainSink.link_(); + mainSink.tableCell_(); + + mainSink.tableCell(); + mainSink.link(connectorPagePath); + mainSink.text(connectorId); + mainSink.link_(); + mainSink.tableCell_(); + + mainSink.tableCell(); + mainSink.text(SinkHelper.replaceCommaWithSpace(connectorJsonNodeReader.getPlatformsOrDefault("N/A"))); + mainSink.tableCell_(); + + mainSink.tableCell(); + mainSink.text(String.join(", ", OsType.mapToDisplayNames(connectorJsonNodeReader.getAppliesTo()))); + mainSink.tableCell_(); + + mainSink.tableRow_(); + }); + + // Close the main page + mainSink.table_(); + + mainSink.section1_(); + + mainSink.body_(); + + mainSink.close(); + } +} diff --git a/src/main/java/org/sentrysoftware/maven/metricshub/connector/producer/PlatformReportProducer.java b/src/main/java/org/sentrysoftware/maven/metricshub/connector/producer/PlatformReportProducer.java new file mode 100644 index 0000000..9d39e44 --- /dev/null +++ b/src/main/java/org/sentrysoftware/maven/metricshub/connector/producer/PlatformReportProducer.java @@ -0,0 +1,223 @@ +package org.sentrysoftware.maven.metricshub.connector.producer; + +/*- + * ╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲ + * MetricsHub Connector Maven Plugin + * ჻჻჻჻჻჻ + * Copyright (C) 2023 Sentry Software + * ჻჻჻჻჻჻ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱ + */ + +import static org.sentrysoftware.maven.metricshub.connector.Constants.CONNECTOR_SUBDIRECTORY_NAME; + +import java.util.Collections; +import java.util.Iterator; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Objects; +import java.util.Set; +import java.util.stream.Collectors; +import lombok.AllArgsConstructor; +import org.apache.maven.doxia.sink.Sink; +import org.apache.maven.plugin.logging.Log; +import org.sentrysoftware.maven.metricshub.connector.producer.model.platform.Platform; + +/** + * Utility class for producing platforms and requirements report page. + */ +@AllArgsConstructor +public class PlatformReportProducer { + + private final Log logger; + + /** + * Produces the Platforms and Requirements page. + * + * @param mainSink The main sink used for generating content. + * @param osPlatforms Map of OS to supported platforms. + */ + public void produce(final Sink mainSink, final Map> osPlatforms) { + Objects.requireNonNull(mainSink, () -> "mainSink cannot be null."); + Objects.requireNonNull(logger, () -> "logger cannot be null."); + Objects.requireNonNull(osPlatforms, () -> "osPlatforms cannot be null."); + + final String pageFilename = "platform-requirements.html"; + logger.debug("Generating " + pageFilename); + + mainSink.head(); + mainSink.title(); + mainSink.text("Supported Platforms and Requirements"); + mainSink.title_(); + mainSink.head_(); + + mainSink.body(); + + // Title + mainSink.section1(); + mainSink.sectionTitle1(); + mainSink.text("Supported Platforms and Requirements"); + mainSink.sectionTitle1_(); + + // Introduction + mainSink.paragraph(); + mainSink.text( + "This page lists all of the supported platforms, grouped by system type," + + " and the corresponding prerequisites to ensure that the Connectors will be able" + + " to connect and gather the required information to assess the health of the platform." + ); + mainSink.paragraph_(); + + // Sort the entries in osPlatforms + final Set osSet = osPlatforms + .keySet() + .stream() + .sorted(String.CASE_INSENSITIVE_ORDER) + .collect(Collectors.toCollection(LinkedHashSet::new)); + + // ToC + mainSink.list(); + for (String os : osSet) { + mainSink.listItem(); + mainSink.link("#" + os.toLowerCase().replace(' ', '-')); + mainSink.text(os); + mainSink.link_(); + mainSink.listItem_(); + } + mainSink.list_(); + + // For each entry is osMap + for (final String os : osSet) { + // List the platforms + final List platformList = osPlatforms.get(os); + Collections.sort(platformList); + + mainSink.anchor(os.toLowerCase().replace(' ', '-')); + mainSink.anchor_(); + mainSink.section2(); + mainSink.sectionTitle2(); + mainSink.text(os); + mainSink.sectionTitle2_(); + + // Table header + mainSink.table(); + mainSink.tableRow(); + mainSink.tableHeaderCell(SinkHelper.setClass("col-md-2")); + mainSink.text("Platform"); + mainSink.tableHeaderCell_(); + mainSink.tableHeaderCell(SinkHelper.setClass("col-md-2")); + mainSink.text("Technology/Protocols"); + mainSink.tableHeaderCell_(); + mainSink.tableHeaderCell(SinkHelper.setClass("col-md-4")); + mainSink.text("Connector"); + mainSink.tableHeaderCell_(); + mainSink.tableHeaderCell(SinkHelper.setClass("col-md-4")); + mainSink.text("Instrumentation Prerequisites"); + mainSink.tableHeaderCell_(); + mainSink.tableRow_(); + + // List all of the platforms for this OS + producePlatformRows(mainSink, platformList); + + // Close the tables + mainSink.table_(); + + // Close the section + mainSink.section2_(); + } + + // Close the Supported Platforms page + mainSink.section1_(); + mainSink.body_(); + mainSink.close(); + } + + /** + * Produces rows for the platform table. + * + * @param mainSink The main sink used for generating content. + * @param platformList List of Platform objects. + */ + private void producePlatformRows(final Sink mainSink, final List platformList) { + for (Platform platform : platformList) { + mainSink.tableRow(); + + mainSink.tableCell(); + final boolean useItalic = platform.getName().toLowerCase().startsWith("any "); + if (useItalic) { + mainSink.italic(); + } + + final String[] platformArray = platform.getName().split(","); + if (platformArray.length > 0) { + mainSink.text(platformArray[0]); + for (int i = 1; i < platformArray.length; i++) { + mainSink.lineBreak(); + mainSink.text(platformArray[i]); + } + } + + if (useItalic) { + mainSink.italic_(); + } + mainSink.tableCell_(); + + mainSink.tableCell(); + mainSink.text(platform.getTechnology()); + mainSink.tableCell_(); + + mainSink.tableCell(); + final Map connectors = platform.getConnectors(); + if (!connectors.isEmpty()) { + final Iterator> iterator = connectors.entrySet().iterator(); + final Entry entry = iterator.next(); + writeConnectorLink(mainSink, entry); + while (iterator.hasNext()) { + final Entry next = iterator.next(); + mainSink.lineBreak(); + writeConnectorLink(mainSink, next); + } + } + mainSink.tableCell_(); + + mainSink.tableCell(); + final Set prerequisitesArray = platform.getPrerequisites(); + if (!prerequisitesArray.isEmpty()) { + final Iterator iterator = prerequisitesArray.iterator(); + mainSink.text(iterator.next()); + while (iterator.hasNext()) { + mainSink.lineBreak(); + mainSink.text(iterator.next()); + } + } + mainSink.tableCell_(); + + mainSink.tableRow_(); + } + } + + /** + * Writes a link to a connector page in the main sink. + * + * @param mainSink The main sink used for generating content. + * @param connectorEntry The entry containing the connector ID and its display name. + */ + private void writeConnectorLink(final Sink mainSink, final Entry connectorEntry) { + mainSink.link(CONNECTOR_SUBDIRECTORY_NAME + "/" + SinkHelper.buildPageFilename(connectorEntry.getKey())); + mainSink.text(connectorEntry.getValue()); + mainSink.link_(); + } +} diff --git a/src/main/java/org/sentrysoftware/maven/metricshub/connector/producer/SinkHelper.java b/src/main/java/org/sentrysoftware/maven/metricshub/connector/producer/SinkHelper.java new file mode 100644 index 0000000..6fa6617 --- /dev/null +++ b/src/main/java/org/sentrysoftware/maven/metricshub/connector/producer/SinkHelper.java @@ -0,0 +1,156 @@ +package org.sentrysoftware.maven.metricshub.connector.producer; + +/*- + * ╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲ + * MetricsHub Connector Maven Plugin + * ჻჻჻჻჻჻ + * Copyright (C) 2023 Sentry Software + * ჻჻჻჻჻჻ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱ + */ + +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import lombok.AccessLevel; +import lombok.NoArgsConstructor; +import org.apache.maven.doxia.sink.Sink; +import org.apache.maven.doxia.sink.SinkEventAttributes; +import org.apache.maven.doxia.sink.impl.SinkEventAttributeSet; + +/** + * Utility class providing helper methods for generating content in a Sink format. + *

    + * The class includes methods for setting CSS classes, inserting code blocks with syntax highlighting, + * outputting FontAwesome and Glyphicon icons, building HTML page filenames, etc. + *

    + */ +@NoArgsConstructor(access = AccessLevel.PRIVATE) +public class SinkHelper { + + /** + * Use a compiled representation of a regular expression to find commas followed by non-whitespace + */ + private static final Pattern COMMA_FOLLOWED_BY_NON_WHITESPACE = Pattern.compile(",(?=\\S)"); + + /** + * Creates an AttributeSet that sets the CSS class to the specified class + * + * @param className The class to be set + * @return the AttributeSet that can be used in any Sink element + */ + public static SinkEventAttributes setClass(final String className) { + // Create a new AttributeSet + final SinkEventAttributes attributes = new SinkEventAttributeSet(); + + // Set the class + attributes.addAttribute(SinkEventAttributes.CLASS, className); + + return attributes; + } + + /** + * Inserts a code block into the provided Sink, applying syntax highlighting if a language is specified. + * + * @param sink The Sink to which the code block will be inserted. + * @param language The programming language for syntax highlighting (null or empty for no highlighting). + * @param code The code to be inserted into the code block. + */ + public static void insertCodeBlock(final Sink sink, final String language, final String code) { + if (language != null && !language.isEmpty()) { + sink.verbatim(setClass("language-" + language)); + sink.rawText(""); + } else { + sink.verbatim(null); + sink.rawText(""); + } + sink.text(code); + sink.rawText(""); + sink.verbatim_(); + } + + /** + * Returns the HTML code to output a FontAwesome icon
    + * Example: + *
    +	 * <i class="fa fa-arrows-v" aria-hidden="true"></i>
    +	 * 
    + *

    + * Note: Returns a question icon if specified icon is empty or null + *

    + * @param iconName The name of the FontAwesome icon to insert (without the "fa-" prefix) + * @return the HTML code for this icon + */ + public static String faIcon(final String iconName) { + String faIconName = "question"; + if (iconName != null && !iconName.isEmpty()) { + faIconName = iconName; + } + return String.format("", faIconName); + } + + /** + * Returns the HTML code to output a Glyphicon (Bootstrap) icon + *
    + * Example: + *
    +	 * <i class="glyphicon glyphicon-info-sign" aria-hidden="true"></i>
    +	 * 
    + *

    + * Note: Returns a question icon if specified icon is empty or null + *

    + * @param iconName The name of the Glyphicon icon to insert (without the "glyphicon-" prefix) + * @return the HTML code for this icon + */ + public static String glyphIcon(final String iconName) { + String glyphIconName = "question-sign"; + if (iconName != null && !iconName.isEmpty()) { + glyphIconName = iconName; + } + return String.format("", glyphIconName); + } + + /** + * Builds the HTML page file name corresponding to the specified connector identifier. + * + * @param connectorId The connector identifier. + * @return The corresponding HTML page filename. + */ + public static String buildPageFilename(final String connectorId) { + return connectorId.toLowerCase() + ".html"; + } + + /** + * Replaces commas followed by non-whitespace with spaces in the given input string. + * + * @param input The input string containing commas to be replaced. + * @return A new string with commas followed by non-whitespace replaced by spaces. + */ + public static String replaceCommaWithSpace(final String input) { + // Use a Matcher to find matches + final Matcher matcher = COMMA_FOLLOWED_BY_NON_WHITESPACE.matcher(input); + + return matcher.replaceAll(" "); + } + + /** + * Replaces special characters in the given string with their corresponding HTML numeric codes.
    + * This will avoid velocity evaluation errors. + * + * @param value The input string. + * @return The string with special characters replaced by their HTML numeric codes. + */ + public static String replaceWithHtmlCode(final String value) { + return value.replace("$", "$"); + } +} diff --git a/src/main/java/org/sentrysoftware/maven/metricshub/connector/producer/model/common/OpenTelemetryHardwareType.java b/src/main/java/org/sentrysoftware/maven/metricshub/connector/producer/model/common/OpenTelemetryHardwareType.java new file mode 100644 index 0000000..6ff9e3f --- /dev/null +++ b/src/main/java/org/sentrysoftware/maven/metricshub/connector/producer/model/common/OpenTelemetryHardwareType.java @@ -0,0 +1,140 @@ +package org.sentrysoftware.maven.metricshub.connector.producer.model.common; + +/*- + * ╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲ + * MetricsHub Connector Maven Plugin + * ჻჻჻჻჻჻ + * Copyright (C) 2023 Sentry Software + * ჻჻჻჻჻჻ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱ + */ + +import java.util.Set; +import java.util.stream.Collectors; +import java.util.stream.Stream; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NonNull; + +/** + * Enumeration representing different OpenTelemetry hardware monitor types. See + * Semantic + * Conventions for Hardware Metrics + */ +@Getter +@AllArgsConstructor +public enum OpenTelemetryHardwareType { + /** + * Host + */ + HOST("host"), + /** + * Battery + */ + BATTERY("battery"), + /** + * Blade Chassis + */ + BLADE("blade"), + /** + * CPU + */ + CPU("cpu"), + /** + * Disk Controller + */ + DISK_CONTROLLER("disk_controller"), + /** + * Enclosure + */ + ENCLOSURE("enclosure"), + /** + * Fan + */ + FAN("fan"), + /** + * GPU + */ + GPU("gpu"), + /** + * LED + */ + LED("led"), + /** + * Logical Disk + */ + LOGICAL_DISK("logical_disk"), + /** + * Memory Module + */ + MEMORY("memory"), + /** + * Network Device + */ + NETWORK("network"), + /** + * Other Device + */ + OTHER_DEVICE("other_device"), + /** + * Physical Disk + */ + PHYSICAL_DISK("physical_disk"), + /** + * Power Supply + */ + POWER_SUPPLY("power_supply"), + /** + * Robotics + */ + ROBOTICS("robotics"), + /** + * Tape Drive + */ + TAPE_DRIVE("tape_drive"), + /** + * Temperature + */ + TEMPERATURE("temperature"), + /** + * Voltage + */ + VOLTAGE("voltage"); + + private String key; + + /** + * Set of hardware monitor types + */ + public static final Set MONITOR_TYPES = Stream + .of(values()) + .map(OpenTelemetryHardwareType::getKey) + .collect(Collectors.toSet()); + + /** + * Checks whether the given string represents a hardware monitor type. + *

    + * This method performs a case-insensitive and whitespace-trimmed comparison with the predefined set + * of hardware monitor types. + *

    + * + * @param type The string to be checked for hardware monitor type. + * @return {@code true} if the provided string is a hardware monitor type, {@code false} otherwise. + * @throws IllegalArgumentException if the provided value is {@code null}. + */ + public static boolean isHardwareMonitorType(@NonNull final String type) { + return MONITOR_TYPES.contains(type.trim().toLowerCase()); + } +} diff --git a/src/main/java/org/sentrysoftware/maven/metricshub/connector/producer/model/common/OsType.java b/src/main/java/org/sentrysoftware/maven/metricshub/connector/producer/model/common/OsType.java new file mode 100644 index 0000000..e5ca219 --- /dev/null +++ b/src/main/java/org/sentrysoftware/maven/metricshub/connector/producer/model/common/OsType.java @@ -0,0 +1,156 @@ +package org.sentrysoftware.maven.metricshub.connector.producer.model.common; + +/*- + * ╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲ + * MetricsHub Connector Maven Plugin + * ჻჻჻჻჻჻ + * Copyright (C) 2023 Sentry Software + * ჻჻჻჻჻჻ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱ + */ + +import java.util.AbstractMap.SimpleEntry; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Optional; +import java.util.regex.Pattern; +import java.util.stream.Collectors; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NonNull; + +/** + * Enumeration representing different operating system types. + */ +@AllArgsConstructor +public enum OsType { + /** + * HP OpenVMS + */ + VMS("HP OpenVMS", "vms"), + + /** + * HP Tru64 + */ + TRU64("HP Tru64", "tru64"), + + /** + * HP-UX + */ + HPUX("HP-UX", "hp"), + + /** + * IBM AIX + */ + AIX("IBM AIX", "aix"), + + /** + * Linux + */ + LINUX("Linux", "linux"), + + /** + * Out-Of-Band + */ + OOB("Out-Of-Band", "management"), + + /** + * Microsoft Windows + */ + WINDOWS("Microsoft Windows", "win"), + + /** + * Network Device + */ + NETWORK("Network Device", "network"), + + /** + * Storage System + */ + STORAGE("Storage System", "storage"), + + /** + * Oracle Solaris + */ + SOLARIS("Oracle Solaris", "solaris"); + + @Getter + private String displayName; + + @Getter + private String possibleHostType; + + /** + * Maps each OsType to a compiled representation of the regular expression that detects it. + */ + private static final Map DETECTORS = Map.ofEntries( + new SimpleEntry<>(LINUX, Pattern.compile("^lin$|^linux$")), + new SimpleEntry<>(WINDOWS, Pattern.compile("^(microsoft\\s*)?windows$|^win$|^nt$")), + new SimpleEntry<>( + OOB, + Pattern.compile("^management$|^mgmt$|^management\\s*card$|^out-of-band$|^out\\s*of\\s*band$|^oob$") + ), + new SimpleEntry<>(NETWORK, Pattern.compile("^network$|^switch$")), + new SimpleEntry<>(STORAGE, Pattern.compile("^storage$|^san$|^library$|^array$")), + new SimpleEntry<>(VMS, Pattern.compile("^vms$|^(hp\\s*)?open\\s*vms$")), + new SimpleEntry<>(TRU64, Pattern.compile("^tru64$|^osf1$|^hp\\s*tru64\\s*unix$")), + new SimpleEntry<>(HPUX, Pattern.compile("^hp-ux$|^hpux$|^hp$")), + new SimpleEntry<>(AIX, Pattern.compile("^ibm(\\s*|-)aix$|^aix$|^rs6000$")), + new SimpleEntry<>(SOLARIS, Pattern.compile("^((sun|oracle)\\s*)?solaris$|^sunos$")) + ); + + /** + * Detects the {@link OsType} display name using the provided value. + * + * @param value The value to process. + * @return The display name of the detected OS type, or the original value if no match is found. + */ + public static String detectDisplayName(final String value) { + // Null? returns null + if (value == null) { + return value; + } + + // Check all regex in DETECTORS to see which one matches + return detect(value).map(OsType::getDisplayName).orElse(value); + } + + /** + * Detects the operating system type based on the provided string value. + * + * @param value The string value used for operating system detection. + * @return An {@code Optional} containing the detected {@code OsType}, or empty if no match is found. + * @throws IllegalArgumentException If the provided value is null. + */ + public static Optional detect(@NonNull final String value) { + final String lCaseValue = value.trim().toLowerCase(); + for (Map.Entry detector : DETECTORS.entrySet()) { + if (detector.getValue().matcher(lCaseValue).find()) { + return Optional.of(detector.getKey()); + } + } + return Optional.empty(); + } + + /** + * Converts a list of operating system types to a list of operating system display names. + * + * @param osTypeValues The list of operating system types. + * @return A list of String values based on the provided list of operating system types. + */ + public static List mapToDisplayNames(final List osTypeValues) { + return osTypeValues.stream().map(OsType::detectDisplayName).filter(Objects::nonNull).collect(Collectors.toList()); + } +} diff --git a/src/main/java/org/sentrysoftware/maven/metricshub/connector/producer/model/common/TechnologyType.java b/src/main/java/org/sentrysoftware/maven/metricshub/connector/producer/model/common/TechnologyType.java new file mode 100644 index 0000000..9a9612e --- /dev/null +++ b/src/main/java/org/sentrysoftware/maven/metricshub/connector/producer/model/common/TechnologyType.java @@ -0,0 +1,103 @@ +package org.sentrysoftware.maven.metricshub.connector.producer.model.common; + +/*- + * ╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲ + * MetricsHub Connector Maven Plugin + * ჻჻჻჻჻჻ + * Copyright (C) 2023 Sentry Software + * ჻჻჻჻჻჻ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱ + */ + +import java.util.Map; +import java.util.Optional; +import lombok.AllArgsConstructor; +import lombok.Getter; + +/** + * Enumeration representing different technology types such as HTTP, IPMI, SNMP, WMI, WBEM, System Commands, etc. + */ +@AllArgsConstructor +public enum TechnologyType { + /** + * HTTP/REST + */ + HTTP("HTTP/REST"), + + /** + * Intelligent Platform Management Interface (IPMI) + */ + IPMI("IPMI"), + + /** + * System Commands + */ + OS_COMMAND("System Commands"), + + /** + * Simple Network Management Protocol (SNMP) + */ + SNMP("SNMP"), + + /** + * Web-Based Enterprise Management (WBEM) + */ + WBEM("WBEM"), + + /** + * Windows Management Instrumentation (WMI) + */ + WMI("WMI"); + + @Getter + private String displayName; + + /** + * Mapping of connector source types to corresponding technology types. + *

    + * This static map associates connector source types (such as "http", "ipmi", etc.) with their + * corresponding {@link TechnologyType}. This map is used to identify the technology type based on + * the source type when processing connector technology. + *

    + */ + // @formatter:off + private static final Map TECHNOLOGY_TYPE_MAP = Map.of( + "http", HTTP, + "ipmi", IPMI, + "oscommand", OS_COMMAND, + "snmptable", SNMP, + "snmpget", SNMP, + "wbem", WBEM, + "wmi", WMI + ); + + // @formatter:on + + /** + * Retrieves the technology type associated with the provided source key. + *

    + * This method looks up the {@link TechnologyType} associated with the given source type + * in the {@link #TECHNOLOGY_TYPE_MAP}. The source key is case-insensitive and trimmed before the lookup. + * + * @param sourceType The source type for which to retrieve the corresponding {@link TechnologyType}. + * @return An {@link Optional} containing the associated {@link TechnologyType}, or an empty {@link Optional} + * if the technology source key is not found in the map. + */ + public static Optional getTechnologyType(final String sourceType) { + if (sourceType == null) { + return Optional.empty(); + } + return Optional.ofNullable(TECHNOLOGY_TYPE_MAP.get(sourceType.trim().toLowerCase())); + } +} diff --git a/src/main/java/org/sentrysoftware/example/Example.java b/src/main/java/org/sentrysoftware/maven/metricshub/connector/producer/model/common/package-info.java similarity index 77% rename from src/main/java/org/sentrysoftware/example/Example.java rename to src/main/java/org/sentrysoftware/maven/metricshub/connector/producer/model/common/package-info.java index 211d94a..feb76c4 100644 --- a/src/main/java/org/sentrysoftware/example/Example.java +++ b/src/main/java/org/sentrysoftware/maven/metricshub/connector/producer/model/common/package-info.java @@ -1,10 +1,12 @@ -package org.sentrysoftware.example; - +/** + * The part of library that groups common data model. + */ +package org.sentrysoftware.maven.metricshub.connector.producer.model.common; /*- * ╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲ - * MY PROJECT + * MetricsHub Connector Maven Plugin * ჻჻჻჻჻჻ - * Copyright 2023 Sentry Software + * Copyright (C) 2023 Sentry Software * ჻჻჻჻჻჻ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -19,16 +21,3 @@ * limitations under the License. * ╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱ */ - -/** - * An Example class - */ -public class Example { - - /** - * The main function - */ - public static void main() { - System.out.println("Hello Open-source World!"); - } -} diff --git a/src/main/java/org/sentrysoftware/maven/metricshub/connector/producer/model/criteria/AbstractCriterion.java b/src/main/java/org/sentrysoftware/maven/metricshub/connector/producer/model/criteria/AbstractCriterion.java new file mode 100644 index 0000000..e06edee --- /dev/null +++ b/src/main/java/org/sentrysoftware/maven/metricshub/connector/producer/model/criteria/AbstractCriterion.java @@ -0,0 +1,59 @@ +package org.sentrysoftware.maven.metricshub.connector.producer.model.criteria; + +/*- + * ╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲ + * MetricsHub Connector Maven Plugin + * ჻჻჻჻჻჻ + * Copyright (C) 2023 Sentry Software + * ჻჻჻჻჻჻ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱ + */ + +import static org.sentrysoftware.maven.metricshub.connector.producer.JsonNodeHelper.nonNullTextOrDefault; + +import com.fasterxml.jackson.databind.JsonNode; +import lombok.AllArgsConstructor; + +/** + * Abstract class representing a criterion. + *

    + * This abstract class defines the contract for criterion types, providing a + * method to accept a visitor implementing specific business logic. + *

    + *

    + * This abstract class also exposes shared methods that are accessible to each + * criterion. + *

    + */ +@AllArgsConstructor +public abstract class AbstractCriterion { + + protected JsonNode criterion; + + /** + * Accepts the given visitor. + * + * @param visitor The visitor class with its specific business logic. + */ + public abstract void accept(ICriterionVisitor visitor); + + /** + * Gets the expected result from the criterion, or a {@code null} if not present. + * + * @return The expected result from the criterion, or the {@code null} if not present. + */ + protected String getExpectedResult() { + return nonNullTextOrDefault(criterion.get("expectedResult"), null); + } +} diff --git a/src/main/java/org/sentrysoftware/maven/metricshub/connector/producer/model/criteria/AbstractSnmpCriterion.java b/src/main/java/org/sentrysoftware/maven/metricshub/connector/producer/model/criteria/AbstractSnmpCriterion.java new file mode 100644 index 0000000..0692289 --- /dev/null +++ b/src/main/java/org/sentrysoftware/maven/metricshub/connector/producer/model/criteria/AbstractSnmpCriterion.java @@ -0,0 +1,64 @@ +package org.sentrysoftware.maven.metricshub.connector.producer.model.criteria; + +/*- + * ╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲ + * MetricsHub Connector Maven Plugin + * ჻჻჻჻჻჻ + * Copyright (C) 2023 Sentry Software + * ჻჻჻჻჻჻ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱ + */ + +import static org.sentrysoftware.maven.metricshub.connector.producer.JsonNodeHelper.nonNullTextOrDefault; + +import com.fasterxml.jackson.databind.JsonNode; + +/** + * An abstract base class for SNMP (Simple Network Management Protocol) + * criteria. + * + *

    + * This class extends {@link AbstractCriterion} and provides common + * functionality for SNMP-related criteria. + *

    + * + * @see AbstractCriterion + */ +public abstract class AbstractSnmpCriterion extends AbstractCriterion { + + /** + * Parent constructor of the SNMP criterion classes with the provided criterion. + * + * @param criterion The {@link JsonNode} representing the SNMP criterion. + */ + protected AbstractSnmpCriterion(JsonNode criterion) { + super(criterion); + } + + /** + * Gets the OID from the current SNMP criterion, or {@code null} if not present. + * + * @return The OID from the criterion, or {@code null} if not present. + */ + public String getOid() { + return nonNullTextOrDefault(criterion.get("oid"), null); + } + + /** + * Get the type of the SNMP criterion. + * + * @return the type of the criterion as string. + */ + protected abstract String getType(); +} diff --git a/src/main/java/org/sentrysoftware/maven/metricshub/connector/producer/model/criteria/AbstractWqlCriterion.java b/src/main/java/org/sentrysoftware/maven/metricshub/connector/producer/model/criteria/AbstractWqlCriterion.java new file mode 100644 index 0000000..3231edb --- /dev/null +++ b/src/main/java/org/sentrysoftware/maven/metricshub/connector/producer/model/criteria/AbstractWqlCriterion.java @@ -0,0 +1,69 @@ +package org.sentrysoftware.maven.metricshub.connector.producer.model.criteria; + +/*- + * ╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲ + * MetricsHub Connector Maven Plugin + * ჻჻჻჻჻჻ + * Copyright (C) 2023 Sentry Software + * ჻჻჻჻჻჻ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱ + */ + +import static org.sentrysoftware.maven.metricshub.connector.producer.JsonNodeHelper.nonNullTextOrDefault; + +import com.fasterxml.jackson.databind.JsonNode; + +/** + * This class extends {@link AbstractCriterion} and provides common + * functionality for WQL-related criteria. + * + * @see AbstractCriterion + */ +public abstract class AbstractWqlCriterion extends AbstractCriterion { + + /** + * Parent constructor of the WQL criterion classes with the provided criterion. + * + * @param criterion The {@link JsonNode} representing the WQL criterion. + */ + protected AbstractWqlCriterion(JsonNode criterion) { + super(criterion); + } + + /** + * Gets the namespace from the current WQL criterion, or default value if not present. + * + * @param defaultValue The default value to return if the value is not present. + * @return The namespace from the criterion, or default value if not present. + */ + public String getNamespaceOrDefault(final String defaultValue) { + return nonNullTextOrDefault(criterion.get("namespace"), defaultValue); + } + + /** + * Gets the query from the current WQL criterion, or {@code null} if not present. + * + * @return The query from the criterion, or {@code null} if not present. + */ + public String getQuery() { + return nonNullTextOrDefault(criterion.get("query"), null); + } + + /** + * Get the type of the WQL criterion. + * + * @return the type of the criterion as string. + */ + protected abstract String getType(); +} diff --git a/src/main/java/org/sentrysoftware/maven/metricshub/connector/producer/model/criteria/CriterionFactory.java b/src/main/java/org/sentrysoftware/maven/metricshub/connector/producer/model/criteria/CriterionFactory.java new file mode 100644 index 0000000..c950473 --- /dev/null +++ b/src/main/java/org/sentrysoftware/maven/metricshub/connector/producer/model/criteria/CriterionFactory.java @@ -0,0 +1,198 @@ +package org.sentrysoftware.maven.metricshub.connector.producer.model.criteria; + +/*- + * ╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲ + * MetricsHub Connector Maven Plugin + * ჻჻჻჻჻჻ + * Copyright (C) 2023 Sentry Software + * ჻჻჻჻჻჻ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱ + */ + +import com.fasterxml.jackson.databind.JsonNode; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; +import java.util.Optional; +import java.util.function.Function; +import lombok.AccessLevel; +import lombok.NoArgsConstructor; +import org.sentrysoftware.maven.metricshub.connector.producer.JsonNodeHelper; + +/** + * Factory class for creating instances of {@link AbstractCriterion} based on connector criterion types. + */ +@NoArgsConstructor(access = AccessLevel.PRIVATE) +public class CriterionFactory { + + /** + * Mapping of connector source types to corresponding criterion creators. + *

    + * This static map associates connector criterion types (such as "http", "ipmi", "service", etc.) with their + * corresponding {@link CriterionFactory}. The map is used to identify the criterion based on + * its string type when processing connector criteria. + *

    + * + *

    Each entry in the map consists of a criterion type string and a corresponding function that creates + * the specific {@link AbstractCriterion} implementation for that type.

    + *

    + */ + private static final Map> CRITERION_FACTORY_MAP; + + static { + final Map> map = new HashMap<>(); + map.put("devicetype", CriterionFactory::newDeviceTypeCriterion); + map.put("http", CriterionFactory::newHttpCriterion); + map.put("ipmi", CriterionFactory::newIpmiCriterion); + map.put("oscommand", CriterionFactory::newOsCommandCriterion); + map.put("process", CriterionFactory::newProcessCriterion); + map.put("productrequirements", CriterionFactory::newProductRequirementsCriterion); + map.put("service", CriterionFactory::newServiceCriterion); + map.put("snmpget", CriterionFactory::newSnmpGetCriterion); + map.put("snmpgetnext", CriterionFactory::newSnmpGetNextCriterion); + map.put("wbem", CriterionFactory::newWbemCriterion); + map.put("wmi", CriterionFactory::newWmiCriterion); + + CRITERION_FACTORY_MAP = Collections.unmodifiableMap(map); + } + + /** + * Creates a new {@link DeviceTypeCriterion} instance based on the provided {@link JsonNode}. + * + * @param node The {@link JsonNode} containing criterion configuration. + * @return A new {@link DeviceTypeCriterion} instance. + */ + private static DeviceTypeCriterion newDeviceTypeCriterion(final JsonNode node) { + return new DeviceTypeCriterion(node); + } + + /** + * Creates a new {@link HttpCriterion} instance based on the provided {@link JsonNode}. + * + * @param node The {@link JsonNode} containing criterion configuration. + * @return A new {@link HttpCriterion} instance. + */ + private static HttpCriterion newHttpCriterion(final JsonNode node) { + return new HttpCriterion(node); + } + + /** + * Creates a new {@link IpmiCriterion} instance based on the provided {@link JsonNode}. + * + * @param node The {@link JsonNode} containing criterion configuration. + * @return A new {@link IpmiCriterion} instance. + */ + private static IpmiCriterion newIpmiCriterion(final JsonNode node) { + return new IpmiCriterion(node); + } + + /** + * Creates a new {@link OsCommandCriterion} instance based on the provided {@link JsonNode}. + * + * @param node The {@link JsonNode} containing criterion configuration. + * @return A new {@link OsCommandCriterion} instance. + */ + private static AbstractCriterion newOsCommandCriterion(final JsonNode node) { + return new OsCommandCriterion(node); + } + + /** + * Creates a new {@link ProcessCriterion} instance based on the provided {@link JsonNode}. + * + * @param node The {@link JsonNode} containing criterion configuration. + * @return A new {@link ProcessCriterion} instance. + */ + private static ProcessCriterion newProcessCriterion(final JsonNode node) { + return new ProcessCriterion(node); + } + + /** + * Creates a new {@link ProductRequirementsCriterion} instance based on the provided {@link JsonNode}. + * + * @param node The {@link JsonNode} containing criterion configuration. + * @return A new {@link ProductRequirementsCriterion} instance. + */ + private static ProductRequirementsCriterion newProductRequirementsCriterion(final JsonNode node) { + return new ProductRequirementsCriterion(node); + } + + /** + * Creates a new {@link ServiceCriterion} instance based on the provided {@link JsonNode}. + * + * @param node The {@link JsonNode} containing criterion configuration. + * @return A new {@link ServiceCriterion} instance. + */ + private static ServiceCriterion newServiceCriterion(final JsonNode node) { + return new ServiceCriterion(node); + } + + /** + * Creates a new {@link SnmpGetCriterion} instance based on the provided {@link JsonNode}. + * + * @param node The {@link JsonNode} containing criterion configuration. + * @return A new {@link SnmpGetCriterion} instance. + */ + private static AbstractSnmpCriterion newSnmpGetCriterion(final JsonNode node) { + return new SnmpGetCriterion(node); + } + + /** + * Creates a new {@link SnmpGetNextCriterion} instance based on the provided {@link JsonNode}. + * + * @param node The {@link JsonNode} containing criterion configuration. + * @return A new {@link SnmpGetNextCriterion} instance. + */ + private static SnmpGetNextCriterion newSnmpGetNextCriterion(final JsonNode node) { + return new SnmpGetNextCriterion(node); + } + + /** + * Creates a new {@link WbemCriterion} instance based on the provided {@link JsonNode}. + * + * @param node The {@link JsonNode} containing criterion configuration. + * @return A new {@link WbemCriterion} instance. + */ + private static AbstractWqlCriterion newWbemCriterion(final JsonNode node) { + return new WbemCriterion(node); + } + + /** + * Creates a new {@link WmiCriterion} instance based on the provided {@link JsonNode}. + * + * @param node The {@link JsonNode} containing criterion configuration. + * @return A new {@link WmiCriterion} instance. + */ + private static WmiCriterion newWmiCriterion(final JsonNode node) { + return new WmiCriterion(node); + } + + /** + * Creates an {@link Optional} instance containing an {@link AbstractCriterion} based on the provided {@link JsonNode} criterion type. + * + * @param criterionNode The {@link JsonNode} containing criterion configuration. + * @return An {@link Optional} containing the created {@link AbstractCriterion}, or an empty {@link Optional} if the criterion type is null or not recognized. + */ + public static Optional withJsonNode(final JsonNode criterionNode) { + final String type = JsonNodeHelper.nonNullTextOrDefault(criterionNode.get("type"), null); + if (type == null) { + return Optional.empty(); + } + + return Optional + .ofNullable(CRITERION_FACTORY_MAP.get(type.trim().toLowerCase())) + .map(creator -> creator.apply(criterionNode)) + .map(Optional::of) + .orElse(Optional.empty()); + } +} diff --git a/src/main/java/org/sentrysoftware/maven/metricshub/connector/producer/model/criteria/CriterionSinkProduceVisitor.java b/src/main/java/org/sentrysoftware/maven/metricshub/connector/producer/model/criteria/CriterionSinkProduceVisitor.java new file mode 100644 index 0000000..72009b0 --- /dev/null +++ b/src/main/java/org/sentrysoftware/maven/metricshub/connector/producer/model/criteria/CriterionSinkProduceVisitor.java @@ -0,0 +1,302 @@ +package org.sentrysoftware.maven.metricshub.connector.producer.model.criteria; + +/*- + * ╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲ + * MetricsHub Connector Maven Plugin + * ჻჻჻჻჻჻ + * Copyright (C) 2023 Sentry Software + * ჻჻჻჻჻჻ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱ + */ + +import java.util.List; +import lombok.AllArgsConstructor; +import org.apache.maven.doxia.sink.Sink; +import org.sentrysoftware.maven.metricshub.connector.producer.SinkHelper; + +/** + * A visitor implementation for producing a criterion sink using the provided + * criteria. + * + *

    + * This class implements the {@link ICriterionVisitor} interface, allowing it to + * visit different types of criteria and produce a criterion sink based on the + * visited criteria. + *

    + * + * @see ICriterionVisitor + */ +@AllArgsConstructor +public class CriterionSinkProduceVisitor implements ICriterionVisitor { + + private static final String CODE_FORMAT = "%s"; + + private final Sink sink; + + @Override + public void visit(DeviceTypeCriterion deviceTypeCriterion) { + // Operating System + sink.listItem(); + final List keptOsList = deviceTypeCriterion.getKeptOsList(); + if (keptOsList != null && !keptOsList.isEmpty()) { + sink.rawText("Operating System is " + String.join(" or ", keptOsList) + ""); + } + final List excludedOsList = deviceTypeCriterion.getExcludedOsList(); + if (excludedOsList != null && !excludedOsList.isEmpty()) { + sink.rawText("Operating System is NOT " + String.join(" and NOT ", excludedOsList) + ""); + } + sink.listItem_(); + } + + @Override + public void visit(HttpCriterion httpCriterion) { + // HTTP + sink.listItem(); + sink.text("The "); + sink.bold(); + sink.text("HTTP Request"); + sink.bold_(); + sink.text(" below to the managed host succeeds:"); + sink.list(); + sink.listItem(); + sink.rawText( + String.format( + "%s %s", + httpCriterion.getMethodOrDefault("GET"), + SinkHelper.replaceWithHtmlCode(httpCriterion.getUrl()) + ) + ); + sink.listItem_(); + + final String httpHeader = httpCriterion.getHeader(); + if (httpHeader != null) { + sink.listItem(); + sink.text("Request Header:"); + sink.lineBreak(); + sink.rawText(String.format(CODE_FORMAT, SinkHelper.replaceWithHtmlCode(httpHeader))); + sink.listItem_(); + } + + final String httpBody = httpCriterion.getBody(); + if (httpBody != null) { + sink.listItem(); + sink.text("Request Body:"); + sink.lineBreak(); + sink.rawText(String.format(CODE_FORMAT, SinkHelper.replaceWithHtmlCode(httpBody))); + sink.listItem_(); + } + + String expectedResult = httpCriterion.getExpectedResult(); + if (expectedResult != null) { + expectedResult = SinkHelper.replaceWithHtmlCode(expectedResult); + final String resultContent = httpCriterion.getResultContentOrDefault("body").toLowerCase(); + sink.listItem(); + if ("body".equals(resultContent)) { + sink.rawText(String.format("The response body contains: %s (regex)", expectedResult)); + } else if ("header".equals(resultContent)) { + sink.rawText(String.format("The response header contains: %s (regex)", expectedResult)); + } else if ("httpstatus".equals(resultContent)) { + sink.rawText(String.format("The HTTP response status code contains: %s (regex)", expectedResult)); + } else { + sink.rawText( + String.format("The entire response (header + body) contains: %s (regex)", expectedResult) + ); + } + sink.listItem_(); + } + sink.list_(); + sink.listItem_(); + } + + @Override + public void visit(IpmiCriterion ipmiCriterion) { + sink.listItem(); + sink.rawText("The IPMI-related WMI classes are populated on Windows,"); + sink.listItem_(); + sink.listItem(); + sink.bold(); + sink.text("OR"); + sink.bold_(); + sink.rawText(" ipmitool works properly with the local IPMI driver on Linux and Solaris, "); + sink.listItem_(); + sink.listItem(); + sink.bold(); + sink.text("OR"); + sink.bold_(); + sink.text(" IPMI-over-LAN has been enabled as an out-of-band interface"); + sink.listItem_(); + } + + @Override + public void visit(OsCommandCriterion osCommandCriterion) { + // Command Line + String commandLine = osCommandCriterion.getCommandLineOrDefault("N/A"); + + // Remove mentions to sudo + commandLine = commandLine.replaceAll("%\\{SUDO:[a-zA-Z\\d/\\-_]+\\}", ""); + + sink.listItem(); + sink.text("The command below succeeds on the "); + if (osCommandCriterion.isExecuteLocallyOrDefault(false)) { + sink.bold(); + sink.text("agent host"); + sink.bold_(); + } else { + sink.text("monitored host"); + } + sink.list(); + sink.listItem(); + sink.rawText(String.format("Command: %s", SinkHelper.replaceWithHtmlCode(commandLine))); + sink.listItem_(); + final String expectedResult = osCommandCriterion.getExpectedResult(); + if (expectedResult != null) { + sink.listItem(); + sink.rawText( + String.format("Output contains: %s (regex)", SinkHelper.replaceWithHtmlCode(expectedResult)) + ); + sink.listItem_(); + } + sink.list_(); + sink.listItem_(); + } + + @Override + public void visit(ProcessCriterion processCriterion) { + // Process + sink.listItem(); + sink.text("At least one process for which the command line matches with the regular expression below is running:"); + sink.lineBreak(); + sink.rawText(String.format(CODE_FORMAT, processCriterion.getCommandLine())); + sink.listItem_(); + } + + @Override + public void visit(ProductRequirementsCriterion productRequirementsCriterion) { + final String version = productRequirementsCriterion.getEngineVersion(); + if (version != null) { + sink.listItem(); + sink.text("The MetricsHub is in version "); + sink.bold(); + sink.text(version); + sink.bold_(); + sink.text(" or greater"); + sink.listItem_(); + } + } + + @Override + public void visit(ServiceCriterion serviceCriterion) { + // Windows Service + sink.listItem(); + sink.text("The Windows service "); + sink.bold(); + sink.text(serviceCriterion.getName()); + sink.bold_(); + sink.text(" is running"); + sink.listItem_(); + } + + @Override + public void visit(SnmpGetCriterion snmpGetCriterion) { + buildSnmpSink(snmpGetCriterion, " a value that matches with the ", " a non-empty value"); + } + + /** + * Builds the sink for SNMP-related information based on the provided SNMP criterion. + * + * @param abstractSnmpCriterion The SNMP criterion to extract information from. + * @param expectedResultStartMsg Start message to use if the expected result directive is present. + * @param nonExpectedResultEndMsg End message to use if the expected result directive is not present. + */ + private void buildSnmpSink( + final AbstractSnmpCriterion abstractSnmpCriterion, + final String expectedResultStartMsg, + final String nonExpectedResultEndMsg + ) { + final String type = abstractSnmpCriterion.getType(); + + // SNMP + sink.listItem(); + final String oid = abstractSnmpCriterion.getOid(); + // SNMP Get + sink.text("An "); + sink.bold(); + sink.text(String.format("SNMP %s", type)); + sink.bold_(); + sink.rawText(String.format(" on the OID %s", SinkHelper.replaceWithHtmlCode(oid))); + sink.text(" must return "); + final String expectedResult = abstractSnmpCriterion.getExpectedResult(); + if (expectedResult != null) { + sink.text(expectedResultStartMsg); + sink.rawText(String.format("%s (regular expression)", expectedResult)); + } else { + sink.text(nonExpectedResultEndMsg); + } + sink.listItem_(); + } + + @Override + public void visit(SnmpGetNextCriterion snmpGetNextCriterion) { + buildSnmpSink(snmpGetNextCriterion, " a value in the same subtree and contains ", " a value in the same subtree"); + } + + @Override + public void visit(WbemCriterion wbemCriterion) { + buildWqlSink(wbemCriterion); + } + + /** + * Builds the sink for WQL-related information based on the provided WQL criterion. + * + * @param abstractWqlCriterion The WQL criterion to extract information from. + */ + private void buildWqlSink(AbstractWqlCriterion abstractWqlCriterion) { + // WQL + sink.listItem(); + sink.text("The "); + sink.bold(); + sink.text(String.format("%s query", abstractWqlCriterion.getType())); + sink.bold_(); + sink.text(" below to the managed host succeeds:"); + sink.list(); + sink.listItem(); + sink.rawText( + String.format( + "Namespace: %s", + SinkHelper.replaceWithHtmlCode(abstractWqlCriterion.getNamespaceOrDefault("root/cimv2")) + ) + ); + sink.listItem_(); + sink.listItem(); + sink.rawText( + String.format("WQL Query: %s", SinkHelper.replaceWithHtmlCode(abstractWqlCriterion.getQuery())) + ); + sink.listItem_(); + final String expectedResult = abstractWqlCriterion.getExpectedResult(); + if (expectedResult != null) { + sink.listItem(); + sink.rawText( + String.format("Result contains: %s (regex)", SinkHelper.replaceWithHtmlCode(expectedResult)) + ); + sink.listItem_(); + } + sink.list_(); + sink.listItem_(); + } + + @Override + public void visit(WmiCriterion wmiCriterion) { + buildWqlSink(wmiCriterion); + } +} diff --git a/src/main/java/org/sentrysoftware/maven/metricshub/connector/producer/model/criteria/DeviceTypeCriterion.java b/src/main/java/org/sentrysoftware/maven/metricshub/connector/producer/model/criteria/DeviceTypeCriterion.java new file mode 100644 index 0000000..c3b5695 --- /dev/null +++ b/src/main/java/org/sentrysoftware/maven/metricshub/connector/producer/model/criteria/DeviceTypeCriterion.java @@ -0,0 +1,82 @@ +package org.sentrysoftware.maven.metricshub.connector.producer.model.criteria; + +/*- + * ╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲ + * MetricsHub Connector Maven Plugin + * ჻჻჻჻჻჻ + * Copyright (C) 2023 Sentry Software + * ჻჻჻჻჻჻ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱ + */ + +import static org.sentrysoftware.maven.metricshub.connector.producer.JsonNodeHelper.nodeToStringList; + +import com.fasterxml.jackson.databind.JsonNode; +import java.util.List; +import java.util.stream.Collectors; +import lombok.Builder; +import org.sentrysoftware.maven.metricshub.connector.producer.model.common.OsType; + +/** + * Represents a criterion for filtering based on kept and excluded device types. + * + * @see AbstractCriterion + */ +public class DeviceTypeCriterion extends AbstractCriterion { + + /** + * Constructs DeviceTypeCriterion with the specified JSON criterion. + * + * @param criterion The JSON criterion for device type. + */ + @Builder + public DeviceTypeCriterion(final JsonNode criterion) { + super(criterion); + } + + @Override + public void accept(ICriterionVisitor visitor) { + visitor.visit(this); + } + + /** + * Gets the list of operating systems to be kept. + * + * @return A list of operating systems to be kept. + * @see #getOsList(String) + */ + public List getKeptOsList() { + return getOsList("keep"); + } + + /** + * Gets the list of operating systems based on the specified criterion key. + * + * @param key The criterion key indicating the type of operating systems. + * @return A list of operating systems based on the specified criterion key. + */ + private List getOsList(final String key) { + return nodeToStringList(criterion.get(key)).stream().map(OsType::detectDisplayName).collect(Collectors.toList()); + } + + /** + * Gets the list of operating systems to be excluded. + * + * @return A list of operating systems to be excluded. + * @see #getOsList(String) + */ + public List getExcludedOsList() { + return getOsList("exclude"); + } +} diff --git a/src/main/java/org/sentrysoftware/maven/metricshub/connector/producer/model/criteria/HttpCriterion.java b/src/main/java/org/sentrysoftware/maven/metricshub/connector/producer/model/criteria/HttpCriterion.java new file mode 100644 index 0000000..9d9bf71 --- /dev/null +++ b/src/main/java/org/sentrysoftware/maven/metricshub/connector/producer/model/criteria/HttpCriterion.java @@ -0,0 +1,96 @@ +package org.sentrysoftware.maven.metricshub.connector.producer.model.criteria; + +/*- + * ╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲ + * MetricsHub Connector Maven Plugin + * ჻჻჻჻჻჻ + * Copyright (C) 2023 Sentry Software + * ჻჻჻჻჻჻ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱ + */ + +import static org.sentrysoftware.maven.metricshub.connector.producer.JsonNodeHelper.nonNullTextOrDefault; + +import com.fasterxml.jackson.databind.JsonNode; +import lombok.Builder; + +/** + * Represents a criterion for filtering based on an HTTP request. + * + * @see AbstractCriterion + */ +public class HttpCriterion extends AbstractCriterion { + + /** + * Constructs HttpCriterion with the specified JSON criterion. + * + * @param criterion The JSON criterion for HTTP. + */ + @Builder + public HttpCriterion(final JsonNode criterion) { + super(criterion); + } + + @Override + public void accept(ICriterionVisitor visitor) { + visitor.visit(this); + } + + /** + * Gets the method from the criterion, or a default value if not present. + * + * @param defaultValue The default value to return if the method is not present. + * @return The method from the criterion, or the default value if not present. + */ + public String getMethodOrDefault(final String defaultValue) { + return nonNullTextOrDefault(criterion.get("method"), defaultValue); + } + + /** + * Gets the URL from the criterion, or {@code null} if not present. + * + * @return The URL from the criterion, or {@code null} if not present. + */ + public String getUrl() { + return nonNullTextOrDefault(criterion.get("url"), null); + } + + /** + * Gets the header from the criterion, or {@code null} if not present. + * + * @return The header from the criterion, or {@code null} if not present. + */ + public String getHeader() { + return nonNullTextOrDefault(criterion.get("header"), null); + } + + /** + * Gets the body from the criterion, or {@code null} if not present. + * + * @return The body from the criterion, or {@code null} if not present. + */ + public String getBody() { + return nonNullTextOrDefault(criterion.get("body"), null); + } + + /** + * Gets the result content from the criterion, or a default value if not present. + * + * @param defaultValue The default value to return if the result content is not present. + * @return The result content from the criterion, or the default value if not present. + */ + public String getResultContentOrDefault(final String defaultValue) { + return nonNullTextOrDefault(criterion.get("resultContent"), defaultValue); + } +} diff --git a/src/main/java/org/sentrysoftware/maven/metricshub/connector/producer/model/criteria/ICriterionVisitor.java b/src/main/java/org/sentrysoftware/maven/metricshub/connector/producer/model/criteria/ICriterionVisitor.java new file mode 100644 index 0000000..b6574a7 --- /dev/null +++ b/src/main/java/org/sentrysoftware/maven/metricshub/connector/producer/model/criteria/ICriterionVisitor.java @@ -0,0 +1,107 @@ +package org.sentrysoftware.maven.metricshub.connector.producer.model.criteria; + +/*- + * ╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲ + * MetricsHub Connector Maven Plugin + * ჻჻჻჻჻჻ + * Copyright (C) 2023 Sentry Software + * ჻჻჻჻჻჻ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱ + */ + +/** + * Interface defining a visitor pattern for criterion types. + *

    + * This interface provides a set of methods for visiting different criterion types. Concrete implementations + * of this interface can be used to define specific business logic for each criterion type when needed. + *

    + */ +public interface ICriterionVisitor { + /** + * Visits the specified device type criterion. + * + * @param deviceTypeCriterion The device type criterion to visit. + */ + void visit(DeviceTypeCriterion deviceTypeCriterion); + + /** + * Visits the specified HTTP criterion. + * + * @param httpCriterion The HTTP criterion to visit. + */ + void visit(HttpCriterion httpCriterion); + + /** + * Visits the specified IPMI criterion. + * + * @param ipmiCriterion The IPMI criterion to visit. + */ + void visit(IpmiCriterion ipmiCriterion); + + /** + * Visits the specified OS Command criterion. + * + * @param osCommandCriterion The OS Command criterion to visit. + */ + void visit(OsCommandCriterion osCommandCriterion); + + /** + * Visits the specified Process criterion. + * + * @param processCriterion The Process criterion to visit. + */ + void visit(ProcessCriterion processCriterion); + + /** + * Visits the specified Product Requirements criterion. + * + * @param productRequirementsCriterion The Product Requirements criterion to visit. + */ + void visit(ProductRequirementsCriterion productRequirementsCriterion); + + /** + * Visits the specified Service criterion. + * + * @param serviceCriterion The Service criterion to visit. + */ + void visit(ServiceCriterion serviceCriterion); + + /** + * Visits the specified SNMP Get criterion. + * + * @param snmpGetCriterion The SNMP Get criterion to visit. + */ + void visit(SnmpGetCriterion snmpGetCriterion); + + /** + * Visits the specified SNMP GetNext criterion. + * + * @param snmpGetNextCriterion The SNMP GetNext criterion to visit. + */ + void visit(SnmpGetNextCriterion snmpGetNextCriterion); + + /** + * Visits the specified WBEM criterion. + * + * @param wbemCriterion The WBEM criterion to visit. + */ + void visit(WbemCriterion wbemCriterion); + + /** + * Visits the specified WMI criterion. + * + * @param wmiCriterion The WMI criterion to visit. + */ + void visit(WmiCriterion wmiCriterion); +} diff --git a/src/main/java/org/sentrysoftware/maven/metricshub/connector/producer/model/criteria/IpmiCriterion.java b/src/main/java/org/sentrysoftware/maven/metricshub/connector/producer/model/criteria/IpmiCriterion.java new file mode 100644 index 0000000..2b963b4 --- /dev/null +++ b/src/main/java/org/sentrysoftware/maven/metricshub/connector/producer/model/criteria/IpmiCriterion.java @@ -0,0 +1,47 @@ +package org.sentrysoftware.maven.metricshub.connector.producer.model.criteria; + +/*- + * ╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲ + * MetricsHub Connector Maven Plugin + * ჻჻჻჻჻჻ + * Copyright (C) 2023 Sentry Software + * ჻჻჻჻჻჻ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱ + */ + +import com.fasterxml.jackson.databind.JsonNode; +import lombok.Builder; + +/** + * Represents a criterion for filtering based on an IPMI request. + * + * @see AbstractCriterion + */ +public class IpmiCriterion extends AbstractCriterion { + + /** + * Constructs IpmiCriterion with the specified JSON criterion. + * + * @param criterion The JSON criterion for IPMI. + */ + @Builder + public IpmiCriterion(final JsonNode criterion) { + super(criterion); + } + + @Override + public void accept(ICriterionVisitor visitor) { + visitor.visit(this); + } +} diff --git a/src/main/java/org/sentrysoftware/maven/metricshub/connector/producer/model/criteria/OsCommandCriterion.java b/src/main/java/org/sentrysoftware/maven/metricshub/connector/producer/model/criteria/OsCommandCriterion.java new file mode 100644 index 0000000..6551ffc --- /dev/null +++ b/src/main/java/org/sentrysoftware/maven/metricshub/connector/producer/model/criteria/OsCommandCriterion.java @@ -0,0 +1,70 @@ +package org.sentrysoftware.maven.metricshub.connector.producer.model.criteria; + +/*- + * ╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲ + * MetricsHub Connector Maven Plugin + * ჻჻჻჻჻჻ + * Copyright (C) 2023 Sentry Software + * ჻჻჻჻჻჻ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱ + */ + +import static org.sentrysoftware.maven.metricshub.connector.producer.JsonNodeHelper.nonNullBooleanOrDefault; +import static org.sentrysoftware.maven.metricshub.connector.producer.JsonNodeHelper.nonNullTextOrDefault; + +import com.fasterxml.jackson.databind.JsonNode; +import lombok.Builder; + +/** + * Represents a criterion for filtering based on an OS Command. + * + * @see AbstractCriterion + */ +public class OsCommandCriterion extends AbstractCriterion { + + /** + * Constructs OsCommandCriterion with the specified JSON criterion. + * + * @param criterion The JSON criterion for OS Command. + */ + @Builder + public OsCommandCriterion(final JsonNode criterion) { + super(criterion); + } + + @Override + public void accept(ICriterionVisitor visitor) { + visitor.visit(this); + } + + /** + * Checks if the execution should be performed locally. + * + * @param defaultValue The default value to return if the executeLocally node is not present. + * @return {@code true} if execution should be performed locally, {@code false} otherwise. + */ + public boolean isExecuteLocallyOrDefault(final boolean defaultValue) { + return nonNullBooleanOrDefault(criterion.get("executeLocally"), defaultValue); + } + + /** + * Gets the command line from the criterion, or a default value if not present. + * + * @param defaultValue The default value to return if the command line is not present. + * @return The command line from the criterion, or the default value if not present. + */ + public String getCommandLineOrDefault(final String defaultValue) { + return nonNullTextOrDefault(criterion.get("commandLine"), defaultValue); + } +} diff --git a/src/main/java/org/sentrysoftware/maven/metricshub/connector/producer/model/criteria/ProcessCriterion.java b/src/main/java/org/sentrysoftware/maven/metricshub/connector/producer/model/criteria/ProcessCriterion.java new file mode 100644 index 0000000..db8ff41 --- /dev/null +++ b/src/main/java/org/sentrysoftware/maven/metricshub/connector/producer/model/criteria/ProcessCriterion.java @@ -0,0 +1,58 @@ +package org.sentrysoftware.maven.metricshub.connector.producer.model.criteria; + +/*- + * ╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲ + * MetricsHub Connector Maven Plugin + * ჻჻჻჻჻჻ + * Copyright (C) 2023 Sentry Software + * ჻჻჻჻჻჻ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱ + */ + +import static org.sentrysoftware.maven.metricshub.connector.producer.JsonNodeHelper.nonNullTextOrDefault; + +import com.fasterxml.jackson.databind.JsonNode; +import lombok.Builder; + +/** + * Represents a criterion for filtering based on a process presence. + * + * @see AbstractCriterion + */ +public class ProcessCriterion extends AbstractCriterion { + + /** + * Constructs ProcessCriterion with the specified JSON criterion. + * + * @param criterion The JSON criterion for process check. + */ + @Builder + public ProcessCriterion(final JsonNode criterion) { + super(criterion); + } + + @Override + public void accept(ICriterionVisitor visitor) { + visitor.visit(this); + } + + /** + * Gets the command line from the current process criterion, or {@code null} if not present. + * + * @return The command line from the criterion, or {@code null} if not present. + */ + public String getCommandLine() { + return nonNullTextOrDefault(criterion.get("commandLine"), null); + } +} diff --git a/src/main/java/org/sentrysoftware/maven/metricshub/connector/producer/model/criteria/ProductRequirementsCriterion.java b/src/main/java/org/sentrysoftware/maven/metricshub/connector/producer/model/criteria/ProductRequirementsCriterion.java new file mode 100644 index 0000000..539982a --- /dev/null +++ b/src/main/java/org/sentrysoftware/maven/metricshub/connector/producer/model/criteria/ProductRequirementsCriterion.java @@ -0,0 +1,58 @@ +package org.sentrysoftware.maven.metricshub.connector.producer.model.criteria; + +/*- + * ╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲ + * MetricsHub Connector Maven Plugin + * ჻჻჻჻჻჻ + * Copyright (C) 2023 Sentry Software + * ჻჻჻჻჻჻ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱ + */ + +import static org.sentrysoftware.maven.metricshub.connector.producer.JsonNodeHelper.nonNullTextOrDefault; + +import com.fasterxml.jackson.databind.JsonNode; +import lombok.Builder; + +/** + * Represents a criterion for filtering based on the engine version. + * + * @see AbstractCriterion + */ +public class ProductRequirementsCriterion extends AbstractCriterion { + + /** + * Constructs ProductRequirementsCriterion with the specified JSON criterion. + * + * @param criterion The JSON criterion for product requirements check. + */ + @Builder + public ProductRequirementsCriterion(final JsonNode criterion) { + super(criterion); + } + + @Override + public void accept(ICriterionVisitor visitor) { + visitor.visit(this); + } + + /** + * Gets the engine version from the current process criterion, or {@code null} if not present. + * + * @return The engine version from the criterion, or {@code null} if not present. + */ + public String getEngineVersion() { + return nonNullTextOrDefault(criterion.get("engineVersion"), null); + } +} diff --git a/src/main/java/org/sentrysoftware/maven/metricshub/connector/producer/model/criteria/ServiceCriterion.java b/src/main/java/org/sentrysoftware/maven/metricshub/connector/producer/model/criteria/ServiceCriterion.java new file mode 100644 index 0000000..f0a194e --- /dev/null +++ b/src/main/java/org/sentrysoftware/maven/metricshub/connector/producer/model/criteria/ServiceCriterion.java @@ -0,0 +1,58 @@ +package org.sentrysoftware.maven.metricshub.connector.producer.model.criteria; + +/*- + * ╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲ + * MetricsHub Connector Maven Plugin + * ჻჻჻჻჻჻ + * Copyright (C) 2023 Sentry Software + * ჻჻჻჻჻჻ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱ + */ + +import static org.sentrysoftware.maven.metricshub.connector.producer.JsonNodeHelper.nonNullTextOrDefault; + +import com.fasterxml.jackson.databind.JsonNode; +import lombok.Builder; + +/** + * Represents a criterion for filtering based on a service presence. + * + * @see AbstractCriterion + */ +public class ServiceCriterion extends AbstractCriterion { + + /** + * Constructs ServiceCriterion with the specified JSON criterion. + * + * @param criterion The JSON criterion for service check. + */ + @Builder + public ServiceCriterion(final JsonNode criterion) { + super(criterion); + } + + @Override + public void accept(ICriterionVisitor visitor) { + visitor.visit(this); + } + + /** + * Gets the name from the current service criterion, or {@code null} if not present. + * + * @return The service name from the criterion, or {@code null} if not present. + */ + public String getName() { + return nonNullTextOrDefault(criterion.get("name"), null); + } +} diff --git a/src/main/java/org/sentrysoftware/maven/metricshub/connector/producer/model/criteria/SnmpGetCriterion.java b/src/main/java/org/sentrysoftware/maven/metricshub/connector/producer/model/criteria/SnmpGetCriterion.java new file mode 100644 index 0000000..804c98a --- /dev/null +++ b/src/main/java/org/sentrysoftware/maven/metricshub/connector/producer/model/criteria/SnmpGetCriterion.java @@ -0,0 +1,54 @@ +package org.sentrysoftware.maven.metricshub.connector.producer.model.criteria; + +/*- + * ╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲ + * MetricsHub Connector Maven Plugin + * ჻჻჻჻჻჻ + * Copyright (C) 2023 Sentry Software + * ჻჻჻჻჻჻ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱ + */ + +import com.fasterxml.jackson.databind.JsonNode; +import lombok.Builder; + +/** + * Represents a criterion for filtering based on an SNMP GET request. + * + * @see AbstractCriterion + */ +public class SnmpGetCriterion extends AbstractSnmpCriterion { + + private static final String SNMP_GET_TYPE = "Get"; + + /** + * Constructs SnmpGetCriterion with the specified JSON criterion. + * + * @param criterion The JSON criterion for SNMP GET check. + */ + @Builder + public SnmpGetCriterion(final JsonNode criterion) { + super(criterion); + } + + @Override + public void accept(ICriterionVisitor visitor) { + visitor.visit(this); + } + + @Override + protected String getType() { + return SNMP_GET_TYPE; + } +} diff --git a/src/main/java/org/sentrysoftware/maven/metricshub/connector/producer/model/criteria/SnmpGetNextCriterion.java b/src/main/java/org/sentrysoftware/maven/metricshub/connector/producer/model/criteria/SnmpGetNextCriterion.java new file mode 100644 index 0000000..8553d58 --- /dev/null +++ b/src/main/java/org/sentrysoftware/maven/metricshub/connector/producer/model/criteria/SnmpGetNextCriterion.java @@ -0,0 +1,54 @@ +package org.sentrysoftware.maven.metricshub.connector.producer.model.criteria; + +/*- + * ╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲ + * MetricsHub Connector Maven Plugin + * ჻჻჻჻჻჻ + * Copyright (C) 2023 Sentry Software + * ჻჻჻჻჻჻ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱ + */ + +import com.fasterxml.jackson.databind.JsonNode; +import lombok.Builder; + +/** + * Represents a criterion for filtering based on an SNMP GET-NEXT request. + * + * @see AbstractCriterion + */ +public class SnmpGetNextCriterion extends AbstractSnmpCriterion { + + private static final String GET_NEXT_TYPE = "Get-Next"; + + /** + * Constructs SnmpGetNextCriterion with the specified JSON criterion. + * + * @param criterion The JSON criterion for SNMP GET-NEXT check. + */ + @Builder + public SnmpGetNextCriterion(final JsonNode criterion) { + super(criterion); + } + + @Override + public void accept(ICriterionVisitor visitor) { + visitor.visit(this); + } + + @Override + protected String getType() { + return GET_NEXT_TYPE; + } +} diff --git a/src/main/java/org/sentrysoftware/maven/metricshub/connector/producer/model/criteria/WbemCriterion.java b/src/main/java/org/sentrysoftware/maven/metricshub/connector/producer/model/criteria/WbemCriterion.java new file mode 100644 index 0000000..ee15cef --- /dev/null +++ b/src/main/java/org/sentrysoftware/maven/metricshub/connector/producer/model/criteria/WbemCriterion.java @@ -0,0 +1,54 @@ +package org.sentrysoftware.maven.metricshub.connector.producer.model.criteria; + +/*- + * ╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲ + * MetricsHub Connector Maven Plugin + * ჻჻჻჻჻჻ + * Copyright (C) 2023 Sentry Software + * ჻჻჻჻჻჻ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱ + */ + +import com.fasterxml.jackson.databind.JsonNode; +import lombok.Builder; + +/** + * Represents a criterion for filtering based on a WBEM request. + * + * @see AbstractCriterion + */ +public class WbemCriterion extends AbstractWqlCriterion { + + private static final String WBEM_TYPE = "WBEM"; + + /** + * Constructs WbemCriterion with the specified JSON criterion. + * + * @param criterion The JSON criterion for WBEM check. + */ + @Builder + public WbemCriterion(final JsonNode criterion) { + super(criterion); + } + + @Override + public void accept(ICriterionVisitor visitor) { + visitor.visit(this); + } + + @Override + protected String getType() { + return WBEM_TYPE; + } +} diff --git a/src/main/java/org/sentrysoftware/maven/metricshub/connector/producer/model/criteria/WmiCriterion.java b/src/main/java/org/sentrysoftware/maven/metricshub/connector/producer/model/criteria/WmiCriterion.java new file mode 100644 index 0000000..5c66408 --- /dev/null +++ b/src/main/java/org/sentrysoftware/maven/metricshub/connector/producer/model/criteria/WmiCriterion.java @@ -0,0 +1,54 @@ +package org.sentrysoftware.maven.metricshub.connector.producer.model.criteria; + +/*- + * ╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲ + * MetricsHub Connector Maven Plugin + * ჻჻჻჻჻჻ + * Copyright (C) 2023 Sentry Software + * ჻჻჻჻჻჻ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱ + */ + +import com.fasterxml.jackson.databind.JsonNode; +import lombok.Builder; + +/** + * Represents a criterion for filtering based on a WMI request. + * + * @see AbstractCriterion + */ +public class WmiCriterion extends AbstractWqlCriterion { + + private static final String WMI_TYPE = "WMI"; + + /** + * Constructs WmiCriterion with the specified JSON criterion. + * + * @param criterion The JSON criterion for WMI check. + */ + @Builder + public WmiCriterion(final JsonNode criterion) { + super(criterion); + } + + @Override + public void accept(ICriterionVisitor visitor) { + visitor.visit(this); + } + + @Override + protected String getType() { + return WMI_TYPE; + } +} diff --git a/src/main/java/org/sentrysoftware/maven/metricshub/connector/producer/model/criteria/package-info.java b/src/main/java/org/sentrysoftware/maven/metricshub/connector/producer/model/criteria/package-info.java new file mode 100644 index 0000000..06e5c60 --- /dev/null +++ b/src/main/java/org/sentrysoftware/maven/metricshub/connector/producer/model/criteria/package-info.java @@ -0,0 +1,26 @@ +/** + * The part of library defining detection criteria and visitor patterns for criterion types.
    + * + * See {@link org.sentrysoftware.maven.metricshub.connector.producer.model.criteria.AbstractCriterion}.
    + * See {@link org.sentrysoftware.maven.metricshub.connector.producer.model.criteria.ICriterionVisitor}. + */ +package org.sentrysoftware.maven.metricshub.connector.producer.model.criteria; +/*- + * ╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲ + * MetricsHub Connector Maven Plugin + * ჻჻჻჻჻჻ + * Copyright (C) 2023 Sentry Software + * ჻჻჻჻჻჻ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱ + */ diff --git a/src/main/java/org/sentrysoftware/maven/metricshub/connector/producer/model/platform/Platform.java b/src/main/java/org/sentrysoftware/maven/metricshub/connector/producer/model/platform/Platform.java new file mode 100644 index 0000000..e6a27c0 --- /dev/null +++ b/src/main/java/org/sentrysoftware/maven/metricshub/connector/producer/model/platform/Platform.java @@ -0,0 +1,187 @@ +package org.sentrysoftware.maven.metricshub.connector.producer.model.platform; + +/*- + * ╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲ + * MetricsHub Connector Maven Plugin + * ჻჻჻჻჻჻ + * Copyright (C) 2023 Sentry Software + * ჻჻჻჻჻჻ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱ + */ + +import java.util.LinkedHashMap; +import java.util.LinkedHashSet; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Set; +import java.util.stream.Collectors; +import lombok.Getter; + +/** + * Platform to MetricsHub Connector implementation + * It is made of { os ; platform ; technology }. + */ +public class Platform implements Comparable { + + /** + * The supported platform name (as specified by the connector `platforms` property) + */ + @Getter + private final String name; + + /** + * The operating system for this platform (Linux, Microsoft Windows, etc.) + */ + @Getter + private final String os; + + /** + * The type of connection we're using to support this platform (SNMP, WBEM, SSH, etc.) + */ + @Getter + private final String technology; + + /** + * Connector ID to display name mapping + */ + private final Map connectors = new LinkedHashMap<>(); + + /** + * All the prerequisites for this platform + */ + private final Set prerequisites = new LinkedHashSet<>(); + + private final int hashCode; + + /** + * Constructs a new instance of the platform + * + * @param name The supported platform name (as specified by the connector `platforms` property) + * @param os The operating system for this platform (Linux, Microsoft Windows, etc.) + * @param technology The type of connection we're using to support this platform (SNMP, WBEM, SSH, etc.) + */ + public Platform(String name, String os, String technology) { + this.name = name; + this.os = os; + this.technology = technology; + + // The hash code needs to be computed only once to avoid calling String.toLowerCase many times. + this.hashCode = lowerCaseHashCode(name, os, technology); + } + + /** + * Calculates the hashCode code of the concatenated lowercased strings. + * + * @param elements The elements to be concatenated and hashed. + * @return The hashCode code of the concatenated lowercased strings. + */ + private int lowerCaseHashCode(final String... elements) { + int result = 1; + + for (String element : elements) { + result = 31 * result + (element != null ? element.toLowerCase().hashCode() : 0); + } + + return result; + } + + /** + * We're sorting Platforms by putting entries that starts with "Any " at the end of the list + * + * @param other the {@link Platform} to be compared. + * @see java.lang.Comparable#compareTo(java.lang.Object) + */ + @Override + public int compareTo(Platform other) { + if (other == null) { + return 1; + } + + int comparison = this.os.compareToIgnoreCase(other.os); + if (comparison == 0) { + // Special case for platforms that start with "Any " + if (this.name.toLowerCase().startsWith("any ")) { + if (other.name.toLowerCase().startsWith("any ")) { + comparison = this.name.compareToIgnoreCase(other.name); + } else { + comparison = 1; + } + } else if (other.name.toLowerCase().startsWith("any ")) { + comparison = -1; + } else { + comparison = this.name.compareToIgnoreCase(other.name); + } + } + + return comparison; + } + + /** + * Adds information about a connector, including its identifier, display name, and a prerequisite. + * + * @param connectorId The identifier of the connector. + * @param displayName The display name of the connector. + * @param reliesOn Considered as the technical prerequisites for this connector. + */ + public void addConnectorInformation(final String connectorId, final String displayName, final String reliesOn) { + connectors.put(connectorId, displayName); + prerequisites.add(reliesOn); + } + + @Override + public int hashCode() { + return hashCode; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + Platform other = (Platform) obj; + // @formatter:off + return + this.name.equalsIgnoreCase(other.name) && + this.os.equalsIgnoreCase(other.os) && + this.technology.equalsIgnoreCase(other.technology); + // @formatter:on + } + + /** + * Gets the connectors associated with the platform. + * + * @return A {@code Map} containing connector names as keys and their associated display names as values. + */ + public Map getConnectors() { + return connectors + .entrySet() + .stream() + .collect(Collectors.toMap(Entry::getKey, Entry::getValue, (a, b) -> b, LinkedHashMap::new)); + } + + /** + * Gets the prerequisites associated with the platform. + * + * @return A {@code Set} containing prerequisites. + */ + public Set getPrerequisites() { + return prerequisites.stream().collect(Collectors.toCollection(LinkedHashSet::new)); + } +} diff --git a/src/main/java/org/sentrysoftware/maven/metricshub/connector/producer/model/platform/package-info.java b/src/main/java/org/sentrysoftware/maven/metricshub/connector/producer/model/platform/package-info.java new file mode 100644 index 0000000..90f3962 --- /dev/null +++ b/src/main/java/org/sentrysoftware/maven/metricshub/connector/producer/model/platform/package-info.java @@ -0,0 +1,23 @@ +/** + * The part of library that groups platform data model. + */ +package org.sentrysoftware.maven.metricshub.connector.producer.model.platform; +/*- + * ╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲ + * MetricsHub Connector Maven Plugin + * ჻჻჻჻჻჻ + * Copyright (C) 2023 Sentry Software + * ჻჻჻჻჻჻ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱ + */ diff --git a/src/main/java/org/sentrysoftware/maven/metricshub/connector/producer/package-info.java b/src/main/java/org/sentrysoftware/maven/metricshub/connector/producer/package-info.java new file mode 100644 index 0000000..0884849 --- /dev/null +++ b/src/main/java/org/sentrysoftware/maven/metricshub/connector/producer/package-info.java @@ -0,0 +1,26 @@ +/** + * The part of library responsible for producing documentation output.
    + * + * See {@link org.sentrysoftware.maven.metricshub.connector.producer.MainPageReferenceProducer}.
    + * See {@link org.sentrysoftware.maven.metricshub.connector.producer.ConnectorPageReferenceProducer}. + */ +package org.sentrysoftware.maven.metricshub.connector.producer; +/*- + * ╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲ + * MetricsHub Connector Maven Plugin + * ჻჻჻჻჻჻ + * Copyright (C) 2023 Sentry Software + * ჻჻჻჻჻჻ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱ + */ diff --git a/src/site/markdown/index.md b/src/site/markdown/index.md index 149e5aa..722f195 100644 --- a/src/site/markdown/index.md +++ b/src/site/markdown/index.md @@ -1,3 +1,85 @@ -# Documentation +# MetricsHub Connector Maven Plugin -This is the documentation. +This is a Maven Report plugin, which is invoked by Maven's site plugin in the `site` lifecycle. + +This plugin is designed to be used with the [MetricsHub Community Connectors](https://github.com/sentrysoftware/metricshub-community-connectors). + +It reads the connector files from a source directory (E.g. `./src/main/connector`), parses the `.yaml` files and produces the corresponding Reference Guide, as a set of HTML documents (through Doxia's Sink API), which is integrated into the project's documentation. + +## How to Use + +Add the plugin to the `` element in the `pom.xml` file of the **MetricsHub Community Connectors** project: + +```xml + + ... + + + + + ${project.groupId} + ${project.artifactId} + ${project.version} + + + + ... + +``` + +To ensure that the **MetricsHub Community Connectors** project's documentation incorporates the [Sentry Maven Skin](https://sentrysoftware.github.io/sentry-maven-skin/), make sure to include a `./src/site/site.xml` file with the following configuration: + +```xml + + + + org.sentrysoftware.maven + sentry-maven-skin + 6.2.00 + + + + + + + + + + + + + + + +``` + +## Help + +As any Maven plugin, the online help provides you with all the necessary information about it: + +```sh +mvn ${project.groupId}:${project.artifactId}:help +``` + +## How to Build + +You can build the plugin with the usual command: +```sh +$ mvn verify +``` + +## How to Run + +In order to run your plugin, you will need to setup the **MetricsHub Community Connectors** project as described above. + +First, from the root directory of your `${project.artifactId}` project, install the plugin in your local repository with: +```sh +$ mvn install +``` + +Then, from the root directory of the **MetricsHub Community Connectors** project, generate the project's documentation with: +```sh +$ mvn clean site -o +``` + +The `-o` flag is to make sure Maven uses the version of the `${project.artifactId}` that you just installed in your local repository (and not the one from a remote repository). diff --git a/src/site/resources/images/sentry-logo-179x75px.png b/src/site/resources/images/sentry-logo-179x75px.png index bdd5fa5f614ede0157018fd014fc6d3badb435de..42e53f78134d10c1e5f527a9fb12e081e3066d05 100644 GIT binary patch delta 4290 zcmV;z5IyhlF5w}N7YaWJ1^@s6t7iF7ks&mH5OzsKK~#8N?VSnq9n&4h^Z#3d*kdb2 z2t_xw)KY3s(Nb;2PKqvCL8a=bql*;B9;vFODme~{YDC0VYOgI4OAuR>)Rv?v2_lI| z($DwKZzl8Jn|U+y-kbMwn)^BDd+xrxdH3D9bC-ENJRXn7&jE;`n-bKt!ce&0u*GcS*Sr#xtI%hA||qEB6`Yyi@VV=PMt?sW)yz`|#t zQ?dFU+fxoSu1#rV&#A#R19@CvSvqiUgWj>=I1k|e8H?uev<4d0PBgCRK^tov@_7bh zDa$r&$TNb_S_pd+eZiu8Jk8B!(3mt!F}ka(OVe#lLHpdZySw{t*~#~_7~{x)cMZAI zAk8$4JxKR#;L{o?U%mc!&==8)=mYBVB<4uW8JLMspH0y==B&q5>U@{kBPjPOjOmnd zGMXRwrbAhp>O2^$hFx>W^F|jCgFL^6u~Fd4&7I`+AG15iXI+e)(QM}tpqZw*NK>hA zQsS?h+9$UhnP)6KI1Xm4@)NQ z(JsKiJr!MshM-r>+ktyB+Q3z+1(4UiDDiXQuOZJa7PSV#2L>_gL%fG9Nd@>nW#MO= zS6zs@G;v*BF)JEh2>yRoco9gmttF4ZZ(j>egm5tqZDL6x2-XAqxFDu~iz|ELa}Qx( zLUS#i*wcc_fpXTOkfgC}Ly*^C%UU01OQeMq2lkgx)I($MiK-j>(-_NHl!hS>{XnCs zMC_Vb(L-l6GlO*|+T4;P|)O-mM`JW(_`fc3 z?>3A+XeG1nT4rM=Xh*`1s*-;E z4(yjyg&mDnx8xD{MU5Hz1k~SjF#02!W5Ehf{4Vgn#G)vU8vP1?7UaNt7yMkuf$SrQKW@Ao*xVI{e!2Uh+t_$(vjrl?Ij==9& z3s(4KjObxL1iw{(EX;xZd<#<8lDMA?LR{EyEhslNUr0SDV`Yqr>9bZqzVYSl>1Kv( zXJ!QP^^2_-R`5WfF?TbohI-KJ8CZLKgevO6L&!TAKS3K}Kat`xAJCeGGE|=te6fkc znnelf`d70GwUe4*A93EtI4N*Bj_0$vhf}_{{)BIB_Sdj~pDTM2iZF^=<*sLT^#HmxT}qmoy5qMwaSt$OQ8Y{Ou`J}%n|uzEJr0fd+UlqLvY`A+ zWl|S4j5}~^bJijY^^5n!^Gr4Hd9Ek!{btvX^#a`L%+8V@cFl~avxv`{v=$oCvM9Pm zv=Z_iWyPp}96jK@Y(Wm(Gtjl@Pv~v)PK0nhDK|9bNZR2+E-oL9Sx2J%aOQfrByN0| z@en#CMTc3E3hU>98H-ZC1H!b(77pp=p&Miu z*!8T^)Rb{16w*H^uJw@jCm7li>cE}r$vF+u=b7t&t%0Pgk@nRfmxbUr4gDU~z1uW)^~Sr=hP?wx)-o+a!Ly(Fs_`a+!wpVp$zx-5CaUPEaf_Pqw7@?;%+RfK$0QLJ ze@2MFzoKV0Z~3o^UNC>@=_54?Q2`?($c|Dtl_KVrmfgdW263f?WQ( zO?Rp>KZYJRr_ip(!gmYu*epoy@_gF78z%2CV@oqjg8{q>GWiO z-B6vms4H@9qaKF5=5y=22kUP5e-QZg&>oDo?%JasI#RY5<-QmA^f(QB#lG)0qCFXh zP<}Xlxij_)&E7HcHs+f3_7XXX2IZDh`2rW9_h!#QU?uxqcrDV{;fFy>Z0-PG1SGK*Q7efas39!-YGI0 zGj-KPE4u5dVYn!|U0uV4>1A&1j5|*Df)wgG`J9@xKL@Fi>a1cls*mfji93&YQ)F*9 zG@{2a4x|oqslyT|Z->-!DElUiD+8C~QtB10=C+$W7h(z2@`aLmaFI5~h*p4qaNo>D z`iD7lL7G*~ninlh%rW}_eoRN<)@N7aT^k%tOwUacg{i7<2iHp!hSQKFWkP_R`UL;-Iv;Cj^Uz2!wn3L5p%zSfT)Qlz|)|C zyo)hRcFNO|xE3s4rVjUGzK-924-|%74d`^MQ!0f25Mxq`)T1>8^}QP7m((ZsH7Nr= zr>5f$r5dx$t?jti;colUw!DqGX2W&aa8YzIwAAUQ*l>@`j5+OkT$_T*Yc+j`qY=3K0Q8WYlK67BwP zE0n8VXB#e-8nWD{W8~URMcg5zi*IVl1?jYaqthFDn9;E;_3-cU`SmW3KVXi-OoU!s zrzu`ik zrcFS4Ko&2O<~L?{ke}Lr$(dNg6}O|Q;0!NX>XTxb1g^qSN!GQReI{2!33G4rd}J2Be+ z%co48hIU7ku8o3p5e{vn1yCiIstXe+mThk=WF^T zep%#g%$j~F_qcZqF0}T#o=>?4BX^6(y#>auvghFTFkKNSL#u!zFyBS*V9#}Og?Ktq ztaFi?-kT^B_f@EW?gJU|^w6Xi+Ki7$C!8zJ#)aU&T`XR;Y8JMXKmIo%osfyZt-}YW zn$v;bn&>H{R|+IT8m%^JsxsFPoxILQI(e_%hT6V59Hp*V5Vo`8S822bw+=Fmb~v>f z^46DsH@Y0>k(d>e?znY6@7W-BeTXL--Ip;8-HKL6o1+(h(1{kD2;sY<_bFNjao0jR z%eD#97pEUY>WgZKt4&N(&|ydoRoWK+#Cd)E^mfk)k1e-kBR>vG={X%LoDvY zkv2}n{j^Z@lsOY2{0OAI=b8JJ1#z{K`xm78?S>vkf4ATo2-^qgpsTKFsAwl;%OF#o z0C|fK(sIauSNkCndqBd}V>}$h%!as6Skgj}RK3`%NYio-{M0y8dm+MBCcfV3QM2L4 z@;eWyXG&|FVPj6*5?T+^T#hp5n2Do>hA8=OhVypjd@^5pi?H_4O{_y zAT1&P80&b#cMN>%LwpS_&I??{uwjYzJ!oT-cExq$HyLSzd?Lj8HpUF%Tpsu=#PZXD z1HG;wX|G2deb$vtdbL4AUuF_~Z#sy3A9AlZpt0!Su7+$wk=vJzpZ1;XM4hjdI|=Hf z)mR;WShyH%guCz_4ASTz@_$jiMYtu1r#oY)+}je0J_KAzLAp3ChkSod#lMQFVTN8e zpk?1HxWIK^(N%?B>6LlPAq~>%G>OK`dauM2)Z+-udJRiMh_R$g+#^9l`Viwaw3+ir zXC&q5wlC?Odx>M_CK!&wgT8tr(|y!gCwcaNlLz*G7|&qV+nuP*URU#(w6b~~$~%vL z0N3$xGzPmmnt^H8%g{oK;_7v1(g>7`LXFwJoRhfNg&(f6-~(=l^dW|04nT!A3Q;BP zWE+GMPX*_JwA1;F>cRS8*G0UHeCw^~>28(!bWd6hFVque%>?zMymd`_l|i^~x>e|Z z;eA8uvljVlGrk(Kjwk5^(faty?|PI7ar7Xoaq|1v^}4J#(f-)i#>_&qQR?m} zuB1ua+l;?%=V}NwFHNb2^0WiZ$-G*B2A@aE;_ChN0-qfy<6flR@o$(qFR33>y&h1T zrrtp6!PUcSjIYB215p-wg@ewIoz!2hUMnMm&QKQ9wz6H|B z^VE$~gy|I%nr+(Kyd1doYA(HcB6Uuk@aO>$QxA@M4RPxBSHjx!02qf~x)vmKCan7H zLv4pTyoIqpzu>0+$j8!aEI5h_s`tTZ(~PF&QX33u^dO_#uf9+<+_TU?q}^K3hRhl*XD3^7io0Bp#tis zalJZ+_%+jWj2>XihYIRdrBkRxTcON}sGVmMJ&OZ0str)Z8!XF&vh;6OMUO>!Jo%t; z^`xDhgIV~0|MEiKdgq*frUpB4jLWmQL1X(o8jT9Syd@2?(%X*oWUL8wkEgxR@bq^M z+H9juI1NEs{a?w&$p4w8CzF=#0Z4;}sF@DU4(P8Q^ma})%w9aq#LA5QjU-Y=?($u?r!M=lsg*bq`MF425BiN3E>D);()K8 z|KR&(-tN4a{dMQkLJf6gy??#wA2o5Y|5fS5)`Wiz-A7MN39WXT z{^*|oI4No=qMn_ND-R1s(I_<@ZOuSHjIB^p$^ zVRbi6rUm1!LLJE68h^r9@J1WBsCMoi$rZGngwLzVfWev#f_y5K~lVp3w z$Gbf{Z-!V^0PW0`L*ib!fk`a~N5}8#`1}QmY-B=UPCf3qKOVy#O^Jx?W`%9!+C%ym zWUjm!{v<-lBJ?MHo}LfplC9Nne+`>)wT2B@@%>5uxRbUcpK?;s5ODGhfl$lp&`Fyg za8RZFP3(8mwqv^EnHU5JQbxgV*nyWsz)J##J`Hm&ehMqlV|pU8rz_0zryVPuN0{~q z5m7evf$a<)tA?tNC2y}5TlEAc&nD=P=B@`&^zzLh`USwN5VtY~kUf&vkV?SR$8Y4Wpzs zbZd_M#kdsvlG^D)r9X%#Hl*#%^SZiMq&M<-+{6!>vs&0(2G~=Pwt?n;>n2Z4L>q3c za?a#;AH2OR)gL!m=2ZSha7LR5-5O*$5jMoXaBsXU<9%_=f%=dfp`jUtC$vHJ>Vh8jWOyMQVr1UG<-gqI5|a^(X4Y-3c>7hk~0o1!_gV7O(k+bMlJ)xprXH%`(V{1!x{OyjNUDLIbu#AY~G)yBi2Jc$3p$ z<_m-+@qK)@l$Mm|Ejw0;`H9XyUO&@cseF3UPZ*S0f65s27}L#pPewQQB!kIwCF|nA z`%Zt4OozkpDvC;@A`j` zWW)sDstxJXeDQfhe?r5%83vju=O@wh>3UXp%9Qj3iti)854B3=p$pVgo&aGw?+V~- zvFJoF7uR`b5Di{#>roEXeq-5(5f;QKkoIc7vS}vtBgDM4qfc)4rlyd&vJ0zJanp#J;bz=h6}s~U`F|F!5ml0-K{(t!FmBM*kmCT>mS2lr*V4>-`OIMio|_h_R^MrCJ@Q+&nOXgdZ5P#uXC?EAq@+MNy)x}L|9aWaLs z0V<+~BIFQi1K+pL6C59der>#rd41d9h$oV$iZu9Y-%7P~^Z5J#sw1T**SY#xr>-#= z=wPc5_WBn!^~sBZzD+ipy|&D8Yh+U<{iSpp4TFH zd8)Y+XdW1h;_r+2m|(Jha&SVRF?vcSC3J&=)n)6>tEw{0;V(l~2Okg_!&+EsNl2=r z?;}GKl#`X-0!sE|2Rj&BY^aJvN$vSpru|{ho(bfWc!QNuB0{1uspms??&(s#g{U2x z`L}>#fLxlK?X&J5Umc>a*A5j#@fn8tC0-}g9UhwgXd^W&P`R0fJ=kWK#o?;p1TB`2eP_V2XGk^&B6%ul@Z0s=ox z5W_cl$>v&VA?%mqO>Ij)IRT0GF9zn)hE>S3HJBCp+tPg4Njl?}cv1tj=Fy)>r=n)4 z3K96q^M*W&E>|fVm@Y`og0W;0b1^dLt(dLeecA2B;#XJ_Z$H_^%KSi7yJEe3f0Gx> zG`aE7%*1bvwJcxSaUyF&(ADT-u=(%z>aQ|_=ceJiEQbd#T-@n`+(pWlEr%}!o4q;G z^2tO=+@v=7qqv+6c4Fp!?B%_TI-qaX6N!LaS=LRcQ#wQ!OPAIJ@pBMQCC&_QE1d5= z52;CW^3bK3TbKgi>8G^*?B*1o>f~&volCN(CZWgBS+5cZ7P~U)Fkjz|!%(CE_R`O+~4f;h)}k-DZR!qkd| z3FzO6JiCT_P2;V@mh(G}?ZwBEJ1>m;*t3>Zo4ir=qknOkZ#_+MHUF>y0U6~82+DcBEdl%XKeJM7@(`%VR@23TMqVVsrf7hD3MwZX?CVqxp~TL;S9)yKmfQxJ<%JRkFT zkAc;$Z|vDADO9bkOl31n&F(I22(vqfp5uQH<1^J^`9fNxvYe(6Qq$c653ePb4avXR z4O@a|<*{ttqi4e6guOgF-W+IM29a+73UcWDpmoaUW?ps<^)H&Gl4d(_an7*$NtQ=1 z)nQXP?V(fsSESv@Bm&X3NZ(V+yZs4no<6ZS4ClKXw0xk*nZ~)+xX24VpwFiX9sH8q z-Ad*+SO6@aUUdvv*+XTj!`+uv$2VvM1fnLDh-DTlgPE{`!1BQU)*qHUd`&9*+qVE~ zFv)iC_6vbo>kFUqdrWRjkKqxO=;zHKekOigDxV0q!_#08IS05GrZL82RgE;k%)t{`P5u&l%^eoUXf?$&YBeVp}aYyjlV9Q1x^ zN`Vzwr!+4#xzVSC!Ew5tLh^+wK{@?fG&<*$%Qqs0Mq>CyzvuJJ2l} zt@UKYJnEIDx^!8`vvuAZ7KI_(gF=S4=&sxZR4GYpJD81RRc;@kulkMx`&d&1e_B+1 z!0{!+PuAA|`VKsuNwK@d(2l+?BHX7y;Ld~Hxo-5 zF7YuSAzmY^g?T0vUMtHLJ>miz{L-xnz~F0~%Wquga!h+}7SAmmmI}?YGi1RmfjCkP;A6DVJw*9)Fqg~L zQ==;^u^RX{oASFxLeS%F|LWA8HIg+85q6_MSoD)l%m2DmYEShB!O zbog3YxU|}S(fF@rK>0ku>6Mhw>RX%f?gN`FW*wO~U+@2961I65NzP)x@inrMj_6N* z#VsdljGcVB3MVwJ59hZ}W!lyUX-7uUvXzb(`lk9?wE-2_OPH*59GgvHo1vm?4#R}~ z?8&=kuh%wYhD7|pbXSTI^}SIAqfE5=?N6(OZniD*%gMwiW_dw7Gy1~`rQ=je?uaH5 ziTPCL>n_8sdtaz>fXXt=p+f+ZF(T|s?M1+N>RXfD7ACKG*3ZO!C2!jW!dm>6x~J1@ z80bhB3L4)rxO=xvv*#D%f0~R$F@bShnDY)#Jq3^DInimeOpSD%En0S@f=&043Tos>L8L&cQtpGlW>^THNOHY=mvHZT&1=$`EVHFzwk z*u^&c++h4S{Y%W6=BcO|5$IZ0%Lv2)3Uvl?mOJsLKRh2aSZ*e9R*k)l z*ZUg@ZnC)`l-w%!W>&v>=X5M(v@MCkLnEB0$hC=Rji-0>Ab(Ge%T7y$7Z1jmz$zl- zRBZ$IjEPX5oogm9m>E^3re(qz>Dv0-lB19s+Se!Z^<9S882IAaSyGx9F*-(r;o4Dp zl?#Sye;fVNm>n0@)odGA(>?}fq+?B_(&IZ~uDYqsj#v-;3abTW_)-%^JngU(OW)ZK8Bvkf2O>~g81?xfkS4r^gZ8N1ya?5 zVFMcJuI!NRXXr~5_0IIC=oI&n`Fuw+J{HWp+=R#L)by8`oW+HdyF8V2no}{?a#bIB z<|`2#W<5fukV;y>bNHC`U0%zVt`ylvf4@BY%B1(e^|LYFQQ3*AnjzD4by$L@_Ny}r zKRonzY`%oi;P@RokVtNXK$h_Ri_Df;4QcOlfw7!=`~nwO^+LByDZ$U4=RdrzB{C?i z+KUR!kzH=X;=J(BEX@{KtW>IV;plMe3|SR=cg@8Zw~m0SSZ=+sG0)*XhoAT^6UxId+y_%k{N6S;4O+5l-SkKZQP$0mnz;!5|d4era9KndybG?6S$2?-Kir(Ol5pPW@dm0pa-b-|hK*9$@asuuzHcWDY zu>C0#MpnG{7ePn!uDImNm_2xXwwQc1@B;@YZ+F z?#?P^V4b7!5S3~D`c|U)Eqtoq3@z5p!mobDqvJxRk?EJJ<2=A;dYY`+jNZQn{qviI zJDz%4yMea#1-FTqFlh<3pZYqne$o-f@%I7lZ)AH@UDX~Pz2hL5h>w=PE{yMl(75h1 zU2kbB7$g4To#>ggQ1y6EQ909LOCb3qm6sX5NL6Gq`LBECPAD_a$e@qMi`vpx>sP?9 zF!Oj~rYs2Fn?V#E+6ByGXisns0FDW- zQv6FZjI!>@P~wscRqJA$J-dGEELsVFUP2mT+tox2wL{;umTZ(H+!X~rA0VqbuO)zs zBqxMHgw6&eC>A)IEE}yQY4f)k5cWjOL^=Ba!kpU3k)%&XZjNLJXx*-P1yytDZlv!} zGlqyChGh1DDC&fI3+~fS3&(-;rj@*G8Zy=9XSD=ozrFoX0fu?H@XlC`gk5GE3N+IoF|d7Uk=f=Zqe6RybPAE9G00NGZK#k>g`ST5UU zVV}x$5O|h`p$Z^%xT<&JEu_Pr5^odj{q@>A&BKGoM6aUVB7bzAqX$1Gm9lmNifun4 z`>KrVQuJA_4G`p5mJ8!G$YX~fspkNu<7!nGceRVJqjPCnBQrEeM)*CY(tw3!$s3vX zXNl7ddOqh|51zvcF_DT0g+cbjCdZt6#@o1Von2n9h8w;`7JqLJX@a!^Q%PHlMm-%@ znD9HVIFrt?lf~))Fw{&`VnAl53EPS;AAo*hGtw=W-S6W;MI+H}>-Kr90EpVfU*LAa z>7HHj}%;LxH{GY!m&hWtc~ov2ux2 zFIpaJ@{#U+X+JZ}svQU8DKa>5ANuS2+tx4wmV;CM_G;&=_|yr2Zv4ka*Y6l-yu?*S zl#^uzq=!$7IGznGZttz|TZ!5!nT#>(3|B92##vUv!aoTea010MB?LH1yjZVlgQ*G& zQgM_Tm6ka#TWwn~rG#amSObbv0rVNp?hYxDiFih26s=!5N1#^WjxVOiQ<3{L~67#EX&%SbJgI z1m%^Z?2`(Vwv=?54<_sLO4$$^XWL7}%cuwmn(gI_X=zIvmD-z^pb6OsGI@0ZWWsij zi*L2mO8tE_1#2|;-V&~)()Y6T%ofH1>}<^YXBs5mh%Aevkg5KJ*j2!*TioFGhGz8e z?S#k;QwDl!Tpwa_@VQ3^XAR&zsX0B>CAl=o%z3yHRf-C_Wa-ojIJFD7$_34Nc&2ih z=o@RCVT2kqhnI(w@{{jx>*#vc2eu;2*s^h%iJ z3&vVv9(CkY3^nawvgy2;N1%g~GI-iz%4O=@cPJ|WxNN_! z2g)VJ)xEMnfcM%YUDkQiE$Jp{OWqOq)+*{ zPRW0#=P`W`lEwMIkw7gZgM32!n~M{&6nw(;FD6IRP|;SdRkDu$A4R}d!~g&Q diff --git a/src/site/site.xml b/src/site/site.xml index b6f8d7f..7cb7ea2 100644 --- a/src/site/site.xml +++ b/src/site/site.xml @@ -4,19 +4,18 @@ org.sentrysoftware.maven sentry-maven-skin - 6.1.00 + 6.2.00 true - open source,oss + connector, reference, guide, platform, requirements - diff --git a/src/test/java/org/sentrysoftware/maven/metricshub/connector/parser/ConnectorLibraryParserTest.java b/src/test/java/org/sentrysoftware/maven/metricshub/connector/parser/ConnectorLibraryParserTest.java new file mode 100644 index 0000000..9ff43f3 --- /dev/null +++ b/src/test/java/org/sentrysoftware/maven/metricshub/connector/parser/ConnectorLibraryParserTest.java @@ -0,0 +1,108 @@ +package org.sentrysoftware.maven.metricshub.connector.parser; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; + +import java.io.IOException; +import java.nio.file.Path; +import java.util.Map; + +import org.junit.jupiter.api.Test; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ArrayNode; + +class ConnectorLibraryParserTest { + + @Test + void testParse() throws IOException { + final ConnectorLibraryParser connectorLibraryParser = new ConnectorLibraryParser(); + final Map connectorMap = connectorLibraryParser.parse( + Path.of("src", "test", "resources", "connector") + ); + assertEquals(1, connectorMap.size()); + final JsonNode connector = connectorMap.get("MIB2"); + assertNotNull(connector); + + // Test the ConstantsProcessor + verifyConstantsProcessorBehavior(connector); + + // Test the ExtendsProcessor + verifyExtendsProcessorBehavior(connector); + } + + /** + * Verifies the behavior of the {@link ConstantsProcessor} which has already processed this connector. + * The method checks if the "_OID" constant is resolved correctly in the detection criteria. + * + * @param connector The JsonNode representing the connector. + */ + private void verifyConstantsProcessorBehavior(final JsonNode connector) { + // The _OID constant is resolved correctly + final ArrayNode criteria = (ArrayNode) connector.get("connector").get("detection").get("criteria"); + assertNotNull(criteria); + assertEquals("1.3.6.1.2.1.2.2.1", criteria.get(0).get("oid").asText()); + } + + /** + * Verifies the behavior of the ExtendsProcessor which has already processed this connector. + * The method checks if the metrics node from Hardware.yaml is available in the MIB2 connector. + * It also tests translations and the mapping section of the discovery and collect jobs. + * + * @param connector The JsonNode representing the connector. + */ + private void verifyExtendsProcessorBehavior(final JsonNode connector) { + // The metrics node that comes from Hardware.yaml is available in the MIB2 connector + final JsonNode metricsDefinition = connector.get("metrics"); + assertNotNull(metricsDefinition); + + // Test the translations + final JsonNode translationTable1 = connector.get("translations").get("PortTypeTranslationTable"); + assertNotNull(translationTable1); + assertEquals("Ethernet", translationTable1.get("7").asText()); + assertEquals("FC Port", translationTable1.get("56").asText()); + + final JsonNode translationTable2 = connector.get("translations").get("PortStatusTranslationTable"); + assertNotNull(translationTable2); + assertEquals("ok", translationTable2.get("1").asText()); + assertEquals("degraded", translationTable2.get("3").asText()); + assertEquals("failed", translationTable2.get("7").asText()); + + // Test the mapping section of the discovery job + final JsonNode discoveryMapping = connector.get("monitors").get("network").get("discovery").get("mapping"); + assertNotNull(discoveryMapping); + + // Test attributes + final JsonNode discoveryAttributes = discoveryMapping.get("attributes"); + assertNotNull(discoveryAttributes); + + // Test individual attributes + assertEquals("$1", discoveryAttributes.get("id").asText()); + assertEquals("$7", discoveryAttributes.get("__display_id").asText()); + assertEquals("$4", discoveryAttributes.get("physical_address").asText()); + assertEquals("MAC", discoveryAttributes.get("physical_address_type").asText()); + assertEquals("$3", discoveryAttributes.get("device_type").asText()); + assertEquals("enclosure", discoveryAttributes.get("hw.parent.type").asText()); + assertEquals("${awk::sprintf(\"%s (%s)\", $7, $3)}", discoveryAttributes.get("name").asText()); + + // Test the mapping section of the collect job + final JsonNode collectMapping = connector.get("monitors").get("network").get("collect").get("mapping"); + assertNotNull(collectMapping); + + // Test attributes and metrics + final JsonNode collectAttributes = collectMapping.get("attributes"); + assertNotNull(collectAttributes); + assertEquals("$1", collectAttributes.get("id").asText()); + + final JsonNode collectMetrics = collectMapping.get("metrics"); + assertNotNull(collectMetrics); + assertEquals("$4", collectMetrics.get("hw.status{hw.type=\"network\"}").asText()); + assertEquals("legacyLinkStatus($6)", collectMetrics.get("hw.network.up").asText()); + assertEquals("megaBit2Bit($16)", collectMetrics.get("hw.network.bandwidth.limit").asText()); + assertEquals("$10", collectMetrics.get("hw.errors{hw.type=\"network\"}").asText()); + assertEquals("$8", collectMetrics.get("hw.network.packets{direction=\"receive\"}").asText()); + assertEquals("$12", collectMetrics.get("hw.network.packets{direction=\"transmit\"}").asText()); + assertEquals("$7", collectMetrics.get("hw.network.io{direction=\"receive\"}").asText()); + assertEquals("$11", collectMetrics.get("hw.network.io{direction=\"transmit\"}").asText()); + } +} diff --git a/src/test/resources/connector/Hardware/Hardware.yaml b/src/test/resources/connector/Hardware/Hardware.yaml new file mode 100644 index 0000000..0b48068 --- /dev/null +++ b/src/test/resources/connector/Hardware/Hardware.yaml @@ -0,0 +1,322 @@ +metrics: + + hw.enclosure.energy: + description: Energy consumed by the enclosure since the start of the MetricsHub Agent. + type: Counter + unit: J + + hw.enclosure.power: + description: Instantaneous power consumed by the enclosure, in Watts (hw.enclosure.energy is preferred). + type: Gauge + unit: W + + hw.energy: + description: Energy consumed by the component since the start of the MetricsHub Agent. + type: Counter + unit: J + + hw.errors: + description: Number of errors encountered by the component. + type: Counter + unit: "{errors}" + + hw.host.ambient_temperature: + description: Host's current ambient temperature in degrees Celsius (°C). This metric is only reported if the value is between 5°C and 35°C. + type: Gauge + unit: Cel + + hw.host.energy: + description: Energy consumed by the host since the start of the MetricsHub Agent. + type: Counter + unit: J + + hw.host.heating_margin: + description: Number of degrees Celsius (°C) remaining before the temperature reaches the closest warning threshold. + type: Gauge + unit: Cel + + hw.host.power: + description: Instantaneous power consumed by the host, in Watts (hw.host.energy is preferred). + type: Gauge + unit: W + + hw.power: + description: Instantaneous power consumed by the component, in Watts (hw.energy is preferred). + type: Gauge + unit: W + + hw.status: + description: 'Operational status: 1 (true) or 0 (false) for each of the possible states.' + type: + stateSet: + - degraded + - failed + - ok + + hw.battery.charge: + description: Remaining fraction of battery charge. + type: Gauge + unit: 1 + + hw.battery.charge.limit: + description: Lower limit of battery charge fraction to ensure proper operation. + type: Gauge + unit: 1 + + hw.battery.time_left: + description: Number of seconds left before recharging the battery when state is discharging. + type: Gauge + unit: s + + hw.power_state: + description: Power state. Each of the possible states (off, on and suspended) will either take the value 1 (true) or 0 (false). + type: + stateSet: + - "off" + - "on" + - suspended + + metricshub.connector.status: + description: Connector operational status. + type: + stateSet: + - failed + - ok + + hw.errors.limit: + description: Number of detected and corrected errors that will generate a warning or an alarm when limit_type is degraded or critical. + type: Gauge + unit: "{errors}" + + hw.cpu.speed: + description: CPU current frequency. + type: Gauge + unit: Hz + + hw.cpu.speed.limit: + description: CPU maximum frequency. + type: Gauge + unit: Hz + + hw.fan.speed: + description: Fan speed. + type: Gauge + unit: rpm + + hw.fan.speed.limit: + description: Speed of the corresponding fan (in revolutions/minute) that will generate a warning or an alarm when limit_type is low.degraded or low.critical. + type: Gauge + unit: rpm + + hw.fan.speed_ratio: + description: Fan speed expressed as a fraction of its maximum speed. + type: Gauge + unit: 1 + + hw.fan.speed_ratio.limit: + description: Fan speed ratio that will generate a warning or an alarm when limit_type is low.degraded or low.critical. + type: Gauge + unit: 1 + + hw.gpu.io: + description: Received and transmitted bytes by the GPU. + type: Counter + unit: By + + hw.gpu.memory.limit: + description: Size of the GPU memory. + type: UpDownCounter + unit: By + + hw.gpu.memory.utilization: + description: Fraction of GPU memory used. + type: Gauge + unit: 1 + + hw.gpu.memory.utilization.limit: + description: GPU memory utilization ratio that will generate a warning or an alarm when limit_type is degraded or critical. + type: Gauge + unit: 1 + + hw.gpu.memory.usage: + description: GPU memory used. + type: UpDownCounter + unit: By + + hw.gpu.power: + description: GPU instantaneous power consumption in Watts. + type: Gauge + unit: W + + hw.gpu.utilization: + description: Ratio of time spent by the GPU for each task (decoder, encoder and general). + type: Gauge + unit: 1 + + hw.gpu.utilization.limit: + description: GPU used time ratio that will generate a warning or an alarm when limit_type is degraded or critical. + type: Gauge + unit: 1 + + hw.logical_disk.limit: + description: Size of the logical disk. + type: UpDownCounter + unit: By + + hw.logical_disk.usage: + description: Logical disk space usage. + type: UpDownCounter + unit: By + + hw.logical_disk.utilization: + description: Logical disk space utilization as a fraction. + type: Gauge + unit: 1 + + hw.lun.paths: + description: Number of available paths. + type: Gauge + unit: "{paths}" + + hw.lun.paths.limit: + description: Number of available paths that will generate a warning when limit_type is low.degraded. + type: Gauge + unit: "{paths}" + + hw.memory.limit: + description: Memory module size. + type: Gauge + unit: By + + hw.network.bandwidth.limit: + description: Speed that the network adapter and its remote counterpart currently use to communicate with each other. + type: UpDownCounter + unit: By + + hw.network.bandwidth.utilization: + description: Utilization of the network bandwidth as a fraction. + type: Gauge + unit: 1 + + hw.network.error_ratio: + description: Ratio of sent and received packets that were in error. + type: Gauge + unit: 1 + + hw.network.error_ratio.limit: + description: Network interface error ratio that will generate a warning or an alarm when limit_type is degraded or critical. + type: Gauge + unit: 1 + + hw.network.full_duplex: + description: Whether the port is configured to operate in full-duplex mode. + type: UpDownCounter + + hw.network.io: + description: Received and transmitted network traffic in bytes. + type: Counter + unit: By + + hw.network.packets: + description: Received and transmitted network traffic in packets (or frames). + type: Counter + unit: "{packets}" + + hw.network.up: + description: Link status. + type: UpDownCounter + + hw.other_device.uses: + description: Number of times the device has been used. + type: Counter + unit: "{uses}" + + hw.other_device.uses.limit: + description: Number of times the device has been used which will generate a warning or an alarm when limit_type is degraded or critical. + type: Gauge + unit: "{uses}" + + hw.other_device.value: + description: Currently reported value of the device. + type: Gauge + + hw.other_device.value.limit: + description: Device reported value that will generate a warning or an alarm when limit_type is degraded or critical. + type: Gauge + + hw.physical_disk.endurance_utilization: + description: Physical disk remaining endurance ratio. + type: Gauge + unit: 1 + + hw.physical_disk.size: + description: Size of the disk. + type: Gauge + unit: By + + hw.physical_disk.smart: + description: Value of the corresponding S.M.A.R.T. attribute. + type: Gauge + unit: 1 + + hw.power_supply.limit: + description: Maximum power output of the power supply. + type: Gauge + unit: W + + hw.power_supply.utilization: + description: Utilization of the power supply as a fraction of its maximum output. + type: Gauge + unit: 1 + + hw.robotics.moves: + description: Number of moves operations that occurred during the last collect interval. + type: Counter + unit: "{moves}" + + hw.tape_drive.operations: + description: Operations performed by the tape drive. + type: Counter + unit: "{operations}" + + hw.temperature: + description: Temperature of the component. + type: Gauge + unit: Cel + + hw.temperature.limit: + description: Temperature of the corresponding component that will generate a warning or an alarm when limit_type is high.degraded or high.critical. + type: Gauge + unit: Cel + + hw.vm.power_ratio: + description: Ratio of host power consumed by the virtual machine. + type: Gauge + unit: Cel + + hw.voltage: + description: Voltage measured by the sensor. + type: Gauge + unit: V + + hw.voltage.limit: + description: Voltage limit in Volts. + type: Gauge + unit: V + + hw.power.limit: + description: Energy consumption of the corresponding component that will generate a warning or an alarm when limit_type is high.degraded or high.critical. + type: Gauge + unit: W + + metricshub.agent.info: + description: MetricsHub Agent information. + type: Gauge + + metricshub.host.configured: + description: Whether the host is configured or not. + type: UpDownCounter + + metricshub.host.up: + description: Whether the configured protocol (http, ipmi, snmp, ssh, wbem, winrm and wmi) is up (1) or not (0). + type: UpDownCounter diff --git a/src/test/resources/connector/MIB2-header/MIB2-header.yaml b/src/test/resources/connector/MIB2-header/MIB2-header.yaml new file mode 100644 index 0000000..d6e0bed --- /dev/null +++ b/src/test/resources/connector/MIB2-header/MIB2-header.yaml @@ -0,0 +1,272 @@ +--- +constants: + _OID: 1.3.6.1.2.1.2.2.1 + +connector: + reliesOn: MIB-2 Standard SNMP Agent + version: 1.0 + information: This connector discovers the enclosure and Ethernet ports of a system equipped with an MIB-2 standard SNMP Agent. + detection: + connectionTypes: + - remote + - local + criteria: + # Criteria(1): there must be something in the ifTable SNMP Table + - type: snmpGetNext + oid: _OID + +monitors: + network: + discovery: + sources: + source(1): + # Source(1) = ifTable SNMP Table + # PortID;Description;TypeCode;MacAddress;AdminStatus; + type: snmpTable + oid: _OID + selectColumns: "ID,2,3,6,7" + computes: + # Keep only ports whose administrative status is 'up' + # PortID;Description;TypeCode;MacAddress;AdminStatus; + - type: keepOnlyMatchingLines + column: 5 + valueList: 1 + # Keep only real Ethernet and/or FC ports + # PortID;Description;TypeCode;MacAddress;AdminStatus; + - type: keepOnlyMatchingLines + column: 3 + valueList: "6,7,26,32,37,62,94,95,96,97,117,166" + # Translate the TypeCode to a readable string + # PortID;Description;PortType;MacAddress;AdminStatus; + - type: translate + column: 3 + translationTable: "${translation::PortTypeTranslationTable}" + source(2): + # Get information from the ifXtable + # ID;Name;Alias; + type: snmpTable + oid: 1.3.6.1.2.1.31.1.1.1 + selectColumns: "ID,1,18" + source(3): + # Join the 32bit MIB2 table with the 64 bit IfXTable + # PortID;Description;TypeCode;MacAddress;AdminStatus;ID;Name;Alias; + type: tableJoin + leftTable: "${source::monitors.network.discovery.sources.source(1)}" + rightTable: "${source::monitors.network.discovery.sources.source(2)}" + leftKeyColumn: 1 + rightKeyColumn: 1 + defaultRightLine: ;;; + collect: + # Collect type = multi-instance + type: multiInstance + keys: + - id + sources: + source(1): + # Source(1) = ifTable SNMP Table + # PortID;Description;Speed;OperationalStatus;ReceivedBytes;ReceivedUnicastPackets;ReceivedNonUnicastPackets;ReceivedErrors;TransmittedBytes;TransmittedUnicastPackets;TransmittedNonUnicastPackets;TransmittedErrors; + type: snmpTable + oid: _OID + selectColumns: "ID,2,5,8,10,11,12,14,16,17,18,20" + computes: + # Duplicate Status twice + # PortID;Description;Speed;OperationalStatus;OperationalStatus;OperationalStatus;ReceivedBytes;ReceivedUnicastPackets;ReceivedNonUnicastPackets;ReceivedErrors;TransmittedBytes;TransmittedUnicastPackets;TransmittedNonUnicastPackets;TransmittedErrors; + - type: duplicateColumn + column: 4 + - type: duplicateColumn + column: 5 + # Translate the first column status into a PATROLStatus + # PortID;Description;Speed;PATROLStatus;OperationalStatus;OperationalStatus;ReceivedBytes;ReceivedUnicastPackets;ReceivedNonUnicastPackets;ReceivedErrors;TransmittedBytes;TransmittedUnicastPackets;TransmittedNonUnicastPackets;TransmittedErrors; + - type: translate + column: 4 + translationTable: "${translation::PortStatusTranslationTable}" + # Translate the second column status into a more readable string + # PortID;Description;Speed;PATROLStatus;StatusInformation;OperationalStatus;ReceivedBytes;ReceivedUnicastPackets;ReceivedNonUnicastPackets;ReceivedErrors;TransmittedBytes;TransmittedUnicastPackets;TransmittedNonUnicastPackets;TransmittedErrors; + - type: translate + column: 5 + translationTable: "${translation::PortStatusInformationTranslationTable}" + # Translate the third column status into a LinkStatus value + # PortID;Description;Speed;PATROLStatus;StatusInformation;LinkStatus;ReceivedBytes;ReceivedUnicastPackets;ReceivedNonUnicastPackets;ReceivedErrors;TransmittedBytes;TransmittedUnicastPackets;TransmittedNonUnicastPackets;TransmittedErrors; + - type: translate + column: 6 + translationTable: "${translation::PortLinkStatusInformationTranslationTable}" + # Convert bits/sec into Mbps + # PortID;Description;SpeedMBps;PATROLStatus;StatusInformation;LinkStatus;ReceivedBytes;ReceivedUnicastPackets;ReceivedNonUnicastPackets;ReceivedErrors;TransmittedBytes;TransmittedUnicastPackets;TransmittedNonUnicastPackets;TransmittedErrors; + - type: divide + column: 3 + value: 1000000 + # Add up ReceivedUnicastPackets and ReceivedNonUnicastPackets + # PortID;Description;SpeedMBps;PATROLStatus;StatusInformation;LinkStatus;ReceivedBytes;ReceivedPackets;ReceivedNonUnicastPackets;ReceivedErrors;TransmittedBytes;TransmittedUnicastPackets;TransmittedNonUnicastPackets;TransmittedErrors; + - type: add + column: 8 + value: $9 + # Add up TransmittedUnicastPackets and TransmittedNonUnicastPackets + # PortID;Description;SpeedMBps;PATROLStatus;StatusInformation;LinkStatus;ReceivedBytes;ReceivedPackets;ReceivedNonUnicastPackets;ReceivedErrors;TransmittedBytes;TransmittedPackets;TransmittedNonUnicastPackets;TransmittedErrors; + - type: add + column: 12 + value: $13 + # Add up ReceivedErrors and TransmittedErrors + # PortID;Description;SpeedMBps;PATROLStatus;StatusInformation;LinkStatus;ReceivedBytes;ReceivedPackets;ReceivedNonUnicastPackets;TotalErrors;TransmittedBytes;TransmittedPackets;TransmittedNonUnicastPackets;TransmittedErrors; + - type: add + column: 10 + value: $14 + source(2): + # Source(2) = ifMIBObjects SNMP Table + # PortID;SpeedMBs;ReceivedBytes64;ReceivedUnicastPackets64;ifHCInMulticastPkts64;ifHCInBroadcastPkts64;TransmittedBytes64;TransmittedPackets64;ifHCOutMulticastPkts;ifHCOutBroadcastPkts; + type: snmpTable + oid: 1.3.6.1.2.1.31.1.1.1 + selectColumns: "ID,15,6,7,8,9,10,11,12,13" + computes: + # Add MulticastPkts and BroadcastPkts to UnicastPackets + # PortID;SpeedMBs;ReceivedBytes64;ReceivedUnicastPackets64;ifHCInMulticastPkts64;ifHCInBroadcastPkts64;TransmittedBytes64;TransmittedPackets64;ifHCOutMulticastPkts;ifHCOutBroadcastPkts; + - type: add + column: 4 + value: $5 + - type: add + column: 4 + value: $6 + - type: add + column: 8 + value: $9 + - type: add + column: 8 + value: $10 + # Keep only useful columns + # PortID;SpeedMBs;ReceivedBytes64;ReceivedPackets64;TransmittedBytes64;TransmittedPackets64; + - type: keepColumns + columnNumbers: "1,2,3,4,7,8" + source(3): + # Source(3) = Table Joint of (1) and (2) + # PortID;Description;SpeedMBps;PATROLStatus;StatusInformation;LinkStatus;ReceivedBytes;ReceivedPackets;ReceivedNonUnicastPackets;TotalErrors;TransmittedBytes;TransmittedPackets;TransmittedNonUnicastPackets;TransmittedErrors;PortID;SpeedMBs;ReceivedBytes64;ReceivedPackets64;TransmittedBytes64;TransmittedPackets64; + type: tableJoin + leftTable: "${source::monitors.network.collect.sources.source(1)}" + rightTable: "${source::monitors.network.collect.sources.source(2)}" + leftKeyColumn: 1 + rightKeyColumn: 1 + defaultRightLine: ;;;;;; + computes: + # Now add MSHW to the Left and Right of the ifMIBObjects SpeedMBS + # PortID;Description;SpeedMBps;PATROLStatus;StatusInformation;LinkStatus;ReceivedBytes;ReceivedPackets;ReceivedNonUnicastPackets;TotalErrors;TransmittedBytes;TransmittedPackets;TransmittedNonUnicastPackets;TransmittedErrors;PortID;SpeedMBs; + - type: leftConcat + column: 16 + value: MSHW + - type: rightConcat + column: 16 + value: MSHW + # Replace "MSHWMSHW", i.e. a blank column with the ifTable value. + # This will use the old 32^2 limited value of link bps speed if a Mbps value is not available + # PortID;Description;SpeedMBps;PATROLStatus;StatusInformation;LinkStatus;ReceivedBytes;ReceivedPackets;ReceivedNonUnicastPackets;TotalErrors;TransmittedBytes;TransmittedPackets;TransmittedNonUnicastPackets;TransmittedErrors;PortID;SpeedMBs; + - type: replace + column: 16 + existingValue: MSHWMSHW + newValue: $3 + # Now get rid of any remaining MSHWs + # PortID;Description;SpeedMBps;PATROLStatus;StatusInformation;LinkStatus;ReceivedBytes;ReceivedPackets;ReceivedNonUnicastPackets;TotalErrors;TransmittedBytes;TransmittedPackets;TransmittedNonUnicastPackets;TransmittedErrors;PortID;SpeedMBs; + - type: replace + column: 16 + existingValue: MSHW + newValue: "" + source(4): + # Make a copy of Source(3) + type: copy + from: "${source::monitors.network.collect.sources.source(3)}" + computes: + # Keep only interfaces with 64bit counters + # 1 2 3 4 # 5 # 6 7 # 8 # 9 # 10 11 12 # 13 # # 14 # 15 16 17 # 18 # 19 # 20 + # PortID;Description;SpeedMBps;PATROLStatus;StatusInformation;LinkStatus;ReceivedBytes;ReceivedPackets;ReceivedNonUnicastPackets;TotalErrors;TransmittedBytes;TransmittedPackets;TransmittedNonUnicastPackets;TransmittedErrors;PortID;SpeedMBs;ReceivedBytes64;ReceivedPackets64;TransmittedBytes64;TransmittedPackets64; + - type: keepOnlyMatchingLines + column: 20 + regExp: . + # Replace 32 bit counters with 64 bit ones + # ReceivedBytes + # PortID;Description;SpeedMBps;PATROLStatus;StatusInformation;LinkStatus;ReceivedBytes;ReceivedPackets;ReceivedNonUnicastPackets;TotalErrors;TransmittedBytes;TransmittedPackets;TransmittedNonUnicastPackets;TransmittedErrors;PortID;SpeedMBs;ReceivedBytes64;ReceivedPackets64;TransmittedBytes64;TransmittedPackets64; + - type: replace + column: 7 + existingValue: $7 + newValue: $17 + # ReceivedPackets + - type: replace + column: 8 + existingValue: $8 + newValue: $18 + # TransmittedBytes + - type: replace + column: 11 + existingValue: $11 + newValue: $19 + # TransmittedPackets + - type: replace + column: 12 + existingValue: $12 + newValue: $20 + source(5): + # Make a copy of Source(3) + type: copy + from: "${source::monitors.network.collect.sources.source(3)}" + computes: + # Exclude interfaces with 64bit counters + # 1 2 3 4 # 5 # 6 7 # 8 # 9 # 10 11 12 # 13 # # 14 # 15 16 17 # 18 # 19 # 20 + # PortID;Description;SpeedMBps;PATROLStatus;StatusInformation;LinkStatus;ReceivedBytes;ReceivedPackets;ReceivedNonUnicastPackets;TotalErrors;TransmittedBytes;TransmittedPackets;TransmittedNonUnicastPackets;TransmittedErrors;PortID;SpeedMBs;ReceivedBytes64;ReceivedPackets64;TransmittedBytes64;TransmittedPackets64; + - type: excludeMatchingLines + column: 20 + regExp: . + source(6): + # Union 32 and 64 bit tables + # PortID;Description;SpeedMBps;PATROLStatus;StatusInformation;LinkStatus;ReceivedBytes;ReceivedPackets;ReceivedNonUnicastPackets;TotalErrors;TransmittedBytes;TransmittedPackets;TransmittedNonUnicastPackets;TransmittedErrors;PortID;SpeedMBs;ReceivedBytes64;ReceivedPackets64;TransmittedBytes64;TransmittedPackets64; + type: tableUnion + tables: + - "${source::monitors.network.collect.sources.source(4)}" + - "${source::monitors.network.collect.sources.source(5)}" + mapping: + # ValueTable = Source(6) + source: "${source::monitors.network.collect.sources.source(6)}" + attributes: + id: $1 + metrics: + hw.status{hw.type="network"}: $4 + hw.network.up: legacyLinkStatus($6) + hw.network.bandwidth.limit: megaBit2Bit($16) + hw.errors{hw.type="network"}: $10 + hw.network.packets{direction="receive"}: $8 + hw.network.packets{direction="transmit"}: $12 + hw.network.io{direction="receive"}: $7 + hw.network.io{direction="transmit"}: $11 + legacyTextParameters: + StatusInformation: $5 +translations: + PortLinkStatusInformationTranslationTable: + "2": degraded + "6": degraded + "7": degraded + Default: ok + PortStatusInformationTranslationTable: + "1": Up + "2": Down + "3": Testing + "5": Dormant + "6": Component Not Present + "7": Lower Layer Down + Default: Unknown Status + PortTypeTranslationTable: + "56": FC Port + "26": Ethernet + "37": ATM + Default: Ethernet + "166": MPLS + "6": Ethernet + "7": Ethernet + "117": Ethernet + "94": ADSL + "62": Ethernet + "95": RSDL + "96": SDSL + "97": VDSL + "32": Frame Relay + PortStatusTranslationTable: + "1": ok + "2": ok + "3": degraded + "5": ok + "6": failed + "7": failed + Default: UNKNOWN diff --git a/src/test/resources/connector/MIB2/MIB2.yaml b/src/test/resources/connector/MIB2/MIB2.yaml new file mode 100644 index 0000000..cab7063 --- /dev/null +++ b/src/test/resources/connector/MIB2/MIB2.yaml @@ -0,0 +1,55 @@ +--- +extends: +- ../Hardware/Hardware +- ../MIB2-header/MIB2-header +connector: + displayName: MIB-2 Standard SNMP Agent - Network Interfaces + platforms: Any system with SNMP + detection: + appliesTo: + - Network + - OOB + - HP + - Storage + - VMS + - OSF1 + supersedes: + - HPUXNetwork +monitors: + network: + discovery: + sources: + source(3): + computes: + # Now add MSHW to the Left and Right of the ifMIBObjects SpeedMBS + # PortID;Description;PortType;MacAddress;AdminStatus;ID;Name;Alias; + - type: leftConcat + column: 7 + value: MSHW + - type: rightConcat + column: 7 + value: MSHW + # Replace "MSHWMSHW", i.e. a blank column with the ifTable value. + # PortID;Description;PortType;MacAddress;AdminStatus;ID;Name;Alias; + - type: replace + column: 7 + existingValue: MSHWMSHW + newValue: $2 + # Now get rid of any remaining MSHWs + # PortID;Description;PortType;MacAddress;AdminStatus;ID;Name;Alias; + - type: replace + column: 7 + existingValue: MSHW + newValue: "" + mapping: + # InstanceTable = Source(1) + # PortID;Description;PortType;MacAddress;AdminStatus;ID;Name;Alias; + source: "${source::monitors.network.discovery.sources.source(3)}" + attributes: + id: $1 + __display_id: $7 + physical_address: $4 + physical_address_type: MAC + device_type: $3 + hw.parent.type: enclosure + name: "${awk::sprintf(\"%s (%s)\", $7, $3)}" From 48c1700a76df0c22705aee02ae19590949daed97 Mon Sep 17 00:00:00 2001 From: Nassim Boutekedjiret Date: Tue, 2 Jan 2024 19:40:19 +0100 Subject: [PATCH 02/13] [#4] Develop Connector Reference Report Goal * Fixed repository URL in POM. --- pom.xml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 2e19886..6d6c449 100644 --- a/pom.xml +++ b/pom.xml @@ -33,8 +33,7 @@ ossrh - - https://s01.oss.sonatype.org/service/local/staging/deploy/maven2/ + https://s01.oss.sonatype.org/service/local/staging/deploy/maven2/ From 58de5163166eba08d010b1bdde0b356c8034a139 Mon Sep 17 00:00:00 2001 From: Nassim Boutekedjiret Date: Wed, 3 Jan 2024 12:11:03 +0100 Subject: [PATCH 03/13] [#4] Develop Connector Reference Report Goal * Removed MIB2Solaris from integration tests. --- .../hardware/MIB2Solaris/MIB2Solaris.yaml | 55 ------------------- .../hardware/MIB2Solaris/embeddedFile-1 | 16 ------ .../SunEthernetDriver-header.yaml | 31 ----------- src/it/metricshub-connectors/verify.groovy | 3 - 4 files changed, 105 deletions(-) delete mode 100644 src/it/metricshub-connectors/src/main/connector/hardware/MIB2Solaris/MIB2Solaris.yaml delete mode 100644 src/it/metricshub-connectors/src/main/connector/hardware/MIB2Solaris/embeddedFile-1 delete mode 100644 src/it/metricshub-connectors/src/main/connector/hardware/SunEthernetDriver-header/SunEthernetDriver-header.yaml diff --git a/src/it/metricshub-connectors/src/main/connector/hardware/MIB2Solaris/MIB2Solaris.yaml b/src/it/metricshub-connectors/src/main/connector/hardware/MIB2Solaris/MIB2Solaris.yaml deleted file mode 100644 index 37947e9..0000000 --- a/src/it/metricshub-connectors/src/main/connector/hardware/MIB2Solaris/MIB2Solaris.yaml +++ /dev/null @@ -1,55 +0,0 @@ ---- -extends: -- ../Hardware/Hardware -- ../MIB2-header/MIB2-header -- ../SunEthernetDriver-header/SunEthernetDriver-header -connector: - displayName: MIB-2 Standard SNMP Agent - Network Interfaces - Solaris - platforms: Any system with SNMP - detection: - appliesTo: - - Solaris - supersedes: - - SunNetwork -monitors: - network: - discovery: - sources: - source(1): - computes: - # Process that through an AWK script to use Description as the device ID - # and isolate the driver name, so we can translate that into a card model - # Also keep only cards where the first word is a purely alphanumeric driverInstance name - # DeviceID;DriverName;PortType;MacAddress;AdminStatus; - - type: awk - script: "${file::embeddedFile-1}" - keep: ^MSHW; - separators: ; - selectColumns: "2,3,4,5,6" - # Translate the Driver column into a model name - # DeviceID;Model;PortType;MacAddress;AdminStatus; - - type: translate - column: 2 - translationTable: "${translation::DriverTranslationTable}" - mapping: - # InstanceTable = Source(1) - # PortID;Description;PortType;MacAddress;AdminStatus; - source: "${source::monitors.network.discovery.sources.source(1)}" - attributes: - id: $1 - vendor: $2 - physical_address: $4 - physical_address_type: MAC - device_type: $3 - hw.parent.type: enclosure - name: "${awk::sprintf(\"%s (%s - %s)\", $1, $3, $2)}" - collect: - sources: - source(6): - computes: - # Same as in the discovery, use Description as the DeviceID - # PortID;Description;SpeedMBps;PATROLStatus;StatusInformation;LinkStatus;ReceivedBytes;ReceivedPackets;ReceivedNonUnicastPackets;TotalErrors;TransmittedBytes;TransmittedPackets;TransmittedNonUnicastPackets;TransmittedErrors; - - type: replace - column: 1 - existingValue: $1 - newValue: $2 diff --git a/src/it/metricshub-connectors/src/main/connector/hardware/MIB2Solaris/embeddedFile-1 b/src/it/metricshub-connectors/src/main/connector/hardware/MIB2Solaris/embeddedFile-1 deleted file mode 100644 index 74395b1..0000000 --- a/src/it/metricshub-connectors/src/main/connector/hardware/MIB2Solaris/embeddedFile-1 +++ /dev/null @@ -1,16 +0,0 @@ -BEGIN { - FS = ";" -} -($2 ~ /^[a-zA-Z0-9]+$/) { - DeviceID = $2 - driver = DeviceID - while (substr(driver, length(driver), 1) ~ /[0-9]/) - { - driver = substr(driver, 1, length(driver) - 1) - } - - if (driver != "lo" && driver != "lpfc" && driver != "jnet" && driver != "dman" && driver != "aggr" && driver != "clprivnet" && driver != "sppp") - { - print "MSHW;" DeviceID ";" driver ";" $3 ";" $4 ";" $5 - } -} \ No newline at end of file diff --git a/src/it/metricshub-connectors/src/main/connector/hardware/SunEthernetDriver-header/SunEthernetDriver-header.yaml b/src/it/metricshub-connectors/src/main/connector/hardware/SunEthernetDriver-header/SunEthernetDriver-header.yaml deleted file mode 100644 index 9604255..0000000 --- a/src/it/metricshub-connectors/src/main/connector/hardware/SunEthernetDriver-header/SunEthernetDriver-header.yaml +++ /dev/null @@ -1,31 +0,0 @@ ---- -translations: - DriverTranslationTable: - bge: Broadcom BCM57xx Gigabit Ethernet - e1000g: Intel PRO/1000 Gigabit Ethernet - hme: Fast Ethernet - pcelx: 3Com Etherlink III PCMCIA Ethernet - fjgi: Fujitsu Quad Gigabit Ethernet - nge: nVidia Gigabit Ethernet - spwr: SMC EtherPower II 10/100 (9432) Ethernet - rge: Realtek Gigabit Ethernet - vge: Gigabit Ethernet - dnet: DEC2104x Ethernet - xge: Neterion Xframe 10Gigabit Ethernet - pcn: AMD Pcnet Ethernet - nxge: Sun 10/1 Gigabit Ethernet - eri: Fast Ethernet - ge: GEM Gigabit Ethernet - ce: Cassini Gigabit Ethernet - dmfe: Davicom Fast Ethernet DM9102A - amd8111s: AMD-8111 Fast Ethernet - bnx: Broadcom NetXtreme II Gigabit Ethernet - Default: "" - qfe: Sun Quad Fast Ethernet - elxl: 3Com Ethernet - ipge: Intel 82571 PCI-E Gigabit Ethernet - chxge: Chelsio Ethernet - bcme: Broadcom BCM57xx Gigabit Ethernet - sk98sol: SysKonnect SK-98xx Gigabit Ethernet - ixgb: Intel 82597ex 10 Gigabit Ethernet - rtls: Realtek 8139 Fast Ethernet diff --git a/src/it/metricshub-connectors/verify.groovy b/src/it/metricshub-connectors/verify.groovy index 9cdac14..c40d106 100644 --- a/src/it/metricshub-connectors/verify.groovy +++ b/src/it/metricshub-connectors/verify.groovy @@ -22,7 +22,6 @@ assert htmlText.indexOf("LinuxIPNetwork") > -1 : "metricshub-connector-reference assert htmlText.indexOf("lmsensors") > -1 : "metricshub-connector-reference: lmsensors must be listed" assert htmlText.indexOf("MIB2") > -1 : "metricshub-connector-reference: MIB2 must be listed" assert htmlText.indexOf("MIB2Linux") > -1 : "metricshub-connector-reference: MIB2Linux must be listed" -assert htmlText.indexOf("MIB2Solaris") > -1 : "metricshub-connector-reference: MIB2Solaris must be listed" assert htmlText.indexOf("MIB2NT") > -1 : "metricshub-connector-reference: MIB2NT must be listed" assert htmlText.indexOf("NvidiaSmi") > -1 : "metricshub-connector-reference: NvidiaSmi must be listed" assert htmlText.indexOf("VMwareESXiDisksIPMI") > -1 : "metricshub-connector-reference: VMwareESXiDisksIPMI must be listed" @@ -57,7 +56,6 @@ String [] fileNamesToCheck = [ 'mib2.html', 'mib2linux.html', 'mib2nt.html', - 'mib2solaris.html', 'mib2switch.html', 'nvidiasmi.html', 'virsh.html', @@ -108,7 +106,6 @@ assert htmlText.indexOf("MIB-2 Standard SNMP Agent - Network Interfaces - Window assert htmlText.indexOf("Libre Hardware Monitor") > -1 : "platform-requirements: 'Libre Hardware Monitor' must be listed" assert htmlText.indexOf("Ethernet Switch with Sensors (SNMP)") > -1 : "platform-requirements: 'Ethernet Switch with Sensors (SNMP)' must be listed" assert htmlText.indexOf("Generic Ethernet Switch") > -1 : "platform-requirements: 'Generic Ethernet Switch' must be listed" -assert htmlText.indexOf("MIB-2 Standard SNMP Agent - Network Interfaces - Solaris") > -1 : "platform-requirements: 'MIB-2 Standard SNMP Agent - Network Interfaces - Solaris' must be listed" assert htmlText.indexOf("Generic UPS") > -1 : "platform-requirements: 'Generic UPS' must be listed" assert htmlText.indexOf("VMware ESXi 5 LUN (esxcli)") > -1 : "platform-requirements: 'VMware ESXi 5 LUN (esxcli)' must be listed" assert htmlText.indexOf("VMware ESXi 5 LUN (SSH)") > -1 : "platform-requirements: 'VMware ESXi 5 LUN (SSH)' must be listed" From 76ce8f3c0eb4049d415a392048f3d53b6326124e Mon Sep 17 00:00:00 2001 From: Nassim Boutekedjiret Date: Thu, 4 Jan 2024 00:29:54 +0100 Subject: [PATCH 04/13] [#4] Develop Connector Reference Report Goal Incorporated feedback from review: * Enhanced plugins configuration in POM. * Fixed issues in Java code and Javadoc. * Added site.css in integration tests for illustration. --- pom.xml | 34 ++++++++++++++++++- .../src/site/resources/css/site.css | 23 +++++++++++++ .../metricshub-connectors/src/site/site.xml | 1 + .../maven/metricshub/connector/Constants.java | 5 --- .../parser/AbstractNodeProcessor.java | 8 ++--- .../connector/parser/ConstantsProcessor.java | 2 +- .../connector/parser/ExtendsProcessor.java | 2 +- .../producer/ConnectorJsonNodeReader.java | 3 +- .../ConnectorPageReferenceProducer.java | 7 ++-- .../producer/MainPageReferenceProducer.java | 6 ++-- .../producer/model/common/TechnologyType.java | 4 +-- .../parser/ConnectorLibraryParserTest.java | 6 ++-- 12 files changed, 74 insertions(+), 27 deletions(-) create mode 100644 src/it/metricshub-connectors/src/site/resources/css/site.css diff --git a/pom.xml b/pom.xml index 6d6c449..4844e87 100644 --- a/pom.xml +++ b/pom.xml @@ -208,6 +208,12 @@ + + + maven-invoker-plugin + 3.6.0 + + @@ -361,7 +367,6 @@ maven-invoker-plugin - 3.6.0 true ${project.build.directory}/it @@ -406,6 +411,10 @@ ${project.build.sourceEncoding} true checkstyle.xml + + ${project.build.sourceDirectory} + ${project.build.testSourceDirectory} + @@ -421,6 +430,9 @@ pmd.xml + + ${project.build.directory}/generated-sources/plugin + @@ -459,8 +471,28 @@ maven-project-info-reports-plugin 3.5.0 + + + + ci-management + dependencies + dependency-info + distribution-management + issue-management + licenses + plugins + scm + summary + team + + + + + + maven-invoker-plugin + diff --git a/src/it/metricshub-connectors/src/site/resources/css/site.css b/src/it/metricshub-connectors/src/site/resources/css/site.css new file mode 100644 index 0000000..1a27d54 --- /dev/null +++ b/src/it/metricshub-connectors/src/site/resources/css/site.css @@ -0,0 +1,23 @@ +.custom-class code[class*=language-js] { + background-color: #f9f2f4; +} + +.custom-class code[class*=language-js] .token.punctuation { + color: #a1a1a1; +} + +.custom-class code[class*=language-js] .token.operator { + color: #3e3f58; +} + +.custom-class code[class*=language-js] .token.string { + color: #c96a43; +} + +.custom-class code[class*=language-js]::selection, +.custom-class code[class*=language-js] ::selection +{ + text-shadow: none; + background: #4f62ad; + color: white; +} \ No newline at end of file diff --git a/src/it/metricshub-connectors/src/site/site.xml b/src/it/metricshub-connectors/src/site/site.xml index 98b004e..c83bdc4 100644 --- a/src/it/metricshub-connectors/src/site/site.xml +++ b/src/it/metricshub-connectors/src/site/site.xml @@ -13,6 +13,7 @@ true metricshub, connector, hardware, system + custom-class diff --git a/src/main/java/org/sentrysoftware/maven/metricshub/connector/Constants.java b/src/main/java/org/sentrysoftware/maven/metricshub/connector/Constants.java index b6947b2..9904232 100644 --- a/src/main/java/org/sentrysoftware/maven/metricshub/connector/Constants.java +++ b/src/main/java/org/sentrysoftware/maven/metricshub/connector/Constants.java @@ -41,9 +41,4 @@ public class Constants { * Name of the subdirectory that will contain the pages for each connector */ public static final String CONNECTOR_SUBDIRECTORY_NAME = "connectors"; - - /** - * Empty string - */ - public static final String EMPTY = ""; } diff --git a/src/main/java/org/sentrysoftware/maven/metricshub/connector/parser/AbstractNodeProcessor.java b/src/main/java/org/sentrysoftware/maven/metricshub/connector/parser/AbstractNodeProcessor.java index aa88dce..665761f 100644 --- a/src/main/java/org/sentrysoftware/maven/metricshub/connector/parser/AbstractNodeProcessor.java +++ b/src/main/java/org/sentrysoftware/maven/metricshub/connector/parser/AbstractNodeProcessor.java @@ -37,14 +37,14 @@ public abstract class AbstractNodeProcessor { protected AbstractNodeProcessor next; /** - * Process the provided JsonNode and return the resulting {@link JsonNode}. + * Process the provided {@link JsonNode} with the remaining chain of processors. * * @param node The JsonNode to be processed. * @return An instance of {@link JsonNode} representing the result of the processing. * @throws IOException If an I/O error occurs during the processing. */ public JsonNode process(final JsonNode node) throws IOException { - final JsonNode processedNode = doProcessing(node); + final JsonNode processedNode = processNode(node); if (next != null) { return next.process(processedNode); @@ -54,11 +54,11 @@ public JsonNode process(final JsonNode node) throws IOException { } /** - * Perform processing on the provided JsonNode and return the resulting {@link JsonNode}. + * Process one {@link JsonNode}. * * @param node The JsonNode to be processed. * @return An instance of {@link JsonNode} representing the result of the processing. * @throws IOException If an I/O error occurs during the processing. */ - protected abstract JsonNode doProcessing(JsonNode node) throws IOException; + protected abstract JsonNode processNode(JsonNode node) throws IOException; } diff --git a/src/main/java/org/sentrysoftware/maven/metricshub/connector/parser/ConstantsProcessor.java b/src/main/java/org/sentrysoftware/maven/metricshub/connector/parser/ConstantsProcessor.java index bc9a973..f1261eb 100644 --- a/src/main/java/org/sentrysoftware/maven/metricshub/connector/parser/ConstantsProcessor.java +++ b/src/main/java/org/sentrysoftware/maven/metricshub/connector/parser/ConstantsProcessor.java @@ -43,7 +43,7 @@ public ConstantsProcessor() { } @Override - public JsonNode doProcessing(final JsonNode node) { + public JsonNode processNode(final JsonNode node) { final JsonNode constantsNode = node.get("constants"); if (constantsNode != null && constantsNode.isObject()) { diff --git a/src/main/java/org/sentrysoftware/maven/metricshub/connector/parser/ExtendsProcessor.java b/src/main/java/org/sentrysoftware/maven/metricshub/connector/parser/ExtendsProcessor.java index 0cb2174..4b085fc 100644 --- a/src/main/java/org/sentrysoftware/maven/metricshub/connector/parser/ExtendsProcessor.java +++ b/src/main/java/org/sentrysoftware/maven/metricshub/connector/parser/ExtendsProcessor.java @@ -54,7 +54,7 @@ public ExtendsProcessor(Path connectorDirectory, AbstractNodeProcessor next) { } @Override - public JsonNode doProcessing(JsonNode node) throws IOException { + public JsonNode processNode(JsonNode node) throws IOException { return doMerge(node); } diff --git a/src/main/java/org/sentrysoftware/maven/metricshub/connector/producer/ConnectorJsonNodeReader.java b/src/main/java/org/sentrysoftware/maven/metricshub/connector/producer/ConnectorJsonNodeReader.java index 610f141..0831edf 100644 --- a/src/main/java/org/sentrysoftware/maven/metricshub/connector/producer/ConnectorJsonNodeReader.java +++ b/src/main/java/org/sentrysoftware/maven/metricshub/connector/producer/ConnectorJsonNodeReader.java @@ -37,7 +37,6 @@ import java.util.stream.Collectors; import java.util.stream.Stream; import lombok.AllArgsConstructor; -import org.sentrysoftware.maven.metricshub.connector.Constants; import org.sentrysoftware.maven.metricshub.connector.producer.model.common.TechnologyType; /** @@ -85,7 +84,7 @@ public String getDisplayName() { .map(node -> node.get("displayName")) .filter(JsonNodeHelper::nonNull) .map(JsonNode::asText) - .orElse(Constants.EMPTY); + .orElse(""); } /** diff --git a/src/main/java/org/sentrysoftware/maven/metricshub/connector/producer/ConnectorPageReferenceProducer.java b/src/main/java/org/sentrysoftware/maven/metricshub/connector/producer/ConnectorPageReferenceProducer.java index 2d18e10..c0e0243 100644 --- a/src/main/java/org/sentrysoftware/maven/metricshub/connector/producer/ConnectorPageReferenceProducer.java +++ b/src/main/java/org/sentrysoftware/maven/metricshub/connector/producer/ConnectorPageReferenceProducer.java @@ -49,7 +49,6 @@ @Builder(setterPrefix = "with") public class ConnectorPageReferenceProducer { - private static final String BOOTSTRAP_MEDIUM_4_CLASS = "col-md-4"; private static final String WMI_SECTION = " wmi:\n"; private static final String PROTOCOLS_SECTION = " protocols:\n"; @@ -282,15 +281,15 @@ private void produceMetricsTable(final Sink sink, final ConnectorJsonNodeReader sink.table(); sink.tableRow(); - sink.tableHeaderCell(SinkHelper.setClass(BOOTSTRAP_MEDIUM_4_CLASS)); + sink.tableHeaderCell(SinkHelper.setClass("col-md-2")); sink.text("Type"); sink.tableHeaderCell_(); - sink.tableHeaderCell(SinkHelper.setClass(BOOTSTRAP_MEDIUM_4_CLASS)); + sink.tableHeaderCell(SinkHelper.setClass("col-md-6")); sink.text("Collected Metrics"); sink.tableHeaderCell_(); - sink.tableHeaderCell(SinkHelper.setClass(BOOTSTRAP_MEDIUM_4_CLASS)); + sink.tableHeaderCell(SinkHelper.setClass("col-md-4")); sink.text("Specific Attributes"); sink.tableHeaderCell_(); diff --git a/src/main/java/org/sentrysoftware/maven/metricshub/connector/producer/MainPageReferenceProducer.java b/src/main/java/org/sentrysoftware/maven/metricshub/connector/producer/MainPageReferenceProducer.java index b8cb935..cd78e2b 100644 --- a/src/main/java/org/sentrysoftware/maven/metricshub/connector/producer/MainPageReferenceProducer.java +++ b/src/main/java/org/sentrysoftware/maven/metricshub/connector/producer/MainPageReferenceProducer.java @@ -58,7 +58,7 @@ public void produce(final Sink mainSink, final Map connectors) mainSink.head(); mainSink.title(); - mainSink.text("Connector Library"); + mainSink.text("Connector Directory"); mainSink.title_(); mainSink.head_(); @@ -67,13 +67,13 @@ public void produce(final Sink mainSink, final Map connectors) // Title mainSink.section1(); mainSink.sectionTitle1(); - mainSink.text("Connector Library"); + mainSink.text("Connector Directory"); mainSink.sectionTitle1_(); // Intro mainSink.paragraph(); mainSink.text( - "This page lists the Connectors of ${project.name} ${project.version}." + + "This directory lists the Connectors of ${project.name} ${project.version}." + " Each page provides you with the details on each Connector, the targeted platform," + " the protocol used, the discovered components and monitored attributes." ); diff --git a/src/main/java/org/sentrysoftware/maven/metricshub/connector/producer/model/common/TechnologyType.java b/src/main/java/org/sentrysoftware/maven/metricshub/connector/producer/model/common/TechnologyType.java index 9a9612e..91e552a 100644 --- a/src/main/java/org/sentrysoftware/maven/metricshub/connector/producer/model/common/TechnologyType.java +++ b/src/main/java/org/sentrysoftware/maven/metricshub/connector/producer/model/common/TechnologyType.java @@ -56,9 +56,9 @@ public enum TechnologyType { WBEM("WBEM"), /** - * Windows Management Instrumentation (WMI) + * Windows Management Instrumentation (WMI) / Windows Remote Management (WinRM) */ - WMI("WMI"); + WMI("WMI/WinRM"); @Getter private String displayName; diff --git a/src/test/java/org/sentrysoftware/maven/metricshub/connector/parser/ConnectorLibraryParserTest.java b/src/test/java/org/sentrysoftware/maven/metricshub/connector/parser/ConnectorLibraryParserTest.java index 9ff43f3..897027f 100644 --- a/src/test/java/org/sentrysoftware/maven/metricshub/connector/parser/ConnectorLibraryParserTest.java +++ b/src/test/java/org/sentrysoftware/maven/metricshub/connector/parser/ConnectorLibraryParserTest.java @@ -3,15 +3,13 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ArrayNode; import java.io.IOException; import java.nio.file.Path; import java.util.Map; - import org.junit.jupiter.api.Test; -import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.node.ArrayNode; - class ConnectorLibraryParserTest { @Test From 733847e7804d2a2b7bca727044d6df3b043f5767 Mon Sep 17 00:00:00 2001 From: Nassim Boutekedjiret Date: Thu, 4 Jan 2024 11:03:41 +0100 Subject: [PATCH 05/13] [#4] Develop Connector Reference Report Goal Incorporated feedback from review: * Fixed `Connectors Directory`head and title. --- .../connector/producer/MainPageReferenceProducer.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/sentrysoftware/maven/metricshub/connector/producer/MainPageReferenceProducer.java b/src/main/java/org/sentrysoftware/maven/metricshub/connector/producer/MainPageReferenceProducer.java index cd78e2b..2c210ed 100644 --- a/src/main/java/org/sentrysoftware/maven/metricshub/connector/producer/MainPageReferenceProducer.java +++ b/src/main/java/org/sentrysoftware/maven/metricshub/connector/producer/MainPageReferenceProducer.java @@ -58,7 +58,7 @@ public void produce(final Sink mainSink, final Map connectors) mainSink.head(); mainSink.title(); - mainSink.text("Connector Directory"); + mainSink.text("Connectors Directory"); mainSink.title_(); mainSink.head_(); @@ -67,7 +67,7 @@ public void produce(final Sink mainSink, final Map connectors) // Title mainSink.section1(); mainSink.sectionTitle1(); - mainSink.text("Connector Directory"); + mainSink.text("Connectors Directory"); mainSink.sectionTitle1_(); // Intro From ec1dcec587d11a8f7e4ef24d88c73a331b5c5b70 Mon Sep 17 00:00:00 2001 From: maalej Date: Tue, 6 Feb 2024 15:11:45 +0100 Subject: [PATCH 06/13] feature/issue-14-update-constant-references * Update method processNode in ConstantsProcessor * Modify test file MIB2-header.yaml and use the new constants reference pattern --- .../metricshub/connector/parser/ConstantsProcessor.java | 7 ++++++- src/test/resources/connector/MIB2-header/MIB2-header.yaml | 6 +++--- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/src/main/java/org/sentrysoftware/maven/metricshub/connector/parser/ConstantsProcessor.java b/src/main/java/org/sentrysoftware/maven/metricshub/connector/parser/ConstantsProcessor.java index f1261eb..b23fe77 100644 --- a/src/main/java/org/sentrysoftware/maven/metricshub/connector/parser/ConstantsProcessor.java +++ b/src/main/java/org/sentrysoftware/maven/metricshub/connector/parser/ConstantsProcessor.java @@ -35,6 +35,11 @@ */ public class ConstantsProcessor extends AbstractNodeProcessor { + /** + * Constants Pattern + */ + private static final String CONSTANTS_REFERENCE_PATTERN = "${constant::%s}"; + /** * Constructs a ConstantsProcessor without a next processor. */ @@ -53,7 +58,7 @@ public JsonNode processNode(final JsonNode node) { final Map replacements = new HashMap<>(); for (String key : constantKeys) { final JsonNode child = constantsNode.get(key); - replacements.put(key, child.asText()); + replacements.put(String.format(CONSTANTS_REFERENCE_PATTERN, key), child.asText()); } final UnaryOperator updater = value -> performReplacements(replacements, value); diff --git a/src/test/resources/connector/MIB2-header/MIB2-header.yaml b/src/test/resources/connector/MIB2-header/MIB2-header.yaml index d6e0bed..cd63d28 100644 --- a/src/test/resources/connector/MIB2-header/MIB2-header.yaml +++ b/src/test/resources/connector/MIB2-header/MIB2-header.yaml @@ -13,7 +13,7 @@ connector: criteria: # Criteria(1): there must be something in the ifTable SNMP Table - type: snmpGetNext - oid: _OID + oid: ${constant::_OID} monitors: network: @@ -23,7 +23,7 @@ monitors: # Source(1) = ifTable SNMP Table # PortID;Description;TypeCode;MacAddress;AdminStatus; type: snmpTable - oid: _OID + oid: ${constant::_OID} selectColumns: "ID,2,3,6,7" computes: # Keep only ports whose administrative status is 'up' @@ -66,7 +66,7 @@ monitors: # Source(1) = ifTable SNMP Table # PortID;Description;Speed;OperationalStatus;ReceivedBytes;ReceivedUnicastPackets;ReceivedNonUnicastPackets;ReceivedErrors;TransmittedBytes;TransmittedUnicastPackets;TransmittedNonUnicastPackets;TransmittedErrors; type: snmpTable - oid: _OID + oid: ${constant::_OID} selectColumns: "ID,2,5,8,10,11,12,14,16,17,18,20" computes: # Duplicate Status twice From 5dbfe003ab7581ce0f6569a55573abc6f0f39feb Mon Sep 17 00:00:00 2001 From: CherfaElyes Date: Thu, 8 Feb 2024 16:12:13 +0100 Subject: [PATCH 07/13] Issue #15: Manage url and path in the HTTP Criterion parser * Updated HttpCriterion class to add getPath() method that retrieves path field value * Updated CriterionSinkProduceVisitor to manage different url usecases --- .../criteria/CriterionSinkProduceVisitor.java | 22 ++++++++++++++++++- .../model/criteria/HttpCriterion.java | 9 ++++++++ 2 files changed, 30 insertions(+), 1 deletion(-) diff --git a/src/main/java/org/sentrysoftware/maven/metricshub/connector/producer/model/criteria/CriterionSinkProduceVisitor.java b/src/main/java/org/sentrysoftware/maven/metricshub/connector/producer/model/criteria/CriterionSinkProduceVisitor.java index 72009b0..95e19d5 100644 --- a/src/main/java/org/sentrysoftware/maven/metricshub/connector/producer/model/criteria/CriterionSinkProduceVisitor.java +++ b/src/main/java/org/sentrysoftware/maven/metricshub/connector/producer/model/criteria/CriterionSinkProduceVisitor.java @@ -70,11 +70,31 @@ public void visit(HttpCriterion httpCriterion) { sink.text(" below to the managed host succeeds:"); sink.list(); sink.listItem(); + // Retrieve URL and Path Fields values + String urlField = httpCriterion.getUrl(); + String pathField = httpCriterion.getPath(); + // Initialize the final URL value + String url = ""; + // If both URL and Path fields aren't null, concatenate them + if(urlField != null && pathField != null) { + String.format( + "%s%s%s", + urlField, + urlField.endsWith("/") || pathField.startsWith("/") ? "" : "/", + urlField.endsWith("/") && pathField.startsWith("/") ? pathField.substring(1) : pathField + ); + // if Only URL field value is found, use it + } else if (urlField != null) { + url = urlField; + // if Only Path field value is found, use it + } else if (pathField != null) { + url = pathField; + } sink.rawText( String.format( "%s %s", httpCriterion.getMethodOrDefault("GET"), - SinkHelper.replaceWithHtmlCode(httpCriterion.getUrl()) + SinkHelper.replaceWithHtmlCode(url) ) ); sink.listItem_(); diff --git a/src/main/java/org/sentrysoftware/maven/metricshub/connector/producer/model/criteria/HttpCriterion.java b/src/main/java/org/sentrysoftware/maven/metricshub/connector/producer/model/criteria/HttpCriterion.java index 9d9bf71..fb90f18 100644 --- a/src/main/java/org/sentrysoftware/maven/metricshub/connector/producer/model/criteria/HttpCriterion.java +++ b/src/main/java/org/sentrysoftware/maven/metricshub/connector/producer/model/criteria/HttpCriterion.java @@ -65,6 +65,15 @@ public String getMethodOrDefault(final String defaultValue) { public String getUrl() { return nonNullTextOrDefault(criterion.get("url"), null); } + + /** + * Gets the Path from the criterion, or {@code null} if not present. + * + * @return The Path from the criterion, or {@code null} if not present. + */ + public String getPath() { + return nonNullTextOrDefault(criterion.get("path"), null); + } /** * Gets the header from the criterion, or {@code null} if not present. From 4a2c2b03519ead69179668cd486bde5b8699d804 Mon Sep 17 00:00:00 2001 From: CherfaElyes Date: Thu, 8 Feb 2024 16:44:41 +0100 Subject: [PATCH 08/13] Issue #15: Manage url and path in the HTTP Criterion parser * Reformat the code using prettier --- .../model/criteria/CriterionSinkProduceVisitor.java | 8 ++++---- .../connector/producer/model/criteria/HttpCriterion.java | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/main/java/org/sentrysoftware/maven/metricshub/connector/producer/model/criteria/CriterionSinkProduceVisitor.java b/src/main/java/org/sentrysoftware/maven/metricshub/connector/producer/model/criteria/CriterionSinkProduceVisitor.java index 95e19d5..fafac01 100644 --- a/src/main/java/org/sentrysoftware/maven/metricshub/connector/producer/model/criteria/CriterionSinkProduceVisitor.java +++ b/src/main/java/org/sentrysoftware/maven/metricshub/connector/producer/model/criteria/CriterionSinkProduceVisitor.java @@ -76,17 +76,17 @@ public void visit(HttpCriterion httpCriterion) { // Initialize the final URL value String url = ""; // If both URL and Path fields aren't null, concatenate them - if(urlField != null && pathField != null) { + if (urlField != null && pathField != null) { String.format( "%s%s%s", urlField, urlField.endsWith("/") || pathField.startsWith("/") ? "" : "/", urlField.endsWith("/") && pathField.startsWith("/") ? pathField.substring(1) : pathField - ); - // if Only URL field value is found, use it + ); + // if Only URL field value is found, use it } else if (urlField != null) { url = urlField; - // if Only Path field value is found, use it + // if Only Path field value is found, use it } else if (pathField != null) { url = pathField; } diff --git a/src/main/java/org/sentrysoftware/maven/metricshub/connector/producer/model/criteria/HttpCriterion.java b/src/main/java/org/sentrysoftware/maven/metricshub/connector/producer/model/criteria/HttpCriterion.java index fb90f18..acbb0d1 100644 --- a/src/main/java/org/sentrysoftware/maven/metricshub/connector/producer/model/criteria/HttpCriterion.java +++ b/src/main/java/org/sentrysoftware/maven/metricshub/connector/producer/model/criteria/HttpCriterion.java @@ -65,7 +65,7 @@ public String getMethodOrDefault(final String defaultValue) { public String getUrl() { return nonNullTextOrDefault(criterion.get("url"), null); } - + /** * Gets the Path from the criterion, or {@code null} if not present. * From 6db2686d8dac9ffd44fd2465787adae6f572b7d4 Mon Sep 17 00:00:00 2001 From: CherfaElyes Date: Fri, 1 Mar 2024 14:12:38 +0100 Subject: [PATCH 09/13] Issue #18: Correction Needed in CLI Command Examples * Updated connector forcing command. * Updated integration test --- src/it/metricshub-connectors/verify.groovy | 6 +++--- .../connector/producer/ConnectorPageReferenceProducer.java | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/it/metricshub-connectors/verify.groovy b/src/it/metricshub-connectors/verify.groovy index c40d106..f88ff5b 100644 --- a/src/it/metricshub-connectors/verify.groovy +++ b/src/it/metricshub-connectors/verify.groovy @@ -118,7 +118,7 @@ assert htmlText.indexOf("VMware ESXi - Virtual Machines (SNMP)") > -1 : "platfor // IpmiTool htmlText = new File(basedir, "target/site/connectors/ipmitool.html").text assert htmlText.indexOf("ipmi:") > -1 && htmlText.indexOf("wmi:") > -1 && htmlText.indexOf("ssh:") > -1 : "IpmiTool: Examples must list ipmi, wmi and ssh" -assert htmlText =~ /metricshub.*-t management.*-f IpmiTool.*--ipmi/ : "IpmiTool: CLI must specify -t management -f IpmiTool --ipmi" +assert htmlText =~ /metricshub.*-t management.*-c \+IpmiTool.*--ipmi/ : "IpmiTool: CLI must specify -t management -c +IpmiTool --ipmi" // IpmiTool Enclosure assert htmlText.indexOf('enclosure') > -1 : 'IpmiTool: the enclosure monitor must be listed' @@ -204,7 +204,7 @@ htmlText = new File(basedir, "target/site/connectors/wbemgennetwork.html").text assert htmlText.indexOf("This connector is superseded by") > - 1 : "WBEMGenNetwork: Page must indicate this connector is superseded by another one" assert htmlText.indexOf('href="mib2nt.html"') > -1 : "WBEMGenNetwork: Path to superseding connector page must be present" assert htmlText.indexOf("wmi:") > -1 : "WBEMGenNetwork: Example must list wmi" -assert htmlText =~ /metricshub.*-f WBEMGenNetwork.*--wmi/ : "WBEMGetNetwork: CLI must specify WBEMGenNetwork and --wmi" +assert htmlText =~ /metricshub.*-c \+WBEMGenNetwork.*--wmi/ : "WBEMGetNetwork: CLI must specify WBEMGenNetwork and --wmi" // lmsensors htmlText = new File(basedir, "target/site/connectors/lmsensors.html").text @@ -231,7 +231,7 @@ assert htmlText.indexOf("Technology and protocols:") > - 1 : "MIB2 'Technology a assert htmlText.indexOf("SNMP") > - 1 : "MIB2 SNMP protocol must be present" assert htmlText.indexOf('

    Examples

    ') > - 1 : "MIB2: Page must indicate 'Examples' as HTML H3 element" assert htmlText.indexOf('

    CLI

    ') > - 1 : "MIB2: Page must indicate 'CLI' as HTML H4 element" -assert htmlText.indexOf("metricshub HOSTNAME -t management -f MIB2 --snmp v2c --community public") > - 1 : "MIB2: Page must indicate the expected CLI example" +assert htmlText.indexOf("metricshub HOSTNAME -t management -c +MIB2 --snmp v2c --community public") > - 1 : "MIB2: Page must indicate the expected CLI example" assert htmlText.indexOf('

    metricshub.yaml

    ') > - 1 : "MIB2: Page must indicate 'metricshub.yaml' as HTML H4 element" assert htmlText.indexOf("snmp:") > - 1 : "MIB2: 'snmp:' yaml section must be present" assert htmlText.indexOf("v2c") > - 1 : "MIB2: version 'v2c' must be present in the yaml configuration example" diff --git a/src/main/java/org/sentrysoftware/maven/metricshub/connector/producer/ConnectorPageReferenceProducer.java b/src/main/java/org/sentrysoftware/maven/metricshub/connector/producer/ConnectorPageReferenceProducer.java index c0e0243..888a35a 100644 --- a/src/main/java/org/sentrysoftware/maven/metricshub/connector/producer/ConnectorPageReferenceProducer.java +++ b/src/main/java/org/sentrysoftware/maven/metricshub/connector/producer/ConnectorPageReferenceProducer.java @@ -389,7 +389,7 @@ private void produceMetricsHubExamplesContent( // Build YAML config and CLI final StringBuilder cli = new StringBuilder("metricshub HOSTNAME -t ").append(hostType); - cli.append(" -f ").append(connectorId); + cli.append(" -c +").append(connectorId); final StringBuilder yamlBuilder = new StringBuilder( "resourceGroups:\n :\n resources:\n :\n attributes:\n" ); From d9658dcd8bfe2eaa4f86127692482bbed33c54e9 Mon Sep 17 00:00:00 2001 From: GitHub Actions Date: Tue, 19 Mar 2024 12:41:05 +0000 Subject: [PATCH 10/13] [maven-release-plugin] prepare release v1.0.00 --- pom.xml | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/pom.xml b/pom.xml index 4931db7..c99b5ae 100644 --- a/pom.xml +++ b/pom.xml @@ -1,12 +1,10 @@ - + 4.0.0 org.sentrysoftware.maven metricshub-connector-maven-plugin MetricsHub Connector Maven Plugin - 1.0.00-SNAPSHOT + 1.0.00 The MetricsHub Connector Maven Plugin is a Maven report plugin that helps generate documentation for MetricsHub connectors. @@ -53,7 +51,7 @@ scm:git:https://github.com/sentrysoftware/metricshub-connector-maven-plugin.git https://github.com/sentrysoftware/metricshub-connector-maven-plugin - HEAD + v1.0.00 @@ -84,7 +82,7 @@ - 2023-10-25T22:28:48Z + 2024-03-19T12:39:39Z From a7a3b98b3130ebb3ea614003db042c5728d18d2d Mon Sep 17 00:00:00 2001 From: GitHub Actions Date: Tue, 19 Mar 2024 12:41:07 +0000 Subject: [PATCH 11/13] [maven-release-plugin] prepare for next development iteration --- pom.xml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pom.xml b/pom.xml index c99b5ae..5b56506 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ org.sentrysoftware.maven metricshub-connector-maven-plugin MetricsHub Connector Maven Plugin - 1.0.00 + 1.0.01-SNAPSHOT The MetricsHub Connector Maven Plugin is a Maven report plugin that helps generate documentation for MetricsHub connectors. @@ -51,7 +51,7 @@ scm:git:https://github.com/sentrysoftware/metricshub-connector-maven-plugin.git https://github.com/sentrysoftware/metricshub-connector-maven-plugin - v1.0.00 + HEAD @@ -82,7 +82,7 @@ - 2024-03-19T12:39:39Z + 2024-03-19T12:41:07Z From 345231391362d082af37693579fc0eba0d80dd24 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 19 Mar 2024 17:11:55 +0000 Subject: [PATCH 12/13] Bump org.apache.maven.plugins:maven-compiler-plugin Bumps [org.apache.maven.plugins:maven-compiler-plugin](https://github.com/apache/maven-compiler-plugin) from 3.12.0 to 3.13.0. - [Release notes](https://github.com/apache/maven-compiler-plugin/releases) - [Commits](https://github.com/apache/maven-compiler-plugin/compare/maven-compiler-plugin-3.12.0...maven-compiler-plugin-3.13.0) --- updated-dependencies: - dependency-name: org.apache.maven.plugins:maven-compiler-plugin dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 5b56506..1bc2e78 100644 --- a/pom.xml +++ b/pom.xml @@ -221,7 +221,7 @@ maven-compiler-plugin - 3.12.0 + 3.13.0 From a8028621757c30b9aa47f37d383f750e7d074563 Mon Sep 17 00:00:00 2001 From: Nassim Boutekedjiret Date: Tue, 16 Apr 2024 17:12:02 +0200 Subject: [PATCH 13/13] Issue #27: Add Support for CommandLine Criteria and Sources * Renamed `OsCommandCriterion` to `CommandLineCriterion`. * Updated `CriterionFactory` to handle `commandLine` criteria. * Changed `TechnologyType` value from `OS_COMMAND` to `COMMAND_LINES`. * Rebranded `System Commands` as `Command Lines`. * Fixed missing `url` in `CriterionSinkProduceVisitor`. * Corrected JavaDoc. * Modified IT tests input file `NvidiaSmi.yaml`. * Updated IT tests to check the newly generated command line technology. --- .../hardware/NvidiaSmi/NvidiaSmi.yaml | 12 +++++------ src/it/metricshub-connectors/verify.groovy | 9 ++++++++ .../ConnectorPageReferenceProducer.java | 2 +- .../producer/model/common/TechnologyType.java | 9 ++++---- ...iterion.java => CommandLineCriterion.java} | 10 ++++----- .../model/criteria/CriterionFactory.java | 15 ++++++------- .../criteria/CriterionSinkProduceVisitor.java | 21 ++++++++++--------- .../model/criteria/ICriterionVisitor.java | 6 +++--- 8 files changed, 48 insertions(+), 36 deletions(-) rename src/main/java/org/sentrysoftware/maven/metricshub/connector/producer/model/criteria/{OsCommandCriterion.java => CommandLineCriterion.java} (88%) diff --git a/src/it/metricshub-connectors/src/main/connector/hardware/NvidiaSmi/NvidiaSmi.yaml b/src/it/metricshub-connectors/src/main/connector/hardware/NvidiaSmi/NvidiaSmi.yaml index ad22673..30b2339 100644 --- a/src/it/metricshub-connectors/src/main/connector/hardware/NvidiaSmi/NvidiaSmi.yaml +++ b/src/it/metricshub-connectors/src/main/connector/hardware/NvidiaSmi/NvidiaSmi.yaml @@ -14,7 +14,7 @@ connector: - NT - Linux criteria: - - type: osCommand + - type: commandLine commandLine: nvidia-smi expectedResult: Driver Version errorMessage: nvidia-smi not found at target host. @@ -23,7 +23,7 @@ monitors: discovery: sources: source(1): - type: osCommand + type: commandLine commandLine: nvidia-smi -q timeout: 90 computes: @@ -62,7 +62,7 @@ monitors: - id sources: source(1): - type: osCommand + type: commandLine commandLine: nvidia-smi -q timeout: 90 computes: @@ -111,7 +111,7 @@ monitors: - id sources: source(1): - type: osCommand + type: commandLine commandLine: nvidia-smi -q timeout: 90 computes: @@ -150,7 +150,7 @@ monitors: - id sources: source(1): - type: osCommand + type: commandLine commandLine: nvidia-smi -q timeout: 90 computes: @@ -192,7 +192,7 @@ monitors: - id sources: source(1): - type: osCommand + type: commandLine commandLine: nvidia-smi -q timeout: 90 computes: diff --git a/src/it/metricshub-connectors/verify.groovy b/src/it/metricshub-connectors/verify.groovy index f88ff5b..9279b80 100644 --- a/src/it/metricshub-connectors/verify.groovy +++ b/src/it/metricshub-connectors/verify.groovy @@ -114,6 +114,7 @@ assert htmlText.indexOf("VMware ESXi - Disks (VMware)") > -1 : "platform-require assert htmlText.indexOf("VMware ESXi 4.x") > -1 : "platform-requirements: 'VMware ESXi 4.x' must be listed" assert htmlText.indexOf("VMware ESXi - Disks (IPMI)") > -1 : "platform-requirements: 'VMware ESXi - Disks (IPMI)' must be listed" assert htmlText.indexOf("VMware ESXi - Virtual Machines (SNMP)") > -1 : "platform-requirements: 'VMware ESXi - Virtual Machines (SNMP)' must be listed" +assert htmlText.indexOf("Command Lines") > -1 : "platform-requirements: 'Command Lines' technology must be listed" // IpmiTool htmlText = new File(basedir, "target/site/connectors/ipmitool.html").text @@ -257,3 +258,11 @@ assert htmlText.indexOf('id') > -1 : "MIB2: the 'id' attribute must be listed fo assert htmlText.indexOf('name') > -1 : "MIB2: the 'name' attribute must be listed for the Network monitor" assert htmlText.indexOf('physical_address') > -1 : "MIB2: the 'physical_address' attribute must be listed for the Network monitor" assert htmlText.indexOf('physical_address_type') > -1 : "MIB2: the 'physical_address_type' attribute must be listed for the Network monitor" + +// Nvidia-Smi +htmlText = new File(basedir, "target/site/connectors/nvidiasmi.html").text +assert htmlText.indexOf("Any system with Nvidia GPUs") > -1 : "NvidiaSmi: Unexpected Typical platform" +assert htmlText.indexOf("Microsoft Windows, Linux") > -1 : "NvidiaSmi: Unexpected Operating Systems" +assert htmlText.indexOf("NVIDIA drivers with NVIDIA-SMI support") > -1 : "NvidiaSmi: Unexpected Leverages" +assert htmlText.indexOf("Command Lines") > -1 : "NvidiaSmi: Unexpected Technology and protocols" +assert htmlText.indexOf("nvidia-smi") > -1 : "NvidiaSmi: Unexpected criterion command line" \ No newline at end of file diff --git a/src/main/java/org/sentrysoftware/maven/metricshub/connector/producer/ConnectorPageReferenceProducer.java b/src/main/java/org/sentrysoftware/maven/metricshub/connector/producer/ConnectorPageReferenceProducer.java index 888a35a..d7edc95 100644 --- a/src/main/java/org/sentrysoftware/maven/metricshub/connector/producer/ConnectorPageReferenceProducer.java +++ b/src/main/java/org/sentrysoftware/maven/metricshub/connector/producer/ConnectorPageReferenceProducer.java @@ -437,7 +437,7 @@ private void produceMetricsHubExamplesContent( yamlBuilder.append(" community: public # or probably something more secure"); } - if (technologies.contains(TechnologyType.OS_COMMAND)) { + if (technologies.contains(TechnologyType.COMMAND_LINES)) { if (OsType.WINDOWS.getPossibleHostType().equals(hostType)) { cli.append(" --wmi -u USER"); yamlBuilder.append(WMI_SECTION); diff --git a/src/main/java/org/sentrysoftware/maven/metricshub/connector/producer/model/common/TechnologyType.java b/src/main/java/org/sentrysoftware/maven/metricshub/connector/producer/model/common/TechnologyType.java index 91e552a..1d34fb1 100644 --- a/src/main/java/org/sentrysoftware/maven/metricshub/connector/producer/model/common/TechnologyType.java +++ b/src/main/java/org/sentrysoftware/maven/metricshub/connector/producer/model/common/TechnologyType.java @@ -26,7 +26,7 @@ import lombok.Getter; /** - * Enumeration representing different technology types such as HTTP, IPMI, SNMP, WMI, WBEM, System Commands, etc. + * Enumeration representing different technology types such as HTTP, IPMI, SNMP, WMI, WBEM, Command Lines, etc. */ @AllArgsConstructor public enum TechnologyType { @@ -41,9 +41,9 @@ public enum TechnologyType { IPMI("IPMI"), /** - * System Commands + * Command Lines */ - OS_COMMAND("System Commands"), + COMMAND_LINES("Command Lines"), /** * Simple Network Management Protocol (SNMP) @@ -75,7 +75,8 @@ public enum TechnologyType { private static final Map TECHNOLOGY_TYPE_MAP = Map.of( "http", HTTP, "ipmi", IPMI, - "oscommand", OS_COMMAND, + "oscommand", COMMAND_LINES, + "commandline", COMMAND_LINES, "snmptable", SNMP, "snmpget", SNMP, "wbem", WBEM, diff --git a/src/main/java/org/sentrysoftware/maven/metricshub/connector/producer/model/criteria/OsCommandCriterion.java b/src/main/java/org/sentrysoftware/maven/metricshub/connector/producer/model/criteria/CommandLineCriterion.java similarity index 88% rename from src/main/java/org/sentrysoftware/maven/metricshub/connector/producer/model/criteria/OsCommandCriterion.java rename to src/main/java/org/sentrysoftware/maven/metricshub/connector/producer/model/criteria/CommandLineCriterion.java index 6551ffc..04f835d 100644 --- a/src/main/java/org/sentrysoftware/maven/metricshub/connector/producer/model/criteria/OsCommandCriterion.java +++ b/src/main/java/org/sentrysoftware/maven/metricshub/connector/producer/model/criteria/CommandLineCriterion.java @@ -27,19 +27,19 @@ import lombok.Builder; /** - * Represents a criterion for filtering based on an OS Command. + * Represents a criterion for filtering based on a command line. * * @see AbstractCriterion */ -public class OsCommandCriterion extends AbstractCriterion { +public class CommandLineCriterion extends AbstractCriterion { /** - * Constructs OsCommandCriterion with the specified JSON criterion. + * Constructs CommandLineCriterion with the specified JSON criterion. * - * @param criterion The JSON criterion for OS Command. + * @param criterion The JSON criterion for command line. */ @Builder - public OsCommandCriterion(final JsonNode criterion) { + public CommandLineCriterion(final JsonNode criterion) { super(criterion); } diff --git a/src/main/java/org/sentrysoftware/maven/metricshub/connector/producer/model/criteria/CriterionFactory.java b/src/main/java/org/sentrysoftware/maven/metricshub/connector/producer/model/criteria/CriterionFactory.java index c950473..3db868e 100644 --- a/src/main/java/org/sentrysoftware/maven/metricshub/connector/producer/model/criteria/CriterionFactory.java +++ b/src/main/java/org/sentrysoftware/maven/metricshub/connector/producer/model/criteria/CriterionFactory.java @@ -55,7 +55,8 @@ public class CriterionFactory { map.put("devicetype", CriterionFactory::newDeviceTypeCriterion); map.put("http", CriterionFactory::newHttpCriterion); map.put("ipmi", CriterionFactory::newIpmiCriterion); - map.put("oscommand", CriterionFactory::newOsCommandCriterion); + map.put("oscommand", CriterionFactory::newCommandLineCriterion); + map.put("commandline", CriterionFactory::newCommandLineCriterion); map.put("process", CriterionFactory::newProcessCriterion); map.put("productrequirements", CriterionFactory::newProductRequirementsCriterion); map.put("service", CriterionFactory::newServiceCriterion); @@ -98,13 +99,13 @@ private static IpmiCriterion newIpmiCriterion(final JsonNode node) { } /** - * Creates a new {@link OsCommandCriterion} instance based on the provided {@link JsonNode}. + * Creates a new {@link CommandLineCriterion} instance based on the provided {@link JsonNode}. * * @param node The {@link JsonNode} containing criterion configuration. - * @return A new {@link OsCommandCriterion} instance. + * @return A new {@link CommandLineCriterion} instance. */ - private static AbstractCriterion newOsCommandCriterion(final JsonNode node) { - return new OsCommandCriterion(node); + private static CommandLineCriterion newCommandLineCriterion(final JsonNode node) { + return new CommandLineCriterion(node); } /** @@ -143,7 +144,7 @@ private static ServiceCriterion newServiceCriterion(final JsonNode node) { * @param node The {@link JsonNode} containing criterion configuration. * @return A new {@link SnmpGetCriterion} instance. */ - private static AbstractSnmpCriterion newSnmpGetCriterion(final JsonNode node) { + private static SnmpGetCriterion newSnmpGetCriterion(final JsonNode node) { return new SnmpGetCriterion(node); } @@ -163,7 +164,7 @@ private static SnmpGetNextCriterion newSnmpGetNextCriterion(final JsonNode node) * @param node The {@link JsonNode} containing criterion configuration. * @return A new {@link WbemCriterion} instance. */ - private static AbstractWqlCriterion newWbemCriterion(final JsonNode node) { + private static WbemCriterion newWbemCriterion(final JsonNode node) { return new WbemCriterion(node); } diff --git a/src/main/java/org/sentrysoftware/maven/metricshub/connector/producer/model/criteria/CriterionSinkProduceVisitor.java b/src/main/java/org/sentrysoftware/maven/metricshub/connector/producer/model/criteria/CriterionSinkProduceVisitor.java index fafac01..c0f411b 100644 --- a/src/main/java/org/sentrysoftware/maven/metricshub/connector/producer/model/criteria/CriterionSinkProduceVisitor.java +++ b/src/main/java/org/sentrysoftware/maven/metricshub/connector/producer/model/criteria/CriterionSinkProduceVisitor.java @@ -77,12 +77,13 @@ public void visit(HttpCriterion httpCriterion) { String url = ""; // If both URL and Path fields aren't null, concatenate them if (urlField != null && pathField != null) { - String.format( - "%s%s%s", - urlField, - urlField.endsWith("/") || pathField.startsWith("/") ? "" : "/", - urlField.endsWith("/") && pathField.startsWith("/") ? pathField.substring(1) : pathField - ); + url = + String.format( + "%s%s%s", + urlField, + urlField.endsWith("/") || pathField.startsWith("/") ? "" : "/", + urlField.endsWith("/") && pathField.startsWith("/") ? pathField.substring(1) : pathField + ); // if Only URL field value is found, use it } else if (urlField != null) { url = urlField; @@ -159,16 +160,16 @@ public void visit(IpmiCriterion ipmiCriterion) { } @Override - public void visit(OsCommandCriterion osCommandCriterion) { + public void visit(CommandLineCriterion commandLineCriterion) { // Command Line - String commandLine = osCommandCriterion.getCommandLineOrDefault("N/A"); + String commandLine = commandLineCriterion.getCommandLineOrDefault("N/A"); // Remove mentions to sudo commandLine = commandLine.replaceAll("%\\{SUDO:[a-zA-Z\\d/\\-_]+\\}", ""); sink.listItem(); sink.text("The command below succeeds on the "); - if (osCommandCriterion.isExecuteLocallyOrDefault(false)) { + if (commandLineCriterion.isExecuteLocallyOrDefault(false)) { sink.bold(); sink.text("agent host"); sink.bold_(); @@ -179,7 +180,7 @@ public void visit(OsCommandCriterion osCommandCriterion) { sink.listItem(); sink.rawText(String.format("Command: %s", SinkHelper.replaceWithHtmlCode(commandLine))); sink.listItem_(); - final String expectedResult = osCommandCriterion.getExpectedResult(); + final String expectedResult = commandLineCriterion.getExpectedResult(); if (expectedResult != null) { sink.listItem(); sink.rawText( diff --git a/src/main/java/org/sentrysoftware/maven/metricshub/connector/producer/model/criteria/ICriterionVisitor.java b/src/main/java/org/sentrysoftware/maven/metricshub/connector/producer/model/criteria/ICriterionVisitor.java index b6574a7..9e02765 100644 --- a/src/main/java/org/sentrysoftware/maven/metricshub/connector/producer/model/criteria/ICriterionVisitor.java +++ b/src/main/java/org/sentrysoftware/maven/metricshub/connector/producer/model/criteria/ICriterionVisitor.java @@ -50,11 +50,11 @@ public interface ICriterionVisitor { void visit(IpmiCriterion ipmiCriterion); /** - * Visits the specified OS Command criterion. + * Visits the specified Command Line criterion. * - * @param osCommandCriterion The OS Command criterion to visit. + * @param commandLineCriterion The Command Line criterion to visit. */ - void visit(OsCommandCriterion osCommandCriterion); + void visit(CommandLineCriterion commandLineCriterion); /** * Visits the specified Process criterion.