Merged in development (pull request #2)

Development
This commit is contained in:
Mark Nellemann 2021-06-11 00:23:31 +00:00
commit 84a1848eb2
72 changed files with 3178 additions and 1374 deletions

View file

@ -2,17 +2,20 @@
Java based system monitoring solution with support for plugins. Java based system monitoring solution with support for plugins.
- Example Grafana [dashboard](https://bitbucket.org/mnellemann/sysmon/downloads/sysmon-example-dashboard.png) showing metrics from a host running *sysmon*.
## Client ## Components
Runs on your hosts and collects metrics. Metrics are aggregated and sent to the central *server*. ### Client
Runs on your hosts and collects metrics, which are sent to the central *server*.
## Server ### Server
Receives aggregated measurements from clients and saves metrics into InfluxDB. Receives aggregated metrics from clients and saves these into InfluxDB.
## Plugins ### Plugins
Loaded by the client and provides extensions for doing the actual metric monitoring. Loaded by the client and provides extensions for doing the actual collecting of metrics.

View file

@ -19,6 +19,7 @@ subprojects {
implementation "org.slf4j:slf4j-api:${slf4jVersion}" implementation "org.slf4j:slf4j-api:${slf4jVersion}"
implementation "org.slf4j:slf4j-simple:${slf4jVersion}" implementation "org.slf4j:slf4j-simple:${slf4jVersion}"
} }
repositories { repositories {

View file

@ -19,6 +19,10 @@ dependencies {
implementation "info.picocli:picocli:${picocliVersion}" implementation "info.picocli:picocli:${picocliVersion}"
implementation 'org.tomlj:tomlj:1.0.0' implementation 'org.tomlj:tomlj:1.0.0'
implementation(group: 'com.github.oshi', name: 'oshi-core', version: "5.7.3") {
exclude(group: "org.slf4j")
}
implementation group: 'org.apache.camel', name: 'camel-core', version: camelVersion implementation group: 'org.apache.camel', name: 'camel-core', version: camelVersion
implementation group: 'org.apache.camel', name: 'camel-main', version: camelVersion implementation group: 'org.apache.camel', name: 'camel-main', version: camelVersion
implementation group: 'org.apache.camel', name: 'camel-http', version: camelVersion implementation group: 'org.apache.camel', name: 'camel-http', version: camelVersion

View file

@ -21,7 +21,7 @@ public class ClientRouteBuilder extends RouteBuilder {
private static final Logger log = LoggerFactory.getLogger(ClientRouteBuilder.class); private static final Logger log = LoggerFactory.getLogger(ClientRouteBuilder.class);
@Override @Override
public void configure() throws Exception { public void configure() {
Registry registry = getContext().getRegistry(); Registry registry = getContext().getRegistry();
@ -45,24 +45,30 @@ public class ClientRouteBuilder extends RouteBuilder {
log.info(">>> Enabling extension: " + ext.getDescription()); log.info(">>> Enabling extension: " + ext.getDescription());
providers.add(provides); providers.add(provides);
// TODO: Make timer thread configurable
// Setup Camel route for this extension // Setup Camel route for this extension
// a unique timer name gives the timer it's own thread, otherwise it's a shared thread for other timers with same name. // a unique timer name gives the timer it's own thread, otherwise it's a shared thread for other timers with same name.
from("timer:"+provides+"?fixedRate=true&period=30s") //from("timer:"+provides+"?fixedRate=true&period=30s")
from("timer:extensions?fixedRate=true&period=30s")
.bean(ext, "getMetrics") .bean(ext, "getMetrics")
//.doTry() //.doTry()
.process(new MetricEnrichProcessor(registry)) .process(new MetricEnrichProcessor(registry))
.choice().when(exchangeProperty("skip").isEqualTo(true)) .choice().when(exchangeProperty("skip").isEqualTo(true))
.log("Skipping empty: ${body}") .log("Skipping empty measurement.")
.stop() .stop()
.otherwise() .otherwise()
.to("seda:metrics"); .to("seda:metrics?discardWhenFull=true");
} else { } else {
log.info(">>> Skipping extension: " + ext.getDescription()); log.info(">>> Skipping extension (not supported here): " + ext.getDescription());
} }
} }
from("seda:metrics") // TODO: Make 'concurrentConsumers' configurable
from("seda:metrics?concurrentConsumers=1")
.setHeader(Exchange.HTTP_METHOD, constant("POST")) .setHeader(Exchange.HTTP_METHOD, constant("POST"))
//.setHeader(Exchange.CONTENT_TYPE, constant("application/json")) //.setHeader(Exchange.CONTENT_TYPE, constant("application/json"))
.doTry() .doTry()

21
doc/AIX.md Normal file
View file

@ -0,0 +1,21 @@
# AIX Notes
Works on IBM Power VIO (Virtual IO) servers, as well as regular IBM Power AIX installations.
## Installation
We require Java 8, which should already be installed.
The RPM packages are *"noarch"* Java bytecode, so we can use the **--ignoreos** option to install:
```shell
rpm -i --ignoreos sysmon-client.rpm sysmon-plugins.rpm
```
## Run automatically at boot
Change the *sysmon-server* URL for your environment.
```shell
mkitab 'sysmon:2:respawn:env JAVA_HOME=/usr/java8_64 /opt/sysmon/client/bin/client -s http://10.20.30.40:9925/metrics >/tmp/sysmon.log 2>&1'
init q
```

File diff suppressed because it is too large Load diff

10
doc/sysmon-client.service Normal file
View file

@ -0,0 +1,10 @@
[Unit]
Description=Sysmon Client Service
[Service]
TimeoutStartSec=0
Restart=always
ExecStart=/opt/sysmon/client/bin/client -s http://10.20.30.40:9925/metrics
[Install]
WantedBy=default.target

10
doc/sysmon-server.service Normal file
View file

@ -0,0 +1,10 @@
[Unit]
Description=Sysmon Server Service
[Service]
TimeoutStartSec=0
Restart=always
ExecStart=/opt/sysmon/server/bin/server
[Install]
WantedBy=default.target

12
doc/systemd.md Normal file
View file

@ -0,0 +1,12 @@
# SystemD Notes
Edit the *sysmon-client.service* file and change the sysmon-server URL accordingly to your environment.
Setup as systemd service to start automatically at boot:
```shell
cp sysmon-client.service /etc/systemd/system/
systemctl daemon-reload
systemctl enable sysmon-client
systemctl restart sysmon-client
```

View file

@ -1,4 +1,4 @@
version=0.0.2 version=0.0.3
pf4jVersion=3.6.0 pf4jVersion=3.6.0
slf4jVersion=1.7.30 slf4jVersion=1.7.30
camelVersion=3.7.4 camelVersion=3.7.4

View file

@ -12,6 +12,7 @@ subprojects {
dependencies { dependencies {
testImplementation 'org.spockframework:spock-core:2.0-groovy-3.0' testImplementation 'org.spockframework:spock-core:2.0-groovy-3.0'
testImplementation "org.slf4j:slf4j-api:${slf4jVersion}" testImplementation "org.slf4j:slf4j-api:${slf4jVersion}"
testImplementation "org.slf4j:slf4j-simple:${slf4jVersion}"
testImplementation project(':shared') testImplementation project(':shared')
implementation project(':shared') implementation project(':shared')
@ -20,22 +21,13 @@ subprojects {
exclude(group: "org.slf4j") exclude(group: "org.slf4j")
} }
annotationProcessor(group: 'org.pf4j', name: 'pf4j', version: "${pf4jVersion}") annotationProcessor(group: 'org.pf4j', name: 'pf4j', version: "${pf4jVersion}")
implementation(group: 'com.github.oshi', name: 'oshi-core', version: "5.7.3") {
exclude(group: "org.slf4j")
} }
/*
jar {
manifest {
attributes(
'Plugin-Id' : "${pluginId}",
'Plugin-Class' : "${pluginClass}",
'Plugin-Version' : "${pluginVersion}",
'Plugin-Provider' : "${pluginProvider}",
'Plugin-Description': "${pluginDescription}"
)
} }
}*/
task uberJar(type: Jar) { task uberJar(type: Jar) {
from sourceSets.main.output from sourceSets.main.output
dependsOn configurations.runtimeClasspath dependsOn configurations.runtimeClasspath
@ -56,8 +48,8 @@ subprojects {
attributes( attributes(
'Plugin-Id' : "${pluginId}", 'Plugin-Id' : "${pluginId}",
'Plugin-Class' : "${pluginClass}", 'Plugin-Class' : "${pluginClass}",
'Plugin-Version' : "${pluginVersion}", 'Plugin-Version' : "${version}",
'Plugin-Provider' : "${pluginProvider}", 'Plugin-Provider' : "System Monitor",
'Plugin-Description': "${pluginDescription}" 'Plugin-Description': "${pluginDescription}"
) )
} }

View file

@ -1,6 +1,3 @@
dependencies { dependencies {
implementation(group: 'com.github.oshi', name: 'oshi-core', version: "5.7.3") {
exclude(group: "org.slf4j")
}
} }

View file

@ -1,91 +0,0 @@
package sysmon.plugins.os_aix;
import org.pf4j.Extension;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import oshi.SystemInfo;
import oshi.hardware.HardwareAbstractionLayer;
import sysmon.shared.Measurement;
import sysmon.shared.MetricExtension;
import sysmon.shared.MetricResult;
import sysmon.shared.PluginHelper;
import java.util.List;
import java.util.Map;
@Extension
public class AixDiskExtension implements MetricExtension {
private static final Logger log = LoggerFactory.getLogger(AixProcessorExtension.class);
private final SystemInfo systemInfo;
private final HardwareAbstractionLayer hardwareAbstractionLayer;
@Override
public boolean isSupported() {
if(!System.getProperty("os.name").toLowerCase().contains("aix")) {
log.warn("Requires AIX.");
return false;
}
if(!PluginHelper.canExecute("iostat")) {
log.warn("Requires the 'iostat' command.");
return false;
}
return true;
}
public AixDiskExtension() {
systemInfo = new SystemInfo();
hardwareAbstractionLayer = systemInfo.getHardware();
log.warn(systemInfo.getOperatingSystem().toString());
}
@Override
public String getName() {
return "aix-disk";
}
@Override
public String getProvides() {
return "disk";
}
@Override
public String getDescription() {
return "AIX Disk Metrics";
}
@Override
public MetricResult getMetrics() {
long writeBytes = hardwareAbstractionLayer.getDiskStores().get(0).getWriteBytes();
log.warn(String.format("Disk 0 - Write Bytes: %d", writeBytes));
long readBytes = hardwareAbstractionLayer.getDiskStores().get(0).getReadBytes();
log.warn(String.format("Disk 0 - Read Bytes: %d", readBytes));
long memAvailable = hardwareAbstractionLayer.getMemory().getAvailable();
log.warn(String.format("Memory - Available: %d", memAvailable));
List<String> iostat = PluginHelper.executeCommand("iostat -d 1 1");
AixDiskStat diskStat = processCommandOutput(iostat);
Map<String, String> tagsMap = diskStat.getTags();
Map<String, Object> fieldsMap = diskStat.getFields();
return new MetricResult("disk", new Measurement(tagsMap, fieldsMap));
}
protected AixDiskStat processCommandOutput(List<String> inputLines) {
return new AixDiskStat(inputLines);
}
}

View file

@ -1,53 +0,0 @@
package sysmon.plugins.os_aix;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class AixDiskStat {
// Disks: % tm_act Kbps tps Kb_read Kb_wrtn
// hdisk0 1.0 752.0 81.0 740 12
private final Pattern pattern = Pattern.compile("^(hdisk\\d+)\\s+(\\d+\\.?\\d*)\\s+\\s+(\\d+\\.?\\d*)\\s+\\s+(\\d+\\.?\\d*)\\s+(\\d+)\\s+(\\d+)");
//private String device;
//private Float tmAct = 0.0f; // Indicates the percentage of time the physical disk/tape was active (bandwidth utilization for the drive).
private float kbps = 0.0f; // Indicates the amount of data transferred (read or written) to the drive in KB per second.
private float tps = 0.0f; // Indicates the number of transfers per second that were issued to the physical disk/tape. A transfer is an I/O request to the physical disk/tape. Multiple logical requests can be combined into a single I/O request to the disk. A transfer is of indeterminate size.
private long kbRead = 0L; // The total number of KB read.
private long kbWritten = 0L; // The total number of KB written.
AixDiskStat(List<String> lines) {
for (String line : lines) {
if (line.startsWith("hdisk")) {
Matcher matcher = pattern.matcher(line);
if (matcher.find() && matcher.groupCount() == 6) {
//device = matcher.group(1);
//tmAct = Float.parseFloat(matcher.group(2));
kbps += Float.parseFloat(matcher.group(3));
tps += Float.parseFloat(matcher.group(4));
kbRead += Long.parseLong(matcher.group(5));
kbWritten += Long.parseLong(matcher.group(6));
}
}
}
}
public Map<String, String> getTags() {
return new HashMap<>();
}
public Map<String, Object> getFields() {
Map<String, Object> fields = new HashMap<>();
fields.put("reads", kbRead * 1024); // from Kb to bytes
fields.put("writes", kbWritten * 1024); // from Kb to bytes
fields.put("kbps", (int) kbps);
fields.put("tps", (int) tps);
return fields;
}
}

View file

@ -1,68 +0,0 @@
package sysmon.plugins.os_aix;
import org.pf4j.Extension;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import sysmon.shared.Measurement;
import sysmon.shared.MetricExtension;
import sysmon.shared.MetricResult;
import sysmon.shared.PluginHelper;
import java.util.List;
import java.util.Map;
@Extension
public class AixMemoryExtension implements MetricExtension {
private static final Logger log = LoggerFactory.getLogger(AixMemoryExtension.class);
@Override
public boolean isSupported() {
if(!System.getProperty("os.name").toLowerCase().contains("aix")) {
log.warn("Requires AIX.");
return false;
}
if(!PluginHelper.canExecute("svmon")) {
log.warn("Requires the 'svmon' command.");
return false;
}
return true;
}
@Override
public String getName() {
return "aix-memory";
}
@Override
public String getProvides() {
return "memory";
}
@Override
public String getDescription() {
return "AIX Memory Metrics";
}
@Override
public MetricResult getMetrics() {
//List<String> svmon = PluginHelper.executeCommand("svmon -G -O unit=KB");
List<String> svmon = PluginHelper.executeCommand("svmon -G -O summary=longreal,unit=KB");
AixMemoryStat memoryStat = processCommandOutput(svmon);
Map<String, String> tagsMap = memoryStat.getTags();
Map<String, Object> fieldsMap = memoryStat.getFields();
return new MetricResult("memory", new Measurement(tagsMap, fieldsMap));
}
protected AixMemoryStat processCommandOutput(List<String> inputLines) {
return new AixMemoryStat(inputLines);
}
}

View file

@ -1,60 +0,0 @@
package sysmon.plugins.os_aix;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class AixMemoryStat {
private final Pattern pattern = Pattern.compile("^\\s+(\\d+)\\s+(\\d+)\\s+(\\d+)\\s+(\\d+)\\s+(\\d+)\\s+(\\d+)\\s+(\\d+)");
private long total;
private long used;
private long free;
private long pin;
private long virtual;
private long available;
private long paged;
AixMemoryStat(List<String> lines) {
for (String line : lines) {
Matcher matcher = pattern.matcher(line);
if (matcher.find() && matcher.groupCount() == 7) {
total = Long.parseLong(matcher.group(1));
used = Long.parseLong(matcher.group(2));
free = Long.parseLong(matcher.group(3));
pin = Long.parseLong(matcher.group(4));
virtual = Long.parseLong(matcher.group(5));
available = Long.parseLong(matcher.group(6));
paged = Long.parseLong(matcher.group(7));
break;
}
}
}
public Map<String, String> getTags() {
return new HashMap<>();
}
public Map<String, Object> getFields() {
float usage = ((float) (total - available) / total ) * 100;
//BigDecimal usage = new BigDecimal(tmp).setScale(2, RoundingMode.HALF_UP);
Map<String, Object> fields = new HashMap<>();
fields.put("total", total);
fields.put("used", used);
fields.put("free", free);
fields.put("pin", pin);
fields.put("virtual", virtual);
fields.put("available", available);
fields.put("paged", paged);
fields.put("usage", usage);
return fields;
}
}

View file

@ -0,0 +1,74 @@
package sysmon.plugins.os_aix;
import org.pf4j.Extension;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import sysmon.shared.Measurement;
import sysmon.shared.MetricExtension;
import sysmon.shared.MetricResult;
import sysmon.shared.PluginHelper;
import java.io.IOException;
import java.io.InputStream;
import java.util.Map;
// Disabled
//@Extension
public class AixNetstatExtension implements MetricExtension {
private static final Logger log = LoggerFactory.getLogger(AixNetstatExtension.class);
@Override
public boolean isSupported() {
if(!System.getProperty("os.name").toLowerCase().contains("aix")) {
log.warn("Requires AIX.");
return false;
}
if(!PluginHelper.canExecute("netstat")) {
log.warn("Requires the 'netstat' command.");
return false;
}
return true;
}
@Override
public String getName() {
return "aix-network-netstat";
}
@Override
public String getProvides() {
return "network-netstat";
}
@Override
public String getDescription() {
return "AIX Netstat Metrics";
}
@Override
public MetricResult getMetrics() throws Exception {
Map<String, String> tagsMap = null;
Map<String, Object> fieldsMap = null;
try (InputStream buf = PluginHelper.executeCommand("netstat -s -f inet")) {
AixNetstatParser parser = processCommandOutput(buf);
tagsMap = parser.getTags();
fieldsMap = parser.getFields();
}
log.debug(fieldsMap.toString());
return new MetricResult("network_netstat", new Measurement(tagsMap, fieldsMap));
}
protected AixNetstatParser processCommandOutput(InputStream input) throws IOException {
return new AixNetstatParser(input);
}
}

View file

@ -0,0 +1,156 @@
package sysmon.plugins.os_aix;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.HashMap;
import java.util.Map;
public class AixNetstatParser {
private static final Logger log = LoggerFactory.getLogger(AixNetstatParser.class);
private long ipTotalPacketsReceived;
private long ipForwarded;
private long tcpConnectionsEstablished;
private long tcpPacketsReceved;
private long tcpPacketsSent;
private long udpPacketsReceived;
private long udpPacketsSent;
public AixNetstatParser(InputStream inputStream) throws IOException {
BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));
while (reader.ready()) {
String line = reader.readLine();
log.debug("AixNetstatParser() - Line: " + line);
if(line.startsWith("tcp:")) {
parseTcp(reader);
}
if(line.startsWith("udp:")) {
parseUdp(reader);
}
if(line.startsWith("ip:")) {
parseIp(reader);
}
}
inputStream.close();
}
protected void parseIp(BufferedReader reader) throws IOException {
while (reader.ready()) {
reader.mark(64);
String line = reader.readLine();
if(!line.startsWith(" ")) {
reader.reset();
return;
}
line = line.trim();
if(line.matches("(\\d+) total packets received")) {
ipTotalPacketsReceived = getFirstLong(line);
}
if(line.matches("(\\d+) packets forwarded")) {
ipForwarded = getFirstLong(line);
}
}
}
protected void parseTcp(BufferedReader reader) throws IOException {
while (reader.ready()) {
reader.mark(64);
String line = reader.readLine();
if(!line.startsWith(" ")) {
reader.reset();
return;
}
line = line.trim();
if(line.matches("(\\d+) connections established \\(including accepts\\)")) {
tcpConnectionsEstablished = getFirstLong(line);
}
if(line.matches("(\\d+) packets received")) {
tcpPacketsReceved = getFirstLong(line);
}
if(line.matches("(\\d+) packets sent")) {
tcpPacketsSent = getFirstLong(line);
}
}
}
protected void parseUdp(BufferedReader reader) throws IOException {
while (reader.ready()) {
reader.mark(64);
String line = reader.readLine();
if(!line.startsWith(" ")) {
reader.reset();
return;
}
line = line.trim();
if(line.matches("(\\d+) datagrams received")) {
udpPacketsReceived = getFirstLong(line);
}
if(line.matches("(\\d+) datagrams output")) {
udpPacketsSent = getFirstLong(line);
}
}
}
public Map<String, String> getTags() {
return new HashMap<>();
}
public Map<String, Object> getFields() {
Map<String, Object> fields = new HashMap<>();
fields.put("ip_forwarded", ipForwarded);
fields.put("ip_received", ipTotalPacketsReceived);
fields.put("tcp_connections", tcpConnectionsEstablished);
fields.put("tcp_pkts_recv", tcpPacketsReceved);
fields.put("tcp_pkts_sent", tcpPacketsSent);
fields.put("udp_pkts_recv", udpPacketsReceived);
fields.put("udp_pkts_sent", udpPacketsSent);
return fields;
}
private Long getFirstLong(String line) {
return Long.parseLong(line.substring(0, line.indexOf(" ")));
}
}

View file

@ -8,6 +8,8 @@ import sysmon.shared.MetricExtension;
import sysmon.shared.MetricResult; import sysmon.shared.MetricResult;
import sysmon.shared.PluginHelper; import sysmon.shared.PluginHelper;
import java.io.IOException;
import java.io.InputStream;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
@ -40,7 +42,7 @@ public class AixProcessorExtension implements MetricExtension {
@Override @Override
public String getProvides() { public String getProvides() {
return "processor"; return "processor-lpar";
} }
@Override @Override
@ -49,20 +51,23 @@ public class AixProcessorExtension implements MetricExtension {
} }
@Override @Override
public MetricResult getMetrics() { public MetricResult getMetrics() throws Exception {
List<String> lparstat = PluginHelper.executeCommand("lparstat 1 1"); Map<String, String> tagsMap = null;
AixProcessorStat processorStat = processCommandOutput(lparstat); Map<String, Object> fieldsMap = null;
Map<String, String> tagsMap = processorStat.getTags(); try (InputStream buf = PluginHelper.executeCommand("lparstat 1 1")) {
Map<String, Object> fieldsMap = processorStat.getFields(); AixProcessorStat processorStat = processCommandOutput(buf);
tagsMap = processorStat.getTags();
fieldsMap = processorStat.getFields();
}
return new MetricResult("processor", new Measurement(tagsMap, fieldsMap)); return new MetricResult("processor_lpar", new Measurement(tagsMap, fieldsMap));
} }
protected AixProcessorStat processCommandOutput(List<String> inputLines) { protected AixProcessorStat processCommandOutput(InputStream input) throws IOException {
return new AixProcessorStat(inputLines); return new AixProcessorStat(input);
} }

View file

@ -1,5 +1,12 @@
package sysmon.plugins.os_aix; package sysmon.plugins.os_aix;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
@ -8,8 +15,13 @@ import java.util.regex.Pattern;
public class AixProcessorStat { public class AixProcessorStat {
private static final Logger log = LoggerFactory.getLogger(AixProcessorStat.class);
// System configuration: type=Shared mode=Uncapped smt=8 lcpu=8 mem=4096MB psize=19 ent=0.50 // System configuration: type=Shared mode=Uncapped smt=8 lcpu=8 mem=4096MB psize=19 ent=0.50
private final Pattern patternAix = Pattern.compile("^System configuration: type=(\\S+) mode=(\\S+) smt=(\\d+) lcpu=(\\d+) mem=(\\d+)MB psize=(\\d+) ent=(\\d+\\.?\\d*)"); private final Pattern patternAixShared = Pattern.compile("^System configuration: type=(\\S+) mode=(\\S+) smt=(\\d+) lcpu=(\\d+) mem=(\\d+)MB psize=(\\d+) ent=(\\d+\\.?\\d*)");
// System configuration: type=Dedicated mode=Donating smt=8 lcpu=16 mem=4096MB
private final Pattern patternAixDedicated = Pattern.compile("^System configuration: type=(\\S+) mode=(\\S+) smt=(\\d+) lcpu=(\\d+) mem=(\\d+)MB");
// type=Shared mode=Uncapped smt=8 lcpu=4 mem=4101120 kB cpus=24 ent=4.00 // type=Shared mode=Uncapped smt=8 lcpu=4 mem=4101120 kB cpus=24 ent=4.00
private final Pattern patternLinux = Pattern.compile("^type=(\\S+) mode=(\\S+) smt=(\\d+) lcpu=(\\d+) mem=(\\d+) kB cpus=(\\d+) ent=(\\d+\\.?\\d*)"); private final Pattern patternLinux = Pattern.compile("^type=(\\S+) mode=(\\S+) smt=(\\d+) lcpu=(\\d+) mem=(\\d+) kB cpus=(\\d+) ent=(\\d+\\.?\\d*)");
@ -31,12 +43,15 @@ public class AixProcessorStat {
private final float lbusy; // Indicates the percentage of logical processor(s) utilization that occurred while executing at the user and system level. private final float lbusy; // Indicates the percentage of logical processor(s) utilization that occurred while executing at the user and system level.
AixProcessorStat(List<String> lines) { public AixProcessorStat(InputStream inputStream) throws IOException {
for (String line : lines) { String lastLine = null;
BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));
while(reader.ready()) {
String line = reader.readLine();
if (line.startsWith("System configuration:")) { if (line.startsWith("System configuration:")) {
Matcher matcher = patternAix.matcher(line); Matcher matcher = patternAixShared.matcher(line);
if (matcher.find() && matcher.groupCount() == 7) { if (matcher.find() && matcher.groupCount() == 7) {
type = matcher.group(1); type = matcher.group(1);
mode = matcher.group(2); mode = matcher.group(2);
@ -45,6 +60,13 @@ public class AixProcessorStat {
psize = Integer.parseInt(matcher.group(5)); psize = Integer.parseInt(matcher.group(5));
ent = Float.parseFloat(matcher.group(7)); ent = Float.parseFloat(matcher.group(7));
} }
matcher = patternAixDedicated.matcher(line);
if (matcher.find() && matcher.groupCount() == 5) {
type = matcher.group(1);
mode = matcher.group(2);
smt = Integer.parseInt(matcher.group(3));
lcpu = Integer.parseInt(matcher.group(4));
}
} }
if (line.startsWith("type=")) { if (line.startsWith("type=")) {
@ -60,12 +82,14 @@ public class AixProcessorStat {
} }
} }
lastLine = line;
} }
String lparstat = lines.get(lines.size() -1); //String lparstat = lines.get(lines.size() -1);
String[] splitStr = lparstat.trim().split("\\s+"); String[] splitStr = lastLine.trim().split("\\s+");
if(splitStr.length < 9) { if(type.equalsIgnoreCase("shared") && splitStr.length < 9 ||
throw new UnsupportedOperationException("lparstat string error: " + lparstat); type.equalsIgnoreCase("dedicated") && splitStr.length < 8) {
throw new UnsupportedOperationException("lparstat string error: " + lastLine);
} }
this.user = Float.parseFloat(splitStr[0]); this.user = Float.parseFloat(splitStr[0]);
@ -73,9 +97,15 @@ public class AixProcessorStat {
this.wait = Float.parseFloat(splitStr[2]); this.wait = Float.parseFloat(splitStr[2]);
this.idle = Float.parseFloat(splitStr[3]); this.idle = Float.parseFloat(splitStr[3]);
this.physc = Float.parseFloat(splitStr[4]); this.physc = Float.parseFloat(splitStr[4]);
if(type.equalsIgnoreCase("shared")) {
this.entc = Float.parseFloat(splitStr[5]); this.entc = Float.parseFloat(splitStr[5]);
this.lbusy = Float.parseFloat(splitStr[6]); this.lbusy = Float.parseFloat(splitStr[6]);
} else {
this.entc = 0f;
this.lbusy = 0f;
}
inputStream.close();
} }
public float getUser() { public float getUser() {

View file

@ -1,26 +0,0 @@
import sysmon.plugins.os_aix.AixDiskExtension
import sysmon.plugins.os_aix.AixDiskStat
import spock.lang.Specification
class AixDiskTest extends Specification {
void "test AIX iostat output processing"() {
setup:
def testFile = new File(getClass().getResource('/iostat.txt').toURI())
List<String> lines = testFile.readLines("UTF-8")
when:
AixDiskExtension extension = new AixDiskExtension()
AixDiskStat stats = extension.processCommandOutput(lines)
then:
//stats.getTags().get("device") == "hdisk0"
stats.getFields().get("reads") == 757760L
stats.getFields().get("writes") == 12288L
stats.getFields().get("kbps") == 752L
stats.getFields().get("tps") == 81L
}
}

View file

@ -1,29 +0,0 @@
import sysmon.plugins.os_aix.AixMemoryExtension
import sysmon.plugins.os_aix.AixMemoryStat
import spock.lang.Specification
class AixMemoryTest extends Specification {
void "test AIX svmon output processing"() {
setup:
def testFile = new File(getClass().getResource('/svmon.txt').toURI())
List<String> lines = testFile.readLines("UTF-8")
when:
AixMemoryExtension extension = new AixMemoryExtension()
AixMemoryStat stats = extension.processCommandOutput(lines)
then:
stats.getFields().get("total") == 4194304L
stats.getFields().get("used") == 4065060L
stats.getFields().get("free") == 129244L
stats.getFields().get("pin") == 1878240L
stats.getFields().get("virtual") == 2784988L
stats.getFields().get("available") == 1058012L
stats.getFields().get("paged") == 524288L
stats.getFields().get("usage") == 74.775024f
}
}

View file

@ -0,0 +1,25 @@
import spock.lang.Specification
import sysmon.plugins.os_aix.AixNetstatParser
class AixNetstatTest extends Specification {
void "test netstat parsing"() {
setup:
InputStream inputStream = getClass().getResourceAsStream('/netstat-aix.txt');
when:
AixNetstatParser parser = new AixNetstatParser(inputStream)
then:
parser.getFields().size() > 0
parser.getFields().get('ip_received') == 76229L
parser.getFields().get('ip_forwarded') == 24L
parser.getFields().get('tcp_connections') == 85L
parser.getFields().get('tcp_pkts_sent') == 31274L
parser.getFields().get('tcp_pkts_recv') == 39830L
parser.getFields().get('udp_pkts_sent') == 26332L
parser.getFields().get('udp_pkts_recv') == 34559L
}
}

View file

@ -4,15 +4,14 @@ import spock.lang.Specification
class AixProcessorTest extends Specification { class AixProcessorTest extends Specification {
void "test AIX lparstat output processing"() { void "test AIX lparstat shared output processing"() {
setup: setup:
def testFile = new File(getClass().getResource('/lparstat-aix.txt').toURI()) InputStream inputStream = getClass().getResourceAsStream('/lparstat-aix-shared.txt');
List<String> lines = testFile.readLines("UTF-8")
when: when:
AixProcessorExtension extension = new AixProcessorExtension() AixProcessorExtension extension = new AixProcessorExtension()
AixProcessorStat stats = extension.processCommandOutput(lines) AixProcessorStat stats = extension.processCommandOutput(inputStream)
then: then:
stats.getUser() == 83.7f stats.getUser() == 83.7f
@ -20,19 +19,37 @@ class AixProcessorTest extends Specification {
stats.getWait() == 0.0f stats.getWait() == 0.0f
stats.getIdle() == 13.0f stats.getIdle() == 13.0f
stats.getFields().get("ent") == 0.50f stats.getFields().get("ent") == 0.50f
stats.getFields().get("type") == "Shared"
} }
void "test AIX lparstat dedicated output processing"() {
setup:
InputStream inputStream = getClass().getResourceAsStream('/lparstat-aix-dedicated.txt');
when:
AixProcessorExtension extension = new AixProcessorExtension()
AixProcessorStat stats = extension.processCommandOutput(inputStream)
then:
stats.getUser() == 0.1f
stats.getSys() == 0.2f
stats.getWait() == 0.0f
stats.getIdle() == 99.7f
stats.getFields().get("physc") == 0.07f
stats.getFields().get("type") == "Dedicated"
}
void "test Linux lparstat output processing"() { void "test Linux lparstat output processing"() {
setup: setup:
def testFile = new File(getClass().getResource('/lparstat-linux.txt').toURI()) InputStream inputStream = getClass().getResourceAsStream('/lparstat-linux.txt');
List<String> lines = testFile.readLines("UTF-8")
when: when:
AixProcessorExtension extension = new AixProcessorExtension() AixProcessorExtension extension = new AixProcessorExtension()
AixProcessorStat stats = extension.processCommandOutput(lines) AixProcessorStat stats = extension.processCommandOutput(inputStream)
then: then:
stats.getUser() == 0.03f stats.getUser() == 0.03f

View file

@ -1,3 +0,0 @@
Disks: % tm_act Kbps tps Kb_read Kb_wrtn
cd0 0.0 0.0 0.0 0 0
hdisk0 1.0 752.0 81.0 740 12

View file

@ -0,0 +1,6 @@
System configuration: type=Dedicated mode=Donating smt=8 lcpu=16 mem=4096MB
%user %sys %wait %idle physc vcsw %nsp %utcyc
----- ----- ------ ------ ----- ----- ----- ------
0.1 0.2 0.0 99.7 0.07 1014627468 132 24.21

View file

@ -1,15 +0,0 @@
System configuration: lcpu=8 ent=0.5 mode=Uncapped
vcpu lcpu us sy wa id pbusy pc VTB(ms)
---- ---- ---- ---- ----- ----- ----- ----- -------
0 33.51 5.62 0.11 60.77 0.00[ 39.1%] 0.00[ 0.1%] 1784440
0 13.38 3.96 0.05 4.81 0.00[ 17.3%] 0.00[ 22.2%] -
1 8.41 0.72 0.04 7.59 0.00[ 9.1%] 0.00[ 16.8%] -
2 5.19 0.35 0.01 6.45 0.00[ 5.5%] 0.00[ 12.0%] -
3 4.42 0.28 0.01 8.42 0.00[ 4.7%] 0.00[ 13.1%] -
4 0.57 0.07 0.00 7.47 0.00[ 0.6%] 0.00[ 8.1%] -
5 0.51 0.06 0.00 9.28 0.00[ 0.6%] 0.00[ 9.8%] -
6 0.49 0.07 0.00 7.49 0.00[ 0.6%] 0.00[ 8.1%] -
7 0.54 0.10 0.00 9.26 0.00[ 0.6%] 0.00[ 9.9%] -

View file

@ -0,0 +1,157 @@
icmp:
12 calls to icmp_error
0 errors not generated because old message was icmp
Output histogram:
destination unreachable: 12
0 messages with bad code fields
0 messages < minimum length
0 bad checksums
0 messages with bad length
Input histogram:
destination unreachable: 3
0 message responses generated
igmp:
0 messages received
0 messages received with too few bytes
0 messages received with bad checksum
0 membership queries received
0 membership queries received with invalid field(s)
0 membership reports received
0 membership reports received with invalid field(s)
0 membership reports received for groups to which we belong
2 membership reports sent
tcp:
31274 packets sent
27328 data packets (82928168 bytes)
86 data packets (108992 bytes) retransmitted
2938 ack-only packets (2698 delayed)
0 URG only packets
0 window probe packets
784 window update packets
138 control packets
3812 large sends
74913716 bytes sent using largesend
64069 bytes is the biggest largesend
39830 packets received
22701 acks (for 82928732 bytes)
112 duplicate acks
0 acks for unsent data
15579 packets (5876585 bytes) received in-sequence
62 completely duplicate packets (320 bytes)
57 old duplicate packets
0 packets with some dup. data (0 bytes duped)
75 out-of-order packets (6408 bytes)
0 packets (0 bytes) of data after window
0 window probes
1723 window update packets
0 packets received after close
0 packets with bad hardware assisted checksum
0 discarded for bad checksums
0 discarded for bad header offset fields
0 discarded because packet too short
1 discarded by listeners
0 discarded due to listener's queue full
3207 ack packet headers correctly predicted
15050 data packet headers correctly predicted
63 connection requests
23 connection accepts
85 connections established (including accepts)
114 connections closed (including 0 drops)
0 connections with ECN capability
0 times responded to ECN
0 embryonic connections dropped
20314 segments updated rtt (of 16791 attempts)
0 segments with congestion window reduced bit set
0 segments with congestion experienced bit set
0 resends due to path MTU discovery
2 path MTU discovery terminations due to retransmits
25 retransmit timeouts
0 connections dropped by rexmit timeout
4 fast retransmits
1 when congestion window less than 4 segments
28 newreno retransmits
4 times avoided false fast retransmits
0 persist timeouts
0 connections dropped due to persist timeout
0 keepalive timeouts
0 keepalive probes sent
0 connections dropped by keepalive
0 times SACK blocks array is extended
0 times SACK holes array is extended
0 packets dropped due to memory allocation failure
0 connections in timewait reused
0 delayed ACKs for SYN
0 delayed ACKs for FIN
0 send_and_disconnects
0 spliced connections
0 spliced connections closed
0 spliced connections reset
0 spliced connections timeout
0 spliced connections persist timeout
0 spliced connections keepalive timeout
0 TCP checksum offload disabled during retransmit
0 Connections dropped due to bad ACKs
0 Connections dropped due to duplicate SYN packets
0 fastpath loopback connections
0 fastpath loopback sent packets (0 bytes)
0 fastpath loopback received packets (0 bytes)
0 fake SYN segments dropped
0 fake RST segments dropped
0 data injection segments dropped
0 TCPTR maximum connections dropped
0 TCPTR connections dropped for no memory
0 TCPTR maximum per host connections dropped
0 connections dropped due to max assembly queue depth
udp:
34559 datagrams received
0 incomplete headers
0 bad data length fields
0 bad checksums
1849 dropped due to no socket
8218 broadcast/multicast datagrams dropped due to no socket
0 socket buffer overflows
24492 delivered
26332 datagrams output
ip:
76229 total packets received
0 bad header checksums
0 with size smaller than minimum
0 with data size < data length
0 with header length < data size
0 with data length < header length
0 with bad options
0 with incorrect version number
0 fragments received
0 fragments dropped (dup or out of space)
0 fragments dropped after timeout
0 packets reassembled ok
72552 packets for this host
3 packets for unknown/unsupported protocol
24 packets forwarded
0 packets not forwardable
0 redirects sent
55784 packets sent from this host
0 packets sent with fabricated ip header
0 output packets dropped due to no bufs, etc.
0 output packets discarded due to no route
0 output datagrams fragmented
0 fragments created
0 datagrams that can't be fragmented
0 IP Multicast packets dropped due to no receiver
0 successful path MTU discovery cycles
0 path MTU rediscovery cycles attempted
0 path MTU discovery no-response estimates
0 path MTU discovery response timeouts
0 path MTU discovery decreases detected
0 path MTU discovery packets sent
0 path MTU discovery memory allocation failures
0 ipintrq overflows
0 with illegal source
0 packets processed by threads
0 packets dropped by threads
0 packets dropped due to the full socket receive buffer
0 dead gateway detection packets sent
0 dead gateway detection packet allocation failures
0 dead gateway detection gateway allocation failures
0 incoming packets dropped due to MLS filters
0 packets not sent due to MLS filters

View file

@ -1,6 +0,0 @@
Unit: KB
------------------------------------------------------------------------
Memory
------------------------------------------------------------------------
Size Inuse Free Pin Virtual Available Pgsp
4194304 4065060 129244 1878240 2784988 1058012 524288

View file

@ -1,7 +0,0 @@
System configuration: lcpu=8 mem=4096MB ent=0.50
kthr memory page faults cpu time
----------- --------------------- ------------------------------------ ------------------ ----------------------- --------
r b p avm fre fi fo pi po fr sr in sy cs us sy id wa pc ec hr mi se
2 1 0 633739 130144 0 13 0 0 2 2 7 969 529 0 0 99 0 0.00 0.1 07:53:28

36
plugins/os-base/README.md Normal file
View file

@ -0,0 +1,36 @@
# Base Plugin
## Processor Extension
Reports the following metrics seen:
- **user** - CPU time spend on user processes.
- **system** -CPU time spend on system processes.
- **iowait** - CPU time spend on waiting (for i/o).
- **idle** - CPU time spend on idle (doing nothing).
- **busy** - CPU time not spend on idle (working).
## Memory Extension
Reports the following metrics, from the *free* command:
- **total** - The total amount of (installed) memory (in KB).
- **used** - Used memory (calculated as total - free - buffers - cache) (in KB).
- **free** - Unused memory (MemFree and SwapFree in /proc/meminfo) (in KB).
- **shared** - Memory used (mostly) by tmpfs (Shmem in /proc/meminfo) (in KB).
- **buffers** - Sum of buffers and cache (in KB).
- **available** - Estimation of how much memory is available for starting new applications, without swapping (in KB).
- **usage** - Percentage of memory used out of the total amount of memory.
## Disk Extension
Only reports first device found. Improvements on the TODO.
Metrics reported are:
- **device** - Name of device.
- **reads** - The total number of KB read.
- **writes** - The total number of KB written.

View file

@ -0,0 +1,2 @@
plugins {
}

View file

@ -0,0 +1,7 @@
pluginId=sysmon-base
pluginClass=sysmon.plugins.os_base.BasePlugin
pluginVersion=0.0.1
pluginProvider=System Monitor
pluginDependencies=
pluginDescription=Base OS metrics where supported.

View file

@ -0,0 +1,79 @@
package sysmon.plugins.os_base;
import org.pf4j.Extension;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import oshi.SystemInfo;
import oshi.hardware.HWDiskStore;
import oshi.hardware.HardwareAbstractionLayer;
import sysmon.shared.Measurement;
import sysmon.shared.MetricExtension;
import sysmon.shared.MetricResult;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@Extension
public class BaseDiskExtension implements MetricExtension {
private static final Logger log = LoggerFactory.getLogger(BaseDiskExtension.class);
private HardwareAbstractionLayer hardwareAbstractionLayer;
@Override
public boolean isSupported() {
hardwareAbstractionLayer = BasePlugin.getHardwareAbstractionLayer();
return hardwareAbstractionLayer != null;
}
@Override
public String getName() {
return "base-disk";
}
@Override
public String getProvides() {
return "disk";
}
@Override
public String getDescription() {
return "Base Disk Metrics";
}
@Override
public MetricResult getMetrics() {
long writeBytes = 0L;
long readBytes = 0L;
long transferTime = 0L;
long queueLength = 0L;
Map<String, String> tagsMap = new HashMap<>();
Map<String, Object> fieldsMap = new HashMap<>();
List<HWDiskStore> diskStores = hardwareAbstractionLayer.getDiskStores();
for(HWDiskStore store : diskStores) {
String name = store.getName();
if (name.matches("hdisk[0-9]+") || name.matches("/dev/[sv]d[a-z]{1}") || name.matches("/dev/nvme[0-9]n[0-9]")) {
log.debug("Using device: " + name);
writeBytes += store.getWriteBytes();
readBytes += store.getReadBytes();
transferTime += store.getTransferTime();
queueLength = store.getCurrentQueueLength();
}
}
fieldsMap.put("reads", readBytes);
fieldsMap.put("writes", writeBytes);
fieldsMap.put("iotime", transferTime);
fieldsMap.put("queue", queueLength);
log.debug(fieldsMap.toString());
return new MetricResult("disk", new Measurement(tagsMap, fieldsMap));
}
}

View file

@ -0,0 +1,64 @@
package sysmon.plugins.os_base;
import org.pf4j.Extension;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import oshi.SystemInfo;
import oshi.hardware.HardwareAbstractionLayer;
import sysmon.shared.Measurement;
import sysmon.shared.MetricExtension;
import sysmon.shared.MetricResult;
import java.util.HashMap;
import java.util.Map;
@Extension
public class BaseMemoryExtension implements MetricExtension {
private static final Logger log = LoggerFactory.getLogger(BaseMemoryExtension.class);
private HardwareAbstractionLayer hardwareAbstractionLayer;
@Override
public boolean isSupported() {
hardwareAbstractionLayer = BasePlugin.getHardwareAbstractionLayer();
return hardwareAbstractionLayer != null;
}
@Override
public String getName() {
return "base-memory";
}
@Override
public String getProvides() {
return "memory";
}
@Override
public String getDescription() {
return "Base Memory Metrics";
}
@Override
public MetricResult getMetrics() {
Map<String, String> tagsMap = new HashMap<>();
Map<String, Object> fieldsMap = new HashMap<>();
long total = hardwareAbstractionLayer.getMemory().getTotal();
long available = hardwareAbstractionLayer.getMemory().getAvailable();
float usage = ((float) (total - available) / total ) * 100;
fieldsMap.put("available", available);
fieldsMap.put("total", total);
fieldsMap.put("usage", usage);
fieldsMap.put("paged", hardwareAbstractionLayer.getMemory().getPageSize());
fieldsMap.put("virtual", hardwareAbstractionLayer.getMemory().getVirtualMemory().getVirtualInUse());
log.debug(fieldsMap.toString());
return new MetricResult("memory", new Measurement(tagsMap, fieldsMap));
}
}

View file

@ -0,0 +1,81 @@
package sysmon.plugins.os_base;
import org.pf4j.Extension;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import oshi.SystemInfo;
import oshi.hardware.HardwareAbstractionLayer;
import oshi.hardware.NetworkIF;
import sysmon.shared.Measurement;
import sysmon.shared.MetricExtension;
import sysmon.shared.MetricResult;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@Extension
public class BaseNetworkExtension implements MetricExtension {
private static final Logger log = LoggerFactory.getLogger(BaseNetworkExtension.class);
private HardwareAbstractionLayer hardwareAbstractionLayer;
@Override
public boolean isSupported() {
hardwareAbstractionLayer = BasePlugin.getHardwareAbstractionLayer();
return hardwareAbstractionLayer != null;
}
@Override
public String getName() {
return "base-network";
}
@Override
public String getProvides() {
return "network";
}
@Override
public String getDescription() {
return "Base Network Metrics";
}
@Override
public MetricResult getMetrics() {
long rxBytes = 0L;
long rxPackets = 0L;
long rxErrs = 0L;
long txBytes = 0L;
long txPackets = 0L;
long txErrs = 0L;
Map<String, String> tagsMap = new HashMap<>();
Map<String, Object> fieldsMap = new HashMap<>();
List<NetworkIF> interfaces = hardwareAbstractionLayer.getNetworkIFs();
for(NetworkIF netif : interfaces) {
//String name = netif.getName();
//log.warn("Device: " + name);
rxPackets += netif.getPacketsRecv();
txPackets += netif.getPacketsSent();
rxBytes += netif.getBytesRecv();
txBytes += netif.getBytesSent();
rxErrs += netif.getInErrors();
txErrs += netif.getOutErrors();
}
fieldsMap.put("rxPackets", rxPackets);
fieldsMap.put("txPackets", txPackets);
fieldsMap.put("rxBytes", rxBytes);
fieldsMap.put("txBytes", txBytes);
fieldsMap.put("rxErrors", rxErrs);
fieldsMap.put("txErrors", txErrs);
log.debug(fieldsMap.toString());
return new MetricResult("network", new Measurement(tagsMap, fieldsMap));
}
}

View file

@ -0,0 +1,39 @@
package sysmon.plugins.os_base;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.pf4j.Plugin;
import org.pf4j.PluginWrapper;
import oshi.SystemInfo;
import oshi.hardware.HardwareAbstractionLayer;
public class BasePlugin extends Plugin {
private static final Logger log = LoggerFactory.getLogger(BasePlugin.class);
private static SystemInfo systemInfo;
private static HardwareAbstractionLayer hardwareAbstractionLayer;
public BasePlugin(PluginWrapper wrapper) {
super(wrapper);
}
public static HardwareAbstractionLayer getHardwareAbstractionLayer() {
try {
if(systemInfo == null) {
systemInfo = new SystemInfo();
}
if(hardwareAbstractionLayer == null) {
hardwareAbstractionLayer = systemInfo.getHardware();
}
} catch (UnsupportedOperationException e) {
log.warn(e.getMessage());
}
return hardwareAbstractionLayer;
}
}

View file

@ -0,0 +1,94 @@
package sysmon.plugins.os_base;
import org.pf4j.Extension;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import oshi.SystemInfo;
import oshi.hardware.CentralProcessor;
import oshi.hardware.HardwareAbstractionLayer;
import sysmon.shared.Measurement;
import sysmon.shared.MetricExtension;
import sysmon.shared.MetricResult;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@Extension
public class BaseProcessorExtension implements MetricExtension {
private static final Logger log = LoggerFactory.getLogger(BaseProcessorExtension.class);
private HardwareAbstractionLayer hardwareAbstractionLayer;
private long[] oldTicks;
@Override
public boolean isSupported() {
hardwareAbstractionLayer = BasePlugin.getHardwareAbstractionLayer();
return hardwareAbstractionLayer != null;
}
@Override
public String getName() {
return "base-processor";
}
@Override
public String getProvides() {
return "processor";
}
@Override
public String getDescription() {
return "Base Processor Metrics";
}
@Override
public MetricResult getMetrics() {
Map<String, String> tagsMap = new HashMap<>();
Map<String, Object> fieldsMap = new HashMap<>();
long[] ticks = hardwareAbstractionLayer.getProcessor().getSystemCpuLoadTicks();
if(oldTicks == null || oldTicks.length != ticks.length) {
oldTicks = ticks;
return null;
}
long nice = ticks[CentralProcessor.TickType.NICE.getIndex()] - oldTicks[CentralProcessor.TickType.NICE.getIndex()];
long user = ticks[CentralProcessor.TickType.USER.getIndex()] - oldTicks[CentralProcessor.TickType.USER.getIndex()];
long system = ticks[CentralProcessor.TickType.SYSTEM.getIndex()] - oldTicks[CentralProcessor.TickType.SYSTEM.getIndex()];
long steal = ticks[CentralProcessor.TickType.STEAL.getIndex()] - oldTicks[CentralProcessor.TickType.STEAL.getIndex()];
long irq = ticks[CentralProcessor.TickType.IRQ.getIndex()] - oldTicks[CentralProcessor.TickType.IRQ.getIndex()];
long softirq = ticks[CentralProcessor.TickType.SOFTIRQ.getIndex()] - oldTicks[CentralProcessor.TickType.SOFTIRQ.getIndex()];
long idle = ticks[CentralProcessor.TickType.IDLE.getIndex()] - oldTicks[CentralProcessor.TickType.IDLE.getIndex()];
long iowait = ticks[CentralProcessor.TickType.IOWAIT.getIndex()] - oldTicks[CentralProcessor.TickType.IOWAIT.getIndex()];
long busy = nice + user + system + steal + irq + softirq;
long nonBusy = idle + iowait;
long total = busy + nonBusy;
fieldsMap.put("system", ((float) system / (float) total) * 100);
fieldsMap.put("user", ((float) user / (float) total) * 100);
fieldsMap.put("nice", ((float) nice / (float) total) * 100);
fieldsMap.put("iowait", ((float) iowait / (float) total) * 100);
fieldsMap.put("steal", ((float) steal / (float) total) * 100);
fieldsMap.put("irq", ((float) irq / (float) total) * 100);
fieldsMap.put("softirq", ((float) softirq / (float) total) * 100);
fieldsMap.put("idle", ((float) idle / (float) total) * 100);
fieldsMap.put("busy", ((float) busy / (float) total) * 100);
oldTicks = ticks;
log.debug(fieldsMap.toString());
return new MetricResult("processor", new Measurement(tagsMap, fieldsMap));
}
}

View file

@ -0,0 +1,6 @@
sockets: used 1238
TCP: inuse 52 orphan 0 tw 18 alloc 55 mem 7
UDP: inuse 11 mem 10
UDPLITE: inuse 0
RAW: inuse 0
FRAG: inuse 0 memory 0

View file

@ -0,0 +1,44 @@
package sysmon.plugins.os_ibmi;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import sysmon.shared.MetricExtension;
import sysmon.shared.MetricResult;
public class TestExtension implements MetricExtension {
private static final Logger log = LoggerFactory.getLogger(TestExtension.class);
@Override
public boolean isSupported() {
String osArch = System.getProperty("os.arch").toLowerCase();
String osName = System.getProperty("os.name").toLowerCase();
System.err.println("OS Arch: " + osArch);
System.err.println("OS Name: " + osName);
return true;
}
@Override
public String getName() {
return "ibmi-test";
}
@Override
public String getProvides() {
return "test";
}
@Override
public String getDescription() {
return "IBM i Test Extension";
}
@Override
public MetricResult getMetrics() {
return null;
}
}

View file

@ -1,14 +1,14 @@
# Linux Plugin # Base Plugin
## Processor Extension ## Processor Extension
Reports the following metrics seen: Reports the following metrics seen:
- **user** - Percentage of CPU time spend on user processes. - **user** - CPU time spend on user processes.
- **sys** - Percentage of CPU time spend on system processes. - **system** -CPU time spend on system processes.
- **wait** - Percentage of CPU time spend on waiting (for i/o). - **iowait** - CPU time spend on waiting (for i/o).
- **idle** - Percentage of CPU time spend on idle (doing nothing). - **idle** - CPU time spend on idle (doing nothing).
- **busy** - Percentage of CPU time not spend on idle (working). - **busy** - CPU time not spend on idle (working).
## Memory Extension ## Memory Extension

View file

@ -1,7 +1,5 @@
pluginId=sysmon-linux pluginId=sysmon-linux
pluginClass=sysmon.plugins.os_linux.LinuxPlugin pluginClass=sysmon.plugins.os_linux.LinuxPlugin
pluginVersion=0.0.1
pluginProvider=System Monitor
pluginDependencies= pluginDependencies=
pluginDescription=Collects Linux OS metrics. pluginDescription=Linux OS Metrics.

View file

@ -1,88 +0,0 @@
package sysmon.plugins.os_linux;
import org.pf4j.Extension;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import sysmon.shared.Measurement;
import sysmon.shared.MetricExtension;
import sysmon.shared.MetricResult;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.List;
@Extension
public class LinuxDiskExtension implements MetricExtension {
private static final Logger log = LoggerFactory.getLogger(LinuxDiskExtension.class);
@Override
public boolean isSupported() {
return System.getProperty("os.name").toLowerCase().contains("linux");
}
@Override
public String getName() {
return "linux-disk";
}
@Override
public String getProvides() {
return "disk";
}
@Override
public String getDescription() {
return "Linux Disk Metrics";
}
@Override
public MetricResult getMetrics() {
LinuxDiskProcLine proc1 = processFileOutput(readProcFile());
try {
Thread.sleep(1 * 1000); // TODO: Configure sample collect time
} catch (InterruptedException e) {
log.warn("getMetrics() - sleep interrupted");
return null;
}
LinuxDiskProcLine proc2 = processFileOutput(readProcFile());
LinuxDiskStat stat = new LinuxDiskStat(proc1, proc2);
return new MetricResult("disk", new Measurement(stat.getTags(), stat.getFields()));
}
protected List<String> readProcFile() {
List<String> allLines = new ArrayList<>();
try {
allLines = Files.readAllLines(Paths.get("/proc/diskstats"), StandardCharsets.UTF_8);
} catch (IOException e) {
log.error(e.getMessage());
}
return allLines;
}
protected LinuxDiskProcLine processFileOutput(List<String> inputLines) {
List<String> lines = new ArrayList<>(inputLines.size());
for(String line : inputLines) {
String[] splitStr = line.trim().split("\\s+");
String device = splitStr[2];
if (device.matches("[sv]d[a-z]{1}") || device.matches("nvme[0-9]n[0-9]")) {
//log.warn("Going for: " + line);
lines.add(line);
}
}
return new LinuxDiskProcLine(lines);
}
}

View file

@ -1,146 +0,0 @@
package sysmon.plugins.os_linux;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.List;
public class LinuxDiskProcLine {
// Sectors to bytes - each sector is 512 bytes - https://lkml.org/lkml/2015/8/17/269
private static final int SECTOR_BYTE_SIZE = 512;
private static final Logger log = LoggerFactory.getLogger(LinuxDiskProcLine.class);
/*
== ===================================
1 major number
2 minor mumber
3 device name
4 reads completed successfully
5 reads merged
6 sectors read
7 time spent reading (ms)
8 writes completed
9 writes merged
10 sectors written
11 time spent writing (ms)
12 I/Os currently in progress
13 time spent doing I/Os (ms)
14 weighted time spent doing I/Os (ms)
== ===================================
Kernel 4.18+ appends four more fields for discard
tracking putting the total at 18:
== ===================================
15 discards completed successfully
16 discards merged
17 sectors discarded
18 time spent discarding
== ===================================
Kernel 5.5+ appends two more fields for flush requests:
== =====================================
19 flush requests completed successfully
20 time spent flushing
== =====================================
*/
private long readsCompleted; // successfully
//private long readsMerged;
private long sectorsRead; // 512 bytes pr. sector
private long timeSpentReading; // ms
private long writesCompleted; // successfully
//private long writesMerged;
private long sectorsWritten; // 512 bytes pr. sector
private long timeSpentWriting; // ms
//private long ioInProgress;
private long timeSpentOnIo; // ms
//private long timeSpentOnIoWeighted;
//private long discardsCompleted; // successfully
//private long discardsMerged;
//private long sectorsDiscarded; // 512 bytes pr. sector
//private long timeSpentDiscarding; // ms
//private long flushRequestsCompleted;
//private long timeSpentFlushing; // ms
public LinuxDiskProcLine(List<String> procLines) {
for(String procLine : procLines) {
String[] splitStr = procLine.trim().split("\\s+");
if (splitStr.length < 14) {
throw new UnsupportedOperationException("Linux proc DISK string error: " + procLine);
}
//this.major = Integer.parseInt(splitStr[0]);
//this.minor = Integer.parseInt(splitStr[1]);
//this.device = splitStr[2];
this.readsCompleted += Long.parseLong(splitStr[3]);
//this.readsMerged += Long.parseLong(splitStr[4]);
this.sectorsRead += Long.parseLong(splitStr[5]);
this.timeSpentReading += Long.parseLong(splitStr[6]);
this.writesCompleted += Long.parseLong(splitStr[7]);
//this.writesMerged += Long.parseLong(splitStr[8]);
this.sectorsWritten += Long.parseLong(splitStr[9]);
this.timeSpentWriting += Long.parseLong(splitStr[10]);
//this.ioInProgress += Long.parseLong(splitStr[11]);
this.timeSpentOnIo += Long.parseLong(splitStr[12]);
//this.timeSpentOnIoWeighted += Long.parseLong(splitStr[13]);
/*
if (splitStr.length >= 18) {
this.discardsCompleted += Long.parseLong(splitStr[10]);
this.discardsMerged += Long.parseLong(splitStr[11]);
this.sectorsDiscarded += Long.parseLong(splitStr[12]);
this.timeSpentDiscarding += Long.parseLong(splitStr[13]);
} else {
this.discardsCompleted = null;
this.discardsMerged = null;
this.sectorsDiscarded = null;
this.timeSpentDiscarding = null;
}
if (splitStr.length == 20) {
this.flushRequestsCompleted += Long.parseLong(splitStr[14]);
this.timeSpentFlushing += Long.parseLong(splitStr[15]);
} else {
this.flushRequestsCompleted = null;
this.timeSpentFlushing = null;
}
*/
}
}
public Long getTimeSpentOnIo() {
return timeSpentOnIo;
}
public Long getBytesRead() {
return sectorsRead * SECTOR_BYTE_SIZE;
}
public Long getBytesWritten() {
return sectorsWritten * SECTOR_BYTE_SIZE;
}
public Long getTimeSpentReading() {
return timeSpentReading;
}
public Long getTimeSpentWriting() {
return timeSpentWriting;
}
public Long getTransactions() {
return readsCompleted + writesCompleted;
}
}

View file

@ -1,39 +0,0 @@
package sysmon.plugins.os_linux;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.HashMap;
import java.util.Map;
public class LinuxDiskStat {
private String device;
private final long iotime;
private final long reads;
private final long writes;
private final long kbps;
private final long tps;
LinuxDiskStat(LinuxDiskProcLine proc1, LinuxDiskProcLine proc2) {
iotime = proc2.getTimeSpentOnIo() - proc1.getTimeSpentOnIo();
writes = proc2.getBytesWritten() - proc1.getBytesWritten();
reads = proc2.getBytesRead() - proc1.getBytesRead();
kbps = (writes + reads) / 1024;
tps = proc2.getTransactions() - proc1.getTransactions();
}
public Map<String, String> getTags() {
return new HashMap<>();
}
public Map<String, Object> getFields() {
Map<String, Object> fields = new HashMap<>();
fields.put("iotime", iotime);
fields.put("writes", writes);
fields.put("reads", reads);
fields.put("kbps", kbps);
fields.put("tps", tps);
return fields;
}
}

View file

@ -1,67 +0,0 @@
package sysmon.plugins.os_linux;
import org.pf4j.Extension;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import sysmon.shared.Measurement;
import sysmon.shared.MetricExtension;
import sysmon.shared.MetricResult;
import sysmon.shared.PluginHelper;
import java.util.List;
import java.util.Map;
@Extension
public class LinuxMemoryExtension implements MetricExtension {
private static final Logger log = LoggerFactory.getLogger(LinuxMemoryExtension.class);
@Override
public boolean isSupported() {
if(!System.getProperty("os.name").toLowerCase().contains("linux")) {
log.warn("Requires Linux.");
return false;
}
if(!PluginHelper.canExecute("free")) {
log.warn("Requires the 'free' command.");
return false;
}
return true;
}
@Override
public String getName() {
return "linux-memory";
}
@Override
public String getProvides() {
return "memory";
}
@Override
public String getDescription() {
return "Linux Memory Metrics";
}
@Override
public MetricResult getMetrics() {
List<String> svmon = PluginHelper.executeCommand("free -k");
LinuxMemoryStat memoryStat = processCommandOutput(svmon);
Map<String, String> tagsMap = memoryStat.getTags();
Map<String, Object> fieldsMap = memoryStat.getFields();
return new MetricResult("memory", new Measurement(tagsMap, fieldsMap));
}
protected LinuxMemoryStat processCommandOutput(List<String> inputLines) {
return new LinuxMemoryStat(inputLines);
}
}

View file

@ -1,63 +0,0 @@
package sysmon.plugins.os_linux;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class LinuxMemoryStat {
/*
total used free shared buff/cache available
Mem: 16069172 5896832 4597860 639780 5574480 9192992
Swap: 3985404 0 3985404
*/
private static final Pattern pattern = Pattern.compile("^Mem:\\s+(\\d+)\\s+(\\d+)\\s+(\\d+)\\s+(\\d+)\\s+(\\d+)\\s+(\\d+)");
private long total;
private long used;
private long free;
private long shared;
private long buffers;
private long available;
//private String mode;
LinuxMemoryStat(List<String> lines) {
for (String line : lines) {
Matcher matcher = pattern.matcher(line);
if (matcher.find() && matcher.groupCount() == 6) {
total = Long.parseLong(matcher.group(1));
used = Long.parseLong(matcher.group(2));
free = Long.parseLong(matcher.group(3));
shared = Long.parseLong(matcher.group(4));
buffers = Long.parseLong(matcher.group(5));
available = Long.parseLong(matcher.group(6));
break;
}
}
}
public Map<String, String> getTags() {
return new HashMap<>();
}
public Map<String, Object> getFields() {
float usage = ((float) (total - available) / total ) * 100;
//BigDecimal usage = new BigDecimal(tmp).setScale(2, RoundingMode.HALF_UP);
Map<String, Object> fields = new HashMap<>();
fields.put("total", total);
fields.put("used", used);
fields.put("free", free);
fields.put("shared", shared);
fields.put("buffers", buffers);
fields.put("available", available);
fields.put("usage", usage);
return fields;
}
}

View file

@ -0,0 +1,74 @@
package sysmon.plugins.os_linux;
import org.pf4j.Extension;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import sysmon.shared.Measurement;
import sysmon.shared.MetricExtension;
import sysmon.shared.MetricResult;
import sysmon.shared.PluginHelper;
import java.io.IOException;
import java.io.InputStream;
import java.util.Map;
// Disabled
//@Extension
public class LinuxNetstatExtension implements MetricExtension {
private static final Logger log = LoggerFactory.getLogger(LinuxNetstatExtension.class);
@Override
public boolean isSupported() {
if(!System.getProperty("os.name").toLowerCase().contains("linux")) {
log.warn("Requires Linux.");
return false;
}
if(!PluginHelper.canExecute("netstat")) {
log.warn("Requires the 'netstat' command.");
return false;
}
return true;
}
@Override
public String getName() {
return "linux-network-netstat";
}
@Override
public String getProvides() {
return "network-netstat";
}
@Override
public String getDescription() {
return "Linux Netstat Metrics";
}
@Override
public MetricResult getMetrics() throws Exception {
Map<String, String> tagsMap = null;
Map<String, Object> fieldsMap = null;
try (InputStream inputStream = PluginHelper.executeCommand("netstat -s")) {
LinuxNetstatParser parser = processCommandOutput(inputStream);
tagsMap = parser.getTags();
fieldsMap = parser.getFields();
}
log.debug(fieldsMap.toString());
return new MetricResult("network_netstat", new Measurement(tagsMap, fieldsMap));
}
protected LinuxNetstatParser processCommandOutput(InputStream input) throws IOException {
return new LinuxNetstatParser(input);
}
}

View file

@ -0,0 +1,168 @@
package sysmon.plugins.os_linux;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.HashMap;
import java.util.Map;
public class LinuxNetstatParser {
private static final Logger log = LoggerFactory.getLogger(LinuxNetstatParser.class);
private long ipTotalPacketsReceived;
private long ipForwarded;
private long ipIncomingPacketsDiscarded;
private long ipOutgoingPacketsDropped;
private long tcpConnectionsEstablished;
private long tcpSegmentsReceived;
private long tcpSegmentsSent;
private long udpPacketsReceived;
private long udpPacketsSent;
public LinuxNetstatParser(InputStream inputStream) throws IOException {
BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));
while (reader.ready()) {
String line = reader.readLine();
log.debug("LinuxNetstatParser() - Line: " + line);
if(line.startsWith("Ip:")) {
parseIp(reader);
}
if(line.startsWith("Tcp:")) {
parseTcp(reader);
}
if(line.startsWith("Udp:")) {
parseUdp(reader);
}
}
inputStream.close();
}
protected void parseIp(BufferedReader reader) throws IOException {
while (reader.ready()) {
reader.mark(64);
String line = reader.readLine();
if(!line.startsWith(" ")) {
reader.reset();
return;
}
line = line.trim();
if(line.matches("(\\d+) total packets received")) {
ipTotalPacketsReceived = getFirstLong(line);
}
if(line.matches("(\\d+) forwarded")) {
ipForwarded = getFirstLong(line);
}
if(line.matches("(\\d+) incoming packets discarded")) {
ipIncomingPacketsDiscarded = getFirstLong(line);
}
if(line.matches("(\\d+) outgoing packets dropped")) {
ipOutgoingPacketsDropped = getFirstLong(line);
}
}
}
protected void parseTcp(BufferedReader reader) throws IOException {
while (reader.ready()) {
reader.mark(64);
String line = reader.readLine();
if(!line.startsWith(" ")) {
reader.reset();
return;
}
line = line.trim();
if(line.matches("(\\d+) connections established")) {
tcpConnectionsEstablished = getFirstLong(line);
}
if(line.matches("(\\d+) segments received")) {
tcpSegmentsReceived = getFirstLong(line);
}
if(line.matches("(\\d+) segments sent out")) {
tcpSegmentsSent = getFirstLong(line);
}
}
}
protected void parseUdp(BufferedReader reader) throws IOException {
while (reader.ready()) {
reader.mark(64);
String line = reader.readLine();
if(!line.startsWith(" ")) {
reader.reset();
return;
}
line = line.trim();
if(line.matches("(\\d+) packets received")) {
udpPacketsReceived = getFirstLong(line);
}
if(line.matches("(\\d+) packets sent")) {
udpPacketsSent = getFirstLong(line);
}
}
}
public Map<String, String> getTags() {
return new HashMap<>();
}
public Map<String, Object> getFields() {
Map<String, Object> fields = new HashMap<>();
fields.put("ip_forwarded", ipForwarded);
fields.put("ip_received", ipTotalPacketsReceived);
fields.put("ip_dropped", ipOutgoingPacketsDropped);
fields.put("ip_discarded", ipIncomingPacketsDiscarded);
fields.put("tcp_connections", tcpConnectionsEstablished);
fields.put("tcp_pkts_recv", tcpSegmentsReceived);
fields.put("tcp_pkts_sent", tcpSegmentsSent);
fields.put("udp_pkts_recv", udpPacketsReceived);
fields.put("udp_pkts_sent", udpPacketsSent);
return fields;
}
private Long getFirstLong(String line) {
return Long.parseLong(line.substring(0, line.indexOf(" ")));
}
}

View file

@ -1,105 +0,0 @@
package sysmon.plugins.os_linux;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class LinuxNetworkDevStat {
private static final Logger log = LoggerFactory.getLogger(LinuxNetworkDevStat.class);
private static final Pattern pattern1 = Pattern.compile("^\\s+([a-z]{2,}[0-9]+):.*");
private static final Pattern pattern2 = Pattern.compile("^\\s+([a-z]{2,}[0-9]+):\\s+(\\d+)\\s+(\\d+)\\s+(\\d+)\\s+(\\d+)\\s+(\\d+)\\s+(\\d+)\\s+(\\d+)\\s+(\\d+)\\s+(\\d+)\\s+(\\d+)\\s+(\\d+)\\s+(\\d+)\\s+(\\d+)\\s+(\\d+)\\s+(\\d+)\\s+(\\d+)");
private long rxBytes;
private long rxPackets;
private long rxErrs;
private long txBytes;
private long txPackets;
private long txErrs;
/*
Inter-| Receive | Transmit
face |bytes packets errs drop fifo frame compressed multicast|bytes packets errs drop fifo colls carrier compressed
env2: 657010764 483686 0 0 0 0 0 0 55416850 431020 0 0 0 0 0 0
env3: 6900272 41836 0 0 0 0 0 0 7667444 41849 0 0 0 0 0 0
lo: 3098805 14393 0 0 0 0 0 0 3098805 14393 0 0 0 0 0 0
*/
public LinuxNetworkDevStat(List<String> procLines) {
Matcher matcher1;
Matcher matcher2;
for(String procLine : procLines) {
matcher1 = pattern1.matcher(procLine);
if(matcher1.matches()) {
if(matcher1.group(1).equals("lo")) {
continue;
}
matcher2 = pattern2.matcher(procLine);
if(matcher2.matches() && matcher2.groupCount() == 17) {
rxBytes += Long.parseLong(matcher2.group(2));
rxPackets += Long.parseLong(matcher2.group(3));
rxErrs += Long.parseLong(matcher2.group(4));
txBytes += Long.parseLong(matcher2.group(10));
txPackets += Long.parseLong(matcher2.group(11));
txErrs += Long.parseLong(matcher2.group(12));
}
}
}
}
public long getRxBytes() {
return rxBytes;
}
public long getRxPackets() {
return rxPackets;
}
public long getRxErrs() {
return rxErrs;
}
public long getTxBytes() {
return txBytes;
}
public long getTxPackets() {
return txPackets;
}
public long getTxErrs() {
return txErrs;
}
public Map<String, String> getTags() {
return new HashMap<>();
}
public Map<String, Object> getFields() {
Map<String, Object> fields = new HashMap<>();
fields.put("rxBytes", rxBytes);
fields.put("rxPackets", rxPackets);
fields.put("txBytes", txBytes);
fields.put("txPackets", txPackets);
return fields;
}
}

View file

@ -1,95 +0,0 @@
package sysmon.plugins.os_linux;
import org.pf4j.Extension;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import sysmon.shared.Measurement;
import sysmon.shared.MetricExtension;
import sysmon.shared.MetricResult;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.List;
@Extension
public class LinuxProcessorExtension implements MetricExtension {
private static final Logger log = LoggerFactory.getLogger(LinuxProcessorExtension.class);
private List<LinuxProcessorProcLine> currentProcessorProc;
private List<LinuxProcessorProcLine> previousProcessorProc;
@Override
public boolean isSupported() {
return System.getProperty("os.name").toLowerCase().contains("linux");
}
@Override
public String getName() {
return "linux-processor";
}
@Override
public String getProvides() {
return "processor";
}
@Override
public String getDescription() {
return "Linux Processor Metrics";
}
@Override
public MetricResult getMetrics() {
LinuxProcessorProcLine proc1 = processFileOutput(readProcFile());
try {
Thread.sleep(1 * 1000); // TODO: Configure sample collect time
} catch (InterruptedException e) {
log.warn("getMetrics() - sleep interrupted");
return null;
}
LinuxProcessorProcLine proc2 = processFileOutput(readProcFile());
LinuxProcessorStat stat = new LinuxProcessorStat(proc1, proc2);
return new MetricResult("processor", new Measurement(stat.getTags(), stat.getFields()));
}
protected List<String> readProcFile() {
List<String> allLines = new ArrayList<>();
try {
allLines = Files.readAllLines(Paths.get("/proc/stat"), StandardCharsets.UTF_8);
} catch (IOException e) {
log.error(e.getMessage());
}
return allLines;
}
protected LinuxProcessorProcLine processFileOutput(List<String> inputLines) {
for(String line : inputLines) {
if(line.matches("^cpu\\S+.*")) {
return new LinuxProcessorProcLine(line);
}
}
return null;
}
}

View file

@ -1,95 +0,0 @@
package sysmon.plugins.os_linux;
public class LinuxProcessorProcLine {
private final String cpuName;
private final long userTime;
private final long niceTime;
private final long systemTime;
private final long idleTime;
private final long ioWaitTime;
private final long irqTime;
private final long softIrqTime;
private final long stealTime;
private final long guestTime;
private final long guestNiceTime;
public LinuxProcessorProcLine(String procString) {
String[] splitStr = procString.trim().split("\\s+");
if(splitStr.length != 11) {
throw new UnsupportedOperationException("Linux proc CPU string error: " + procString);
}
this.cpuName = splitStr[0];
this.userTime = Long.parseLong(splitStr[1]);
this.niceTime = Long.parseLong(splitStr[2]);
this.systemTime = Long.parseLong(splitStr[3]);
this.idleTime = Long.parseLong(splitStr[4]);
this.ioWaitTime = Long.parseLong(splitStr[5]);
this.irqTime = Long.parseLong(splitStr[6]);
this.softIrqTime = Long.parseLong(splitStr[7]);
this.stealTime = Long.parseLong(splitStr[8]);
this.guestTime = Long.parseLong(splitStr[9]);
this.guestNiceTime = Long.parseLong(splitStr[10]);
}
public String getCpuName() {
return cpuName;
}
public long getUserTime() {
return userTime;
}
public long getNiceTime() {
return niceTime;
}
public long getSystemTime() {
return systemTime;
}
public long getIdleTime() {
return idleTime;
}
public long getIoWaitTime() {
return ioWaitTime;
}
public long getIrqTime() {
return irqTime;
}
public long getSoftIrqTime() {
return softIrqTime;
}
public long getStealTime() {
return stealTime;
}
public long getGuestTime() {
return guestTime;
}
public long getGuestNiceTime() {
return guestNiceTime;
}
public long getCombinedIdleTime() {
return idleTime + ioWaitTime;
}
public long getCombinedWorkTime() {
return userTime + niceTime + systemTime + irqTime + softIrqTime + stealTime + guestTime + guestNiceTime;
}
public long getCombinedTime() {
return getCombinedIdleTime() + getCombinedWorkTime();
}
}

View file

@ -1,60 +0,0 @@
package sysmon.plugins.os_linux;
import java.util.HashMap;
import java.util.Map;
public class LinuxProcessorStat {
private final float user;
private final float sys;
private final float wait;
private final float idle;
private final float busy;
public LinuxProcessorStat(LinuxProcessorProcLine previous, LinuxProcessorProcLine current) {
long workTime = current.getCombinedTime() - previous.getCombinedTime();
long busyTime = current.getCombinedIdleTime() - previous.getCombinedIdleTime();
float busyDiff = (float) (workTime - busyTime) / workTime;
busy = (busyDiff * 100);
long userTime = current.getUserTime() - previous.getUserTime();
float userDiff = (float) (workTime - userTime) / workTime;
user = 100 - (userDiff * 100);
long sysTime = current.getSystemTime() - previous.getSystemTime();
float sysDiff = (float) (workTime - sysTime) / workTime;
sys = 100 - (sysDiff * 100);
long waitTime = current.getIoWaitTime() - previous.getIoWaitTime();
float waitDiff = (float) (workTime - waitTime) / workTime;
wait = 100 - (waitDiff * 100);
long idleTime = current.getIdleTime() - previous.getIdleTime();
float idleDiff = (float) (workTime - idleTime) / workTime;
idle = 100 - (idleDiff * 100);
}
public Float getBusy() {
return busy;
}
public Map<String, String> getTags() {
return new HashMap<>();
}
public Map<String, Object> getFields() {
Map<String, Object> fields = new HashMap<>();
fields.put("user", user);
fields.put("sys", sys);
fields.put("wait", wait);
fields.put("idle", idle);
fields.put("busy", busy);
return fields;
}
}

View file

@ -12,9 +12,9 @@ import java.util.List;
import java.util.Map; import java.util.Map;
@Extension @Extension
public class LinuxNetworkExtension implements MetricExtension { public class LinuxSockstatExtension implements MetricExtension {
private static final Logger log = LoggerFactory.getLogger(LinuxNetworkExtension.class); private static final Logger log = LoggerFactory.getLogger(LinuxSockstatExtension.class);
@Override @Override
public boolean isSupported() { public boolean isSupported() {
@ -29,39 +29,34 @@ public class LinuxNetworkExtension implements MetricExtension {
@Override @Override
public String getName() { public String getName() {
return "linux-network"; return "linux-network-sockets";
} }
@Override @Override
public String getProvides() { public String getProvides() {
return "network"; return "network-sockets";
} }
@Override @Override
public String getDescription() { public String getDescription() {
return "Linux Network Metrics"; return "Linux Network Socket Metrics";
} }
@Override @Override
public MetricResult getMetrics() { public MetricResult getMetrics() {
LinuxNetworkSockStat sockStat = processSockOutput(PluginHelper.readFile("/proc/net/sockstat")); LinuxNetworkSockStat sockStat = processSockOutput(PluginHelper.readFile("/proc/net/sockstat"));
LinuxNetworkDevStat devStat = processDevOutput(PluginHelper.readFile("/proc/net/dev"));
Map<String, String> tagsMap = sockStat.getTags(); Map<String, String> tagsMap = sockStat.getTags();
Map<String, Object> fieldsMap = sockStat.getFields(); Map<String, Object> fieldsMap = sockStat.getFields();
fieldsMap.putAll(devStat.getFields());
return new MetricResult("network", new Measurement(tagsMap, fieldsMap)); log.debug(fieldsMap.toString());
return new MetricResult("network_sockets", new Measurement(tagsMap, fieldsMap));
} }
protected LinuxNetworkSockStat processSockOutput(List<String> inputLines) { protected LinuxNetworkSockStat processSockOutput(List<String> inputLines) {
return new LinuxNetworkSockStat(inputLines); return new LinuxNetworkSockStat(inputLines);
} }
protected LinuxNetworkDevStat processDevOutput(List<String> inputLines) {
return new LinuxNetworkDevStat(inputLines);
}
} }

View file

@ -1,43 +0,0 @@
import sysmon.plugins.os_linux.LinuxDiskExtension
import sysmon.plugins.os_linux.LinuxDiskProcLine
import sysmon.plugins.os_linux.LinuxDiskStat
import spock.lang.Specification
class LinuxDiskTest extends Specification {
void "test proc file processing"() {
setup:
def testFile = new File(getClass().getResource('/proc_diskstats1.txt').toURI())
List<String> lines = testFile.readLines("UTF-8")
when:
LinuxDiskExtension extension = new LinuxDiskExtension()
LinuxDiskProcLine procLine = extension.processFileOutput(lines)
then:
procLine.getTimeSpentOnIo() == 11145860l
}
void "test disk utilization"() {
setup:
def testFile1 = new File(getClass().getResource('/proc_diskstats1.txt').toURI())
def testFile2 = new File(getClass().getResource('/proc_diskstats2.txt').toURI())
LinuxDiskExtension extension = new LinuxDiskExtension()
LinuxDiskProcLine procLine1 = extension.processFileOutput(testFile1.readLines())
LinuxDiskProcLine procLine2 = extension.processFileOutput(testFile2.readLines())
when:
LinuxDiskStat diskStat = new LinuxDiskStat(procLine1, procLine2)
then:
diskStat.getFields().get("iotime") == 180L
diskStat.getFields().get("writes") == 108371968L
diskStat.getFields().get("reads") == 69632L
diskStat.getFields().get("kbps") == 105900.0f
diskStat.getFields().get("tps") == 97.0f
}
}

View file

@ -1,28 +0,0 @@
import sysmon.plugins.os_linux.LinuxMemoryExtension
import sysmon.plugins.os_linux.LinuxMemoryStat
import spock.lang.Specification
class LinuxMemoryTest extends Specification {
void "test Linux free output processing"() {
setup:
def testFile = new File(getClass().getResource('/free.txt').toURI())
List<String> lines = testFile.readLines("UTF-8")
when:
LinuxMemoryExtension extension = new LinuxMemoryExtension()
LinuxMemoryStat stats = extension.processCommandOutput(lines)
then:
stats.getFields().get("total") == 16069172l
stats.getFields().get("used") == 5896832l
stats.getFields().get("free") == 4597860l
stats.getFields().get("shared") == 639780l
stats.getFields().get("buffers") == 5574480l
stats.getFields().get("available") == 9192992l
stats.getFields().get("usage") == 42.79113f
}
}

View file

@ -0,0 +1,26 @@
import spock.lang.Specification
import sysmon.plugins.os_linux.LinuxNetstatParser
class LinuxNetstatTest extends Specification {
void "test netstat parsing"() {
setup:
InputStream inputStream = getClass().getResourceAsStream('/netstat-linux.txt');
when:
LinuxNetstatParser parser = new LinuxNetstatParser(inputStream)
then:
parser.getFields().size() > 0
parser.getFields().get('ip_received') == 109772L
parser.getFields().get('ip_dropped') == 70L
parser.getFields().get('ip_discarded') == 0L
parser.getFields().get('tcp_pkts_sent') == 89891L
parser.getFields().get('tcp_pkts_recv') == 86167L
parser.getFields().get('udp_pkts_sent') == 10682L
parser.getFields().get('udp_pkts_recv') == 31928L
}
}

View file

@ -1,6 +1,5 @@
import spock.lang.Specification import spock.lang.Specification
import sysmon.plugins.os_linux.LinuxNetworkDevStat import sysmon.plugins.os_linux.LinuxSockstatExtension
import sysmon.plugins.os_linux.LinuxNetworkExtension
import sysmon.plugins.os_linux.LinuxNetworkSockStat import sysmon.plugins.os_linux.LinuxNetworkSockStat
class LinuxNetworkTest extends Specification { class LinuxNetworkTest extends Specification {
@ -12,7 +11,7 @@ class LinuxNetworkTest extends Specification {
List<String> lines = testFile.readLines("UTF-8") List<String> lines = testFile.readLines("UTF-8")
when: when:
LinuxNetworkExtension extension = new LinuxNetworkExtension() LinuxSockstatExtension extension = new LinuxSockstatExtension()
LinuxNetworkSockStat stats = extension.processSockOutput(lines) LinuxNetworkSockStat stats = extension.processSockOutput(lines)
then: then:
@ -27,44 +26,4 @@ class LinuxNetworkTest extends Specification {
} }
void "test /proc/net/dev parsing"() {
setup:
def testFile = new File(getClass().getResource('/proc_net_dev1.txt').toURI())
List<String> lines = testFile.readLines("UTF-8")
when:
LinuxNetworkExtension extension = new LinuxNetworkExtension()
LinuxNetworkDevStat procLine = extension.processDevOutput(lines)
then:
procLine.getRxBytes() == 663911036L
procLine.getRxPackets() == 525522L
procLine.getRxErrs() == 0L
procLine.getTxBytes() == 63084294L
procLine.getTxPackets() == 472869L
procLine.getTxErrs() == 0L
}
/*
void "test dev utilization"() {
setup:
def testFile1 = new File(getClass().getResource('/proc_net_dev1.txt').toURI())
def testFile2 = new File(getClass().getResource('/proc_net_dev2.txt').toURI())
LinuxNetworkExtension extension = new LinuxNetworkExtension()
LinuxNetworkDevStat procLine1 = extension.processDevOutput(testFile1.readLines())
LinuxNetworkDevStat procLine2 = extension.processDevOutput(testFile2.readLines())
when:
LinuxNetworkDevStat networkDevStat = new LinuxNetworkDevStat(procLine1, procLine2)
then:
networkDevStat.getFields().get("rxPackets") == 223L
networkDevStat.getFields().get("rxBytes") == 31501L
networkDevStat.getFields().get("txBytes") == 46460L
networkDevStat.getFields().get("txPackets") == 341L
}*/
} }

View file

@ -1,48 +0,0 @@
import sysmon.plugins.os_linux.LinuxProcessorExtension
import sysmon.plugins.os_linux.LinuxProcessorProcLine
import sysmon.plugins.os_linux.LinuxProcessorStat
import spock.lang.Specification
class LinuxProcessorTest extends Specification {
void "test proc file processing"() {
setup:
def testFile = new File(getClass().getResource('/proc_stats1.txt').toURI())
List<String> lines = testFile.readLines("UTF-8")
when:
LinuxProcessorExtension extension = new LinuxProcessorExtension()
LinuxProcessorProcLine procLine = extension.processFileOutput(lines)
then:
procLine.getSystemTime() == 4686l
procLine.getUserTime() == 27477l
procLine.getIdleTime() == 281276l
procLine.getIoWaitTime() == 252l
}
void "test processor utilization"() {
setup:
def testFile1 = new File(getClass().getResource('/proc_stats1.txt').toURI())
def testFile2 = new File(getClass().getResource('/proc_stats2.txt').toURI())
LinuxProcessorProcLine processorProcLine1 = new LinuxProcessorProcLine(testFile1.readLines().get(0))
LinuxProcessorProcLine processorProcLine2 = new LinuxProcessorProcLine(testFile2.readLines().get(0))
when:
LinuxProcessorStat processorStat = new LinuxProcessorStat(processorProcLine1, processorProcLine2)
then:
processorStat.getBusy() == 38.001614f
processorStat.getFields().get("user") == 35.6989f
processorStat.getFields().get("sys") == 2.2623215f
processorStat.getFields().get("idle") == 61.823322f
processorStat.getFields().get("wait") == 0.17505646f
}
}

View file

@ -0,0 +1,112 @@
Ip:
Forwarding: 1
109772 total packets received
1 with invalid addresses
0 forwarded
0 incoming packets discarded
109769 incoming packets delivered
103916 requests sent out
70 outgoing packets dropped
1 dropped because of missing route
Icmp:
52 ICMP messages received
0 input ICMP message failed
ICMP input histogram:
destination unreachable: 40
echo requests: 12
108 ICMP messages sent
0 ICMP messages failed
ICMP output histogram:
destination unreachable: 96
echo replies: 12
IcmpMsg:
InType3: 40
InType8: 12
OutType0: 12
OutType3: 96
Tcp:
3142 active connection openings
5 passive connection openings
2105 failed connection attempts
193 connection resets received
70 connections established
86167 segments received
89891 segments sent out
184 segments retransmitted
3 bad segments received
2735 resets sent
Udp:
31928 packets received
96 packets to unknown port received
0 packet receive errors
10682 packets sent
0 receive buffer errors
0 send buffer errors
IgnoredMulti: 22
UdpLite:
TcpExt:
30 packets pruned from receive queue because of socket buffer overrun
178 TCP sockets finished time wait in fast timer
426 delayed acks sent
1 delayed acks further delayed because of locked socket
Quick ack mode was activated 1059 times
45809 packet headers predicted
7293 acknowledgments not containing data payload received
7659 predicted acknowledgments
TCPSackRecovery: 3
Detected reordering 4 times using SACK
TCPDSACKUndo: 1
1 congestion windows recovered without slow start after partial ack
TCPLostRetransmit: 82
3 timeouts after reno fast retransmit
1 timeouts in loss state
3 fast retransmits
3 retransmits in slow start
TCPTimeouts: 129
TCPLossProbes: 69
TCPLossProbeRecovery: 10
TCPBacklogCoalesce: 450
TCPDSACKOldSent: 991
TCPDSACKOfoSent: 6
TCPDSACKRecv: 45
202 connections reset due to unexpected data
147 connections reset due to early user close
13 connections aborted due to timeout
TCPDSACKIgnoredNoUndo: 10
TCPSackShifted: 1
TCPSackMerged: 1
TCPSackShiftFallback: 9
TCPRcvCoalesce: 5338
TCPOFOQueue: 793
TCPOFOMerge: 6
TCPChallengeACK: 3
TCPSYNChallenge: 3
TCPSpuriousRtxHostQueues: 6
TCPAutoCorking: 710
TCPFromZeroWindowAdv: 1
TCPToZeroWindowAdv: 1
TCPWantZeroWindowAdv: 4
TCPSynRetrans: 98
TCPOrigDataSent: 19048
TCPHystartTrainDetect: 3
TCPHystartTrainCwnd: 54
TCPHystartDelayDetect: 1
TCPHystartDelayCwnd: 24
TCPACKSkippedSeq: 1
TCPKeepAlive: 2595
TCPDelivered: 20025
TCPAckCompressed: 260
TcpTimeoutRehash: 116
IpExt:
InMcastPkts: 2257
OutMcastPkts: 480
InBcastPkts: 98
OutBcastPkts: 78
InOctets: 147193028
OutOctets: 14723163
InMcastOctets: 478599
OutMcastOctets: 73462
InBcastOctets: 10094
OutBcastOctets: 5580
InNoECTPkts: 177661
MPTcpExt:

View file

@ -36,9 +36,11 @@ public class ServerRouteBuilder extends RouteBuilder {
.to("seda:inbound") .to("seda:inbound")
.endRest(); .endRest();
//from("seda:inbound").log("Got metric from: ${header.component}").to("mock:sink"); //from("seda:inbound").log("Got metric from: ${header.component}").to("mock:sink");
from("seda:inbound") // TODO: Make 'concurrentConsumers' configurable
from("seda:inbound?concurrentConsumers=5")
.log(">>> metric: ${header.hostname} - ${body}") .log(">>> metric: ${header.hostname} - ${body}")
.doTry() .doTry()
.process(new MetricResultToPointProcessor()) .process(new MetricResultToPointProcessor())

View file

@ -2,6 +2,8 @@ package sysmon.shared;
import org.pf4j.ExtensionPoint; import org.pf4j.ExtensionPoint;
import java.io.IOException;
public interface MetricExtension extends ExtensionPoint { public interface MetricExtension extends ExtensionPoint {
boolean isSupported(); boolean isSupported();
@ -10,5 +12,5 @@ public interface MetricExtension extends ExtensionPoint {
String getProvides(); String getProvides();
String getDescription(); String getDescription();
MetricResult getMetrics(); MetricResult getMetrics() throws Exception;
} }

View file

@ -3,10 +3,7 @@ package sysmon.shared;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import java.io.BufferedReader; import java.io.*;
import java.io.File;
import java.io.IOException;
import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import java.nio.file.Files; import java.nio.file.Files;
import java.nio.file.Paths; import java.nio.file.Paths;
@ -23,10 +20,9 @@ public class PluginHelper {
.toLowerCase().startsWith("windows"); .toLowerCase().startsWith("windows");
public static List<String> executeCommand(String... cmd) { public static InputStream executeCommand(String... cmd) {
List<String> outputLines = new ArrayList<>();
InputStream inputStream = null;
ProcessBuilder builder = new ProcessBuilder(); ProcessBuilder builder = new ProcessBuilder();
if (isWindows) { if (isWindows) {
builder.command("cmd.exe", "/c"); builder.command("cmd.exe", "/c");
@ -40,15 +36,8 @@ public class PluginHelper {
builder.directory(new File(System.getProperty("user.home"))); builder.directory(new File(System.getProperty("user.home")));
try { try {
Process process = builder.start(); Process process = builder.start();
BufferedReader reader = inputStream = process.getInputStream();
new BufferedReader(new InputStreamReader(process.getInputStream()));
String line;
while ((line = reader.readLine()) != null) {
outputLines.add(line);
}
int exitCode = process.waitFor(); int exitCode = process.waitFor();
if(exitCode > 0) { if(exitCode > 0) {
@ -59,7 +48,7 @@ public class PluginHelper {
e.printStackTrace(); e.printStackTrace();
} }
return outputLines; return inputStream;
} }