Work on support for groovy scripts.

This commit is contained in:
Mark Nellemann 2022-12-21 17:12:15 +01:00
parent b07e949fb2
commit 31b494163d
63 changed files with 246 additions and 1086 deletions

View file

@ -2,6 +2,9 @@
All notable changes to this project will be documented in this file.
## [1.1.1] - 2022-12-xx
- Simplify plugin naming
## [1.1.0] - 2022-12-17
- Lower influx time precision from milliseconds to seconds
- requires you to update server and clients to this version.

View file

@ -23,6 +23,9 @@ dependencies {
exclude(group: "org.slf4j")
}
//implementation "org.apache.groovy:groovy-all:${groovyVersion}" // From version 4.+
implementation "org.codehaus.groovy:groovy:${groovyVersion}"
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-http', version: camelVersion

View file

@ -0,0 +1,3 @@
# Example Scripts
TODO.

View file

@ -0,0 +1,20 @@
import sysmon.shared.MetricResult
import sysmon.shared.MetricScript
import sysmon.shared.Measurement
class ExampleScript implements MetricScript {
@Override
MetricResult getMetrics() {
Map<String,String> tags = new TreeMap<>();
Map<String,Object> fields = new TreeMap<>();
tags.put("location", "blabla");
fields.put("temp1", 23);
fields.put("temp2", 25);
Measurement measurement = new Measurement(tags, fields);
return new MetricResult("script_example", measurement);
}
}

View file

@ -4,6 +4,9 @@
### Example configuration with some default values.
###
# Local path for Groovy scripts
scripts = "/opt/sysmon/scripts"
[extension.base_info]
enabled = true
interval = '60m'

View file

@ -14,15 +14,25 @@ import sysmon.shared.ComboResult;
import sysmon.shared.MetricExtension;
import sysmon.shared.MetricResult;
import javax.script.*;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;
public class ClientRouteBuilder extends RouteBuilder {
private static final Logger log = LoggerFactory.getLogger(ClientRouteBuilder.class);
private final Set<String> scriptFiles = new HashSet<>();
@Override
public void configure() {
@ -34,12 +44,9 @@ public class ClientRouteBuilder extends RouteBuilder {
pluginManager.loadPlugins();
pluginManager.startPlugins();
List<String> providers = new ArrayList<>();
List<MetricExtension> metricExtensions = pluginManager.getExtensions(MetricExtension.class);
for (MetricExtension ext : metricExtensions) {
final String name = ext.getName();
final String provides = ext.getProvides();
// Load configuration if available
if(configuration.isForExtension(name)) {
@ -48,33 +55,10 @@ public class ClientRouteBuilder extends RouteBuilder {
}
if(ext.isSupported() && ext.isEnabled()) {
if(providers.contains(provides)) {
log.warn("Skipping extension (already provided): " + ext.getName());
continue;
}
log.info("Enabling extension: " + ext.getDescription());
providers.add(provides);
// 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.
String timerName = ext.isThreaded() ? ext.getProvides() : "default";
String timerInterval = (ext.getInterval() != null) ? ext.getInterval() : "30s";
from("timer:"+timerName+"?fixedRate=true&period="+timerInterval)
.bean(ext, "getMetrics")
.outputType(MetricResult.class)
.process(new MetricEnrichProcessor(registry))
.choice().when(exchangeProperty("skip").isEqualTo(true))
.log(LoggingLevel.WARN,"Skipping empty measurement.")
.stop()
.otherwise()
.log("${body}")
.to("seda:metrics?discardWhenFull=true");
addExtensionRoute(ext);
} else {
log.info("Skipping extension (not supported or disabled): " + ext.getDescription());
}
}
from("seda:metrics?purgeWhenStopping=true")
@ -97,7 +81,97 @@ public class ClientRouteBuilder extends RouteBuilder {
.log(LoggingLevel.WARN,"Error: ${exception.message}.")
.end();
// Find all local scripts
String scriptsPath = configuration.getScriptPath();
if(scriptsPath != null && Files.isDirectory(Paths.get(scriptsPath))) {
try {
scriptFiles.addAll(listFilesByExtension(scriptsPath, "groovy"));
} catch (IOException e) {
throw new RuntimeException(e);
}
}
// Enable the local scripts
for (String scriptFile : scriptFiles) {
try {
ScriptWrapper scriptWrapper = new ScriptWrapper(scriptsPath, scriptFile);
addScriptRoute(scriptWrapper);
} catch(Exception e) {
log.error("configure() - script error: {}", e.getMessage());
}
}
}
void addScriptRoute(ScriptWrapper script) {
Registry registry = getContext().getRegistry();
from("timer:scripts?fixedRate=true&period=30s")
.bean(script, "run")
.outputType(MetricResult.class)
.process(new MetricEnrichProcessor(registry))
.choice().when(exchangeProperty("skip").isEqualTo(true))
.log(LoggingLevel.WARN, "Skipping empty measurement.")
.stop()
.otherwise()
.log("${body}")
.to("seda:metrics?discardWhenFull=true");
}
void addExtensionRoute(MetricExtension ext) {
Registry registry = getContext().getRegistry();
// 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.
String timerName = ext.isThreaded() ? ext.getName() : "default";
String timerInterval = (ext.getInterval() != null) ? ext.getInterval() : "30s";
from("timer:" + timerName + "?fixedRate=true&period=" + timerInterval)
.bean(ext, "getMetrics")
.outputType(MetricResult.class)
.process(new MetricEnrichProcessor(registry))
.choice().when(exchangeProperty("skip").isEqualTo(true))
.log(LoggingLevel.WARN, "Skipping empty measurement.")
.stop()
.otherwise()
.log("${body}")
.to("seda:metrics?discardWhenFull=true");
}
List<String> findScripts(String location) {
log.info("Looking for scripts in: {}", location);
List<String> scripts = new ArrayList<>();
ScriptEngineManager manager = new ScriptEngineManager();
List<ScriptEngineFactory> factoryList = manager.getEngineFactories();
for (ScriptEngineFactory factory : factoryList) {
log.info("findScripts() - Supporting: {}", factory.getLanguageName());
for(String ex : factory.getExtensions()) {
log.info("findScripts() - Extension: {}", ex);
try {
scripts.addAll(listFilesByExtension(location, ex));
log.warn(scripts.toString());
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
return scripts;
}
Set<String> listFilesByExtension(String dir, String ext) throws IOException {
try (Stream<Path> stream = Files.list(Paths.get(dir))) {
return stream
.filter(file -> !Files.isDirectory(file))
.map(Path::getFileName)
.map(Path::toString)
.filter(s -> s.endsWith(ext))
.collect(Collectors.toSet());
}
}
}

View file

@ -65,4 +65,10 @@ public final class Configuration {
return map;
}
String getScriptPath() {
return result.getString("scripts");
}
}

View file

@ -0,0 +1,40 @@
package sysmon.client;
import groovy.lang.GroovyClassLoader;
import groovy.lang.GroovyObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import sysmon.shared.MetricResult;
import sysmon.shared.MetricScript;
import java.io.File;
import java.io.IOException;
public class ScriptWrapper {
private static final Logger log = LoggerFactory.getLogger(ScriptWrapper.class);
private final static GroovyClassLoader loader = new GroovyClassLoader();
private GroovyObject script;
public ScriptWrapper(String scriptPath, String scriptFile) {
try {
Class scriptClass = loader.parseClass(new File(scriptPath, scriptFile));
script = (GroovyObject) scriptClass.newInstance();
} catch (IOException |InstantiationException | IllegalAccessException e) {
log.error("ScriptWrapper() - error: {}", e.getMessage());
}
}
MetricResult run() {
MetricResult result = null;
if (script != null && script instanceof MetricScript) {
result = (MetricResult) script.invokeMethod("getMetrics", null);
}
return result;
}
}

View file

@ -2,6 +2,5 @@
Collection of standard sysmon plugins for use with the client.
- [base](os-base/README.md) - Base OS metrics (uses [oshi](https://github.com/oshi/oshi))
- [aix](os-aix/README.md) - AIX (and IBM Power) specific metrics
- [linux](os-linux/README.md) - Linux specific metrics
- [base](base/README.md) - Base OS metrics (uses [oshi](https://github.com/oshi/oshi))
- [power](power/README.md) - IBM Power specific metrics

View file

@ -1,5 +1,5 @@
pluginId=sysmon-base
pluginClass=sysmon.plugins.os_base.BasePlugin
pluginClass=sysmon.plugins.base.BasePlugin
pluginDependencies=
pluginDescription=Base OS metrics where supported.

View file

@ -1,4 +1,4 @@
package sysmon.plugins.os_base;
package sysmon.plugins.base;
import org.pf4j.Extension;
import org.slf4j.Logger;
@ -18,7 +18,6 @@ public class BaseDiskExtension implements MetricExtension {
// Extension details
private final String name = "base_disk";
private final String provides = "disk";
private final String description = "Base Disk Metrics";
// Configuration / Options
@ -57,11 +56,6 @@ public class BaseDiskExtension implements MetricExtension {
return interval;
}
@Override
public String getProvides() {
return provides;
}
@Override
public String getDescription() {
return description;

View file

@ -1,4 +1,4 @@
package sysmon.plugins.os_base;
package sysmon.plugins.base;
import org.pf4j.Extension;
import org.slf4j.Logger;
@ -19,7 +19,6 @@ public class BaseFilesystemExtension implements MetricExtension {
// Extension details
private final String name = "base_filesystem";
private final String provides = "filesystem";
private final String description = "Base Filesystem Metrics";
// Configuration / Options
@ -67,11 +66,6 @@ public class BaseFilesystemExtension implements MetricExtension {
return interval;
}
@Override
public String getProvides() {
return provides;
}
@Override
public String getDescription() {
return description;

View file

@ -1,4 +1,4 @@
package sysmon.plugins.os_base;
package sysmon.plugins.base;
import org.pf4j.Extension;
import org.slf4j.Logger;
@ -19,7 +19,6 @@ public class BaseInfoExtension implements MetricExtension {
// Extension details
private final String name = "base_info";
private final String provides = "info";
private final String description = "Base System Information";
// Configuration / Options
@ -52,11 +51,6 @@ public class BaseInfoExtension implements MetricExtension {
return name;
}
@Override
public String getProvides() {
return provides;
}
@Override
public String getInterval() { return interval; }

View file

@ -1,4 +1,4 @@
package sysmon.plugins.os_base;
package sysmon.plugins.base;
import org.pf4j.Extension;
import org.slf4j.Logger;
@ -8,7 +8,6 @@ import sysmon.shared.Measurement;
import sysmon.shared.MetricExtension;
import sysmon.shared.MetricResult;
import java.util.HashMap;
import java.util.Map;
import java.util.TreeMap;
@ -19,7 +18,6 @@ public class BaseLoadExtension implements MetricExtension {
// Extension details
private final String name = "base_load";
private final String provides = "load";
private final String description = "Base Load Average Metrics";
// Configuration / Options
@ -55,11 +53,6 @@ public class BaseLoadExtension implements MetricExtension {
return interval;
}
@Override
public String getProvides() {
return provides;
}
@Override
public String getDescription() {
return description;

View file

@ -1,4 +1,4 @@
package sysmon.plugins.os_base;
package sysmon.plugins.base;
import org.pf4j.Extension;
import org.slf4j.Logger;
@ -8,7 +8,6 @@ import sysmon.shared.Measurement;
import sysmon.shared.MetricExtension;
import sysmon.shared.MetricResult;
import java.util.HashMap;
import java.util.Map;
import java.util.TreeMap;
@ -19,7 +18,6 @@ public class BaseMemoryExtension implements MetricExtension {
// Extension details
private final String name = "base_memory";
private final String provides = "memory";
private final String description = "Base Memory Metrics";
// Configuration / Options
@ -56,11 +54,6 @@ public class BaseMemoryExtension implements MetricExtension {
return interval;
}
@Override
public String getProvides() {
return provides;
}
@Override
public String getDescription() {
return description;

View file

@ -1,4 +1,4 @@
package sysmon.plugins.os_base;
package sysmon.plugins.base;
import org.pf4j.Extension;
import org.slf4j.Logger;
@ -8,7 +8,6 @@ import sysmon.shared.Measurement;
import sysmon.shared.MetricExtension;
import sysmon.shared.MetricResult;
import java.util.HashMap;
import java.util.Map;
import java.util.TreeMap;
@ -19,7 +18,6 @@ public class BaseNetstatExtension implements MetricExtension {
// Extension details
private final String name = "base_netstat";
private final String provides = "netstat";
private final String description = "Base Netstat Metrics";
// Configuration / Options
@ -56,11 +54,6 @@ public class BaseNetstatExtension implements MetricExtension {
return interval;
}
@Override
public String getProvides() {
return provides;
}
@Override
public String getDescription() {
return description;

View file

@ -1,4 +1,4 @@
package sysmon.plugins.os_base;
package sysmon.plugins.base;
import org.pf4j.Extension;
import org.slf4j.Logger;
@ -18,7 +18,6 @@ public class BaseNetworkExtension implements MetricExtension {
// Extension details
private final String name = "base_network";
private final String provides = "network";
private final String description = "Base Network Metrics";
// Configuration / Options
@ -57,11 +56,6 @@ public class BaseNetworkExtension implements MetricExtension {
return interval;
}
@Override
public String getProvides() {
return provides;
}
@Override
public String getDescription() {
return description;

View file

@ -1,4 +1,4 @@
package sysmon.plugins.os_base;
package sysmon.plugins.base;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

View file

@ -1,4 +1,4 @@
package sysmon.plugins.os_base;
package sysmon.plugins.base;
import org.pf4j.Extension;
import org.slf4j.Logger;
@ -18,7 +18,6 @@ public class BaseProcessExtension implements MetricExtension {
// Extension details
private final String name = "base_process";
private final String provides = "process";
private final String description = "Base Process Metrics";
// Configuration / Options
@ -75,11 +74,6 @@ public class BaseProcessExtension implements MetricExtension {
return interval;
}
@Override
public String getProvides() {
return provides;
}
@Override
public String getDescription() {
return description;

View file

@ -1,4 +1,4 @@
package sysmon.plugins.os_base;
package sysmon.plugins.base;
import org.pf4j.Extension;
import org.slf4j.Logger;
@ -10,7 +10,6 @@ import sysmon.shared.MetricExtension;
import sysmon.shared.MetricResult;
import sysmon.shared.PluginHelper;
import java.util.HashMap;
import java.util.Map;
import java.util.TreeMap;
@ -21,7 +20,6 @@ public class BaseProcessorExtension implements MetricExtension {
// Extension details
private final String name = "base_processor";
private final String provides = "processor";
private final String description = "Base Processor Metrics";
// Configuration / Options
@ -58,11 +56,6 @@ public class BaseProcessorExtension implements MetricExtension {
return interval;
}
@Override
public String getProvides() {
return provides;
}
@Override
public String getDescription() {
return description;

View file

@ -1,6 +0,0 @@
pluginId=sysmon-aix
pluginClass=sysmon.plugins.os_aix.AixPlugin
pluginVersion=0.0.1
pluginProvider=System Monitor
pluginDependencies=
pluginDescription=Collects AIX OS metrics.

View file

@ -1,18 +0,0 @@
package sysmon.plugins.os_aix;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.pf4j.Plugin;
import org.pf4j.PluginWrapper;
public class AixPlugin extends Plugin {
private static final Logger log = LoggerFactory.getLogger(AixPlugin.class);
public AixPlugin(PluginWrapper wrapper) {
super(wrapper);
}
}

View file

@ -1,4 +0,0 @@
# IBM i Plugin
This is just for testing purposes.

View file

@ -1,7 +0,0 @@
plugins {
}
dependencies {
// https://sourceforge.net/projects/jt400/ and http://jt400.sourceforge.net/
implementation group: 'net.sf.jt400', name: 'jt400', version: '11.0'
}

View file

@ -1,4 +0,0 @@
pluginId=sysmon-ibmi
pluginClass=sysmon.plugins.os_ibmi.IbmIPlugin
pluginDependencies=
pluginDescription=Collects IBM-i OS metrics.

View file

@ -1,97 +0,0 @@
package sysmon.plugins.os_ibmi;
import com.ibm.as400.access.AS400;
import com.ibm.as400.access.SystemStatus;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.pf4j.Plugin;
import org.pf4j.PluginWrapper;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
public class IbmIPlugin extends Plugin {
private static final Logger log = LoggerFactory.getLogger(IbmIPlugin.class);
private static AS400 as400;
private static SystemStatus systemStatus;
private static Connection connection;
public IbmIPlugin(PluginWrapper wrapper) {
super(wrapper);
}
public static AS400 getAS400() {
if(as400 != null) {
return as400;
}
// Check platform
String osArch = System.getProperty("os.arch").toLowerCase();
String osName = System.getProperty("os.name").toLowerCase();
if(!osArch.equals("ppc64") && !osName.equals("os/400")) {
log.info("getAS400() - OS Arch: {}", osArch);
log.info("getAS400() - OS Name: {}", osName);
return null;
}
try {
as400 = new AS400("localhost", "*CURRENT");
//as400 = new AS400("localhost", "*LOCAL");
//as400 = new AS400("10.32.64.142");
return as400;
} catch (Exception exception) {
log.error("getAS400() - {}", exception.getMessage());
}
return null;
}
public static SystemStatus getSystemStatus() {
if(systemStatus != null) {
return systemStatus;
}
try {
if (as400 == null) {
as400 = IbmIPlugin.getAS400();
}
if(systemStatus == null && as400 != null) {
systemStatus = new SystemStatus(as400);
return systemStatus;
}
} catch (Exception exception) {
log.error("getSystemStatus() - {}", exception.getMessage());
}
return null;
}
public static Connection getConnection() {
if(connection != null) {
return connection;
}
try {
DriverManager.registerDriver(new com.ibm.as400.access.AS400JDBCDriver());
connection = DriverManager.getConnection("jdbc:as400://localhost");
return connection;
} catch (SQLException exception) {
log.error("getConnection() - {}", exception.getMessage());
}
return null;
}
}

View file

@ -1,146 +0,0 @@
package sysmon.plugins.os_ibmi;
import com.ibm.as400.access.*;
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.util.HashMap;
import java.util.Map;
import java.util.TreeMap;
//@Extension
public class TestExtension implements MetricExtension {
private static final Logger log = LoggerFactory.getLogger(TestExtension.class);
// Extension details
private final String name = "ibmi_test";
private final String provides = "ibmi_test";
private final String description = "IBM i Test Metrics";
// Configuration / Options
private boolean enabled = true;
private boolean threaded = false;
private String interval = "10s";
private SystemStatus systemStatus;
@Override
public boolean isEnabled() {
return enabled;
}
@Override
public boolean isThreaded() {
return threaded;
}
@Override
public boolean isSupported() {
systemStatus = IbmIPlugin.getSystemStatus();
return systemStatus != null;
}
@Override
public String getName() {
return name;
}
@Override
public String getInterval() {
return interval;
}
@Override
public String getProvides() {
return provides;
}
@Override
public String getDescription() {
return description;
}
@Override
public void setConfiguration(Map<String, Object> map) {
if (map.containsKey("enabled")) {
enabled = (boolean) map.get("enabled");
}
if(map.containsKey("threaded")) {
threaded = (boolean) map.get("threaded");
}
if(map.containsKey("interval")) {
interval = (String) map.get("interval");
}
}
@Override
public MetricResult getMetrics() {
if(systemStatus == null) {
log.warn("getMetrics() - no system or status");
return null;
}
/* const hSql = "select
SERVER_NAME,
HTTP_FUNCTION,
SERVER_NORMAL_CONNECTIONS,
SERVER_ACTIVE_THREADS,
SERVER_IDLE_THREADS,
BYTES_RECEIVED,
BYTES_SENT,
NONCACHE_PROCESSING_TIME,
CACHE_PROCESSING_TIME
from
QSYS2.HTTP_SERVER_INFO";
*/
try {
int jobsInSystem = systemStatus.getJobsInSystem();
log.info("Jobs In System: {}", jobsInSystem);
int batchJobsRunning = systemStatus.getBatchJobsRunning();
log.info("Batch Jobs Running: {}", batchJobsRunning);
int activeThreads = systemStatus.getActiveThreadsInSystem();
log.info("Active Threads: {}", activeThreads);
int activeJobs = systemStatus.getActiveJobsInSystem();
log.info("Active Jobs: {}", activeJobs);
int onlineUsers = systemStatus.getUsersCurrentSignedOn();
log.info("Online Users: {}", onlineUsers);
// The storage capacity of the system auxiliary storage pool (ASP1) in MBytes.
long systemAsp = systemStatus.getSystemASP();
System.out.println("Current Processing Capacity :" + systemStatus.getCurrentProcessingCapacity());
System.out.println("ASPUsed:" + systemStatus.getPercentSystemASPUsed());
System.out.println("Temp Addresses Used:" + systemStatus.getPercentTemporaryAddresses());
TreeMap<String, Object> fieldsMap = new TreeMap<String, Object>() {{
put("jobs_total", jobsInSystem);
put("jobs_running", batchJobsRunning);
put("jobs_active", activeJobs);
put("threads", activeThreads);
put("users", onlineUsers);
}};
return new MetricResult(name, new Measurement(new TreeMap<>(), fieldsMap));
} catch (AS400SecurityException | ErrorCompletingRequestException | InterruptedException | IOException | ObjectDoesNotExistException e) {
log.error("getMetrics() {}", e.getMessage());
e.printStackTrace();
}
return null;
}
}

View file

@ -1,7 +0,0 @@
# Linux Plugins
## Components
### Network Sockets
Collects statistics from */proc/net/sockstats*.

View file

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

View file

@ -1,5 +0,0 @@
pluginId=sysmon-linux
pluginClass=sysmon.plugins.os_linux.LinuxPlugin
pluginDependencies=
pluginDescription=Linux OS Metrics.

View file

@ -1,109 +0,0 @@
package sysmon.plugins.os_linux;
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.HashMap;
import java.util.Map;
import java.util.TreeMap;
// Disabled
//@Extension
public class LinuxNetstatExtension implements MetricExtension {
private static final Logger log = LoggerFactory.getLogger(LinuxNetstatExtension.class);
// Extension details
private final String name = "linux_network_netstat";
private final String provides = "network_netstat";
private final String description = "Linux Netstat Metrics";
// Configuration / Options
private boolean enabled = true;
private boolean threaded = false;
@Override
public boolean isEnabled() {
return enabled;
}
@Override
public boolean isThreaded() {
return threaded;
}
@Override
public boolean isSupported() {
if(!System.getProperty("os.name").toLowerCase().contains("linux")) {
log.warn("Requires Linux.");
return false;
}
if(PluginHelper.notExecutable("netstat")) {
log.warn("Requires the 'netstat' command.");
return false;
}
return true;
}
@Override
public String getName() {
return name;
}
@Override
public String getInterval() {
return null;
}
@Override
public String getProvides() {
return provides;
}
@Override
public String getDescription() {
return description;
}
@Override
public void setConfiguration(Map<String, Object> map) {
if (map.containsKey("enabled")) {
enabled = (boolean) map.get("enabled");
}
if(map.containsKey("threaded")) {
threaded = (boolean) map.get("threaded");
}
}
@Override
public MetricResult getMetrics() throws Exception {
TreeMap<String, String> tagsMap;
TreeMap<String, Object> fieldsMap;
try (InputStream inputStream = PluginHelper.executeCommand("netstat -s")) {
LinuxNetstatParser parser = processCommandOutput(inputStream);
tagsMap = parser.getTags();
fieldsMap = parser.getFields();
}
return new MetricResult(name, new Measurement(tagsMap, fieldsMap));
}
protected LinuxNetstatParser processCommandOutput(InputStream input) throws IOException {
return new LinuxNetstatParser(input);
}
}

View file

@ -1,165 +0,0 @@
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.TreeMap;
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 TreeMap<String, String> getTags() {
return new TreeMap<>();
}
public TreeMap<String, Object> getFields() {
return new TreeMap<String, Object>() {{
put("ip_forwarded", ipForwarded);
put("ip_received", ipTotalPacketsReceived);
put("ip_dropped", ipOutgoingPacketsDropped);
put("ip_discarded", ipIncomingPacketsDiscarded);
put("tcp_connections", tcpConnectionsEstablished);
put("tcp_pkts_recv", tcpSegmentsReceived);
put("tcp_pkts_sent", tcpSegmentsSent);
put("udp_pkts_recv", udpPacketsReceived);
put("udp_pkts_sent", udpPacketsSent);
}};
}
private Long getFirstLong(String line) {
return Long.parseLong(line.substring(0, line.indexOf(" ")));
}
}

View file

@ -1,17 +0,0 @@
package sysmon.plugins.os_linux;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.pf4j.Plugin;
import org.pf4j.PluginWrapper;
public class LinuxPlugin extends Plugin {
private static final Logger log = LoggerFactory.getLogger(LinuxPlugin.class);
public LinuxPlugin(PluginWrapper wrapper) {
super(wrapper);
}
}

View file

@ -1,98 +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.HashMap;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
@Extension
public class LinuxSocketExtension implements MetricExtension {
private static final Logger log = LoggerFactory.getLogger(LinuxSocketExtension.class);
// Extension details
private final String name = "linux_network_sockets";
private final String provides = "network_sockets";
private final String description = "Linux Network Socket Metrics";
// Configuration / Options
private boolean enabled = true;
private boolean threaded = false;
@Override
public boolean isEnabled() {
return enabled;
}
@Override
public boolean isThreaded() {
return threaded;
}
@Override
public boolean isSupported() {
if(!System.getProperty("os.name").toLowerCase().contains("linux")) {
log.debug("Requires Linux.");
return false;
}
return true;
}
@Override
public String getName() {
return name;
}
@Override
public String getInterval() {
return null;
}
@Override
public String getProvides() {
return provides;
}
@Override
public String getDescription() {
return description;
}
@Override
public void setConfiguration(Map<String, Object> map) {
if (map.containsKey("enabled")) {
enabled = (boolean) map.get("enabled");
}
if(map.containsKey("threaded")) {
threaded = (boolean) map.get("threaded");
}
}
@Override
public MetricResult getMetrics() {
LinuxSocketStat sockStat = processSockOutput(PluginHelper.readFile("/proc/net/sockstat"));
TreeMap<String, String> tagsMap = sockStat.getTags();
TreeMap<String, Object> fieldsMap = sockStat.getFields();
log.debug("getMetrics() - tags: {}, fields: {}", tagsMap, fieldsMap);
return new MetricResult(name, new Measurement(tagsMap, fieldsMap));
}
protected LinuxSocketStat processSockOutput(List<String> inputLines) {
return new LinuxSocketStat(inputLines);
}
}

View file

@ -1,97 +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.TreeMap;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class LinuxSocketStat {
private static final Logger log = LoggerFactory.getLogger(LinuxSocketStat.class);
private static final Pattern pattern1 = Pattern.compile("^sockets: used (\\d+)");
private static final Pattern pattern2 = Pattern.compile("^TCP: inuse (\\d+) orphan (\\d+) tw (\\d+) alloc (\\d+) mem (\\d+)");
private static final Pattern pattern3 = Pattern.compile("^UDP: inuse (\\d+) mem (\\d+)");
private long sockets;
private long tcp_inuse;
private long tcp_orphan;
private long tcp_tw;
private long tcp_alloc;
private long tcp_mem;
private long udp_inuse;
private long udp_mem;
/*
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
*/
LinuxSocketStat(List<String> lines) {
Matcher matcher;
for(String line : lines) {
String proto = line.substring(0, line.indexOf(':'));
switch (proto) {
case "sockets":
matcher = pattern1.matcher(line);
if (matcher.matches() && matcher.groupCount() == 1) {
sockets = Long.parseLong(matcher.group(1));
}
break;
case "TCP":
matcher = pattern2.matcher(line);
if (matcher.matches() && matcher.groupCount() == 5) {
tcp_inuse = Long.parseLong(matcher.group(1));
tcp_orphan = Long.parseLong(matcher.group(2));
tcp_tw = Long.parseLong(matcher.group(3));
tcp_alloc = Long.parseLong(matcher.group(4));
tcp_mem = Long.parseLong(matcher.group(5));
}
break;
case "UDP":
matcher = pattern3.matcher(line);
if (matcher.matches() && matcher.groupCount() == 2) {
udp_inuse = Long.parseLong(matcher.group(1));
udp_mem = Long.parseLong(matcher.group(2));
}
break;
}
}
}
public TreeMap<String, String> getTags() {
return new TreeMap<>();
}
public TreeMap<String, Object> getFields() {
return new TreeMap<String, Object>() {{
put("sockets", sockets);
put("tcp_inuse", tcp_inuse);
put("tcp_alloc", tcp_alloc);
put("tcp_orphan", tcp_orphan);
put("tcp_mem", tcp_mem);
put("tcp_tw", tcp_tw);
put("udp_inuse", udp_inuse);
put("udp_mem", udp_mem);
}};
}
}

View file

@ -1,26 +0,0 @@
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,29 +0,0 @@
import spock.lang.Specification
import sysmon.plugins.os_linux.LinuxSocketExtension
import sysmon.plugins.os_linux.LinuxSocketStat
class LinuxNetworkTest extends Specification {
void "test /proc/net/sockstat parsing"() {
setup:
def testFile = new File(getClass().getResource('/proc_net_sockstat.txt').toURI())
List<String> lines = testFile.readLines("UTF-8")
when:
LinuxSocketExtension extension = new LinuxSocketExtension()
LinuxSocketStat stats = extension.processSockOutput(lines)
then:
stats.getFields().get("sockets") == 1238L
stats.getFields().get("tcp_inuse") == 52L
stats.getFields().get("tcp_orphan") == 0L
stats.getFields().get("tcp_alloc") == 55L
stats.getFields().get("tcp_mem") == 7l
stats.getFields().get("tcp_tw") == 18L
stats.getFields().get("udp_inuse") == 11L
stats.getFields().get("udp_mem") == 10L
}
}

View file

@ -1,112 +0,0 @@
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

@ -1,6 +0,0 @@
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,6 @@
pluginId=sysmon-power
pluginClass=sysmon.plugins.power.PowerPlugin
pluginVersion=0.0.1
pluginProvider=System Monitor
pluginDependencies=
pluginDescription=Collects IBM Power specific metrics.

View file

@ -0,0 +1,18 @@
package sysmon.plugins.power;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.pf4j.Plugin;
import org.pf4j.PluginWrapper;
public class PowerPlugin extends Plugin {
private static final Logger log = LoggerFactory.getLogger(PowerPlugin.class);
public PowerPlugin(PluginWrapper wrapper) {
super(wrapper);
}
}

View file

@ -1,4 +1,4 @@
package sysmon.plugins.os_aix;
package sysmon.plugins.power;
import org.pf4j.Extension;
import org.slf4j.Logger;
@ -10,19 +10,17 @@ import sysmon.shared.PluginHelper;
import java.io.IOException;
import java.io.InputStream;
import java.util.HashMap;
import java.util.Map;
import java.util.TreeMap;
@Extension
public class AixProcessorExtension implements MetricExtension {
public class PowerProcessorExtension implements MetricExtension {
private static final Logger log = LoggerFactory.getLogger(AixProcessorExtension.class);
private static final Logger log = LoggerFactory.getLogger(PowerProcessorExtension.class);
// Extension details
private final String name = "aix_processor";
private final String provides = "lpar_processor";
private final String description = "AIX Processor Metrics";
private final String name = "power_processor";
private final String description = "IBM Power Processor Metrics";
// Configuration / Options
private boolean enabled = true;
@ -66,11 +64,6 @@ public class AixProcessorExtension implements MetricExtension {
return interval;
}
@Override
public String getProvides() {
return provides;
}
@Override
public String getDescription() {
return description;
@ -96,7 +89,7 @@ public class AixProcessorExtension implements MetricExtension {
TreeMap<String, Object> fieldsMap = null;
try (InputStream buf = PluginHelper.executeCommand("lparstat 3 1")) {
AixProcessorStat processorStat = processCommandOutput(buf);
PowerProcessorStat processorStat = processCommandOutput(buf);
tagsMap = processorStat.getTags();
fieldsMap = processorStat.getFields();
} catch (IOException e) {
@ -108,8 +101,8 @@ public class AixProcessorExtension implements MetricExtension {
}
protected AixProcessorStat processCommandOutput(InputStream input) throws IOException {
return new AixProcessorStat(input);
protected PowerProcessorStat processCommandOutput(InputStream input) throws IOException {
return new PowerProcessorStat(input);
}
}

View file

@ -1,4 +1,4 @@
package sysmon.plugins.os_aix;
package sysmon.plugins.power;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -8,15 +8,14 @@ import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Objects;
import java.util.TreeMap;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class AixProcessorStat {
public class PowerProcessorStat {
private static final Logger log = LoggerFactory.getLogger(AixProcessorStat.class);
private static final Logger log = LoggerFactory.getLogger(PowerProcessorStat.class);
// System configuration: type=Shared mode=Uncapped smt=8 lcpu=8 mem=4096MB psize=19 ent=0.50
private static final Pattern patternAixShared = Pattern.compile("^System configuration: type=(\\S+) mode=(\\S+) smt=(\\d+) lcpu=(\\d+) mem=(\\d+)MB psize=(\\d+) ent=(\\d+\\.?\\d*)");
@ -47,7 +46,7 @@ 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.
public AixProcessorStat(InputStream inputStream) throws IOException {
public PowerProcessorStat(InputStream inputStream) throws IOException {
String lastLine = null;
BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));

View file

@ -1,8 +1,8 @@
import sysmon.plugins.os_aix.AixProcessorExtension
import sysmon.plugins.os_aix.AixProcessorStat
import sysmon.plugins.power.PowerProcessorExtension
import sysmon.plugins.power.PowerProcessorStat
import spock.lang.Specification
class AixProcessorTest extends Specification {
class PowerProcessorTest extends Specification {
void "test AIX lparstat shared output processing"() {
@ -10,8 +10,8 @@ class AixProcessorTest extends Specification {
InputStream inputStream = getClass().getResourceAsStream('/lparstat-aix-shared.txt')
when:
AixProcessorExtension extension = new AixProcessorExtension()
AixProcessorStat stats = extension.processCommandOutput(inputStream)
PowerProcessorExtension extension = new PowerProcessorExtension()
PowerProcessorStat stats = extension.processCommandOutput(inputStream)
then:
stats.getUser() == 83.7f
@ -30,8 +30,8 @@ class AixProcessorTest extends Specification {
InputStream inputStream = getClass().getResourceAsStream('/lparstat-aix-dedicated-donating.txt')
when:
AixProcessorExtension extension = new AixProcessorExtension()
AixProcessorStat stats = extension.processCommandOutput(inputStream)
PowerProcessorExtension extension = new PowerProcessorExtension()
PowerProcessorStat stats = extension.processCommandOutput(inputStream)
then:
stats.getUser() == 0.1f
@ -51,8 +51,8 @@ class AixProcessorTest extends Specification {
InputStream inputStream = getClass().getResourceAsStream('/lparstat-aix-dedicated-capped.txt')
when:
AixProcessorExtension extension = new AixProcessorExtension()
AixProcessorStat stats = extension.processCommandOutput(inputStream)
PowerProcessorExtension extension = new PowerProcessorExtension()
PowerProcessorStat stats = extension.processCommandOutput(inputStream)
then:
stats.getUser() == 0.0f
@ -71,8 +71,8 @@ class AixProcessorTest extends Specification {
InputStream inputStream = getClass().getResourceAsStream('/lparstat-linux.txt')
when:
AixProcessorExtension extension = new AixProcessorExtension()
AixProcessorStat stats = extension.processCommandOutput(inputStream)
PowerProcessorExtension extension = new PowerProcessorExtension()
PowerProcessorStat stats = extension.processCommandOutput(inputStream)
then:
stats.getUser() == 0.03f

View file

@ -12,7 +12,6 @@ public interface MetricExtension extends ExtensionPoint {
String getName();
String getInterval();
String getProvides();
String getDescription();
void setConfiguration(Map<String, Object> map);

View file

@ -0,0 +1,7 @@
package sysmon.shared;
public interface MetricScript {
MetricResult getMetrics();
}