diff --git a/CHANGELOG.md b/CHANGELOG.md index 53849ca..fbcc81d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,10 @@ All notable changes to this project will be documented in this file. +## 1.4.5 - 2023-11-13 +- Adjust timeout to not have lingering sessions on HMC +- Update 3rd party dependencies + ## 1.4.4 - 2023-05-20 - Support for InfluxDB v2, now requires InfluxDB 1.8 or later - Increase influx writer buffer limit diff --git a/build.gradle b/build.gradle index 57476aa..486c2e8 100644 --- a/build.gradle +++ b/build.gradle @@ -4,7 +4,7 @@ plugins { id 'groovy' id 'application' id "net.nemerosa.versioning" version "2.15.1" - id "com.netflix.nebula.ospackage" version "11.2.0" + id "com.netflix.nebula.ospackage" version "11.5.0" id "com.github.johnrengelman.shadow" version "7.1.2" } @@ -17,15 +17,15 @@ group = projectGroup version = projectVersion dependencies { - annotationProcessor 'info.picocli:picocli-codegen:4.7.3' - implementation 'info.picocli:picocli:4.7.3' - implementation 'org.slf4j:slf4j-api:2.0.7' - implementation 'org.slf4j:slf4j-simple:2.0.7' - implementation 'com.squareup.okhttp3:okhttp:4.10.0' // Also used by InfluxDB Client - implementation 'com.influxdb:influxdb-client-java:6.8.0' - implementation 'com.fasterxml.jackson.core:jackson-databind:2.14.3' - implementation 'com.fasterxml.jackson.dataformat:jackson-dataformat-xml:2.14.3' - implementation 'com.fasterxml.jackson.dataformat:jackson-dataformat-toml:2.14.3' + annotationProcessor 'info.picocli:picocli-codegen:4.7.5' + implementation 'info.picocli:picocli:4.7.5' + implementation 'org.slf4j:slf4j-api:2.0.9' + implementation 'org.slf4j:slf4j-simple:2.0.9' + implementation 'com.squareup.okhttp3:okhttp:4.11.0' // Also used by InfluxDB Client + implementation 'com.influxdb:influxdb-client-java:6.10.0' + implementation 'com.fasterxml.jackson.core:jackson-databind:2.15.2' + implementation 'com.fasterxml.jackson.dataformat:jackson-dataformat-xml:2.15.2' + implementation 'com.fasterxml.jackson.dataformat:jackson-dataformat-toml:2.15.2' testImplementation 'junit:junit:4.13.2' testImplementation 'org.spockframework:spock-core:2.3-groovy-4.0' diff --git a/doc/readme-hmc.md b/doc/readme-hmc.md index 610c6be..6b889fe 100644 --- a/doc/readme-hmc.md +++ b/doc/readme-hmc.md @@ -11,9 +11,9 @@ Ensure you have **correct date/time** and NTPd running to keep it accurate! - Navigate to *Users and Security* - Create a new read-only/viewer **hmci** user, which will be used to connect to the HMC. - Click *Manage User Profiles and Access*, edit the newly created *hmci* user and click *User Properties*: - - Set *Session timeout minutes* to **60** + - Set *Session timeout minutes* to **120** (or at least 61 minutes) - Set *Verify timeout minutes* to **15** - - Set *Idle timeout minutes* to **90** + - Set *Idle timeout minutes* to **15** - Set *Minimum time in days between password changes* to **0** - **Enable** *Allow remote access via the web* - Navigate to *HMC Management* and *Console Settings* diff --git a/gradle.properties b/gradle.properties index 91253d0..842f543 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,3 +1,3 @@ projectId = hmci projectGroup = biz.nellemann.hmci -projectVersion = 1.4.4 +projectVersion = 1.4.5 diff --git a/src/main/java/biz/nellemann/hmci/Application.java b/src/main/java/biz/nellemann/hmci/Application.java index 9b6abb4..135ce28 100644 --- a/src/main/java/biz/nellemann/hmci/Application.java +++ b/src/main/java/biz/nellemann/hmci/Application.java @@ -15,17 +15,19 @@ */ package biz.nellemann.hmci; -import biz.nellemann.hmci.dto.toml.Configuration; -import com.fasterxml.jackson.dataformat.toml.TomlMapper; -import picocli.CommandLine; -import picocli.CommandLine.Option; -import picocli.CommandLine.Command; - import java.io.File; +import java.io.IOException; import java.util.ArrayList; import java.util.List; import java.util.concurrent.Callable; +import com.fasterxml.jackson.dataformat.toml.TomlMapper; + +import biz.nellemann.hmci.dto.toml.Configuration; +import picocli.CommandLine; +import picocli.CommandLine.Command; +import picocli.CommandLine.Option; + @Command(name = "hmci", mixinStandardHelpOptions = true, versionProvider = biz.nellemann.hmci.VersionProvider.class, @@ -90,7 +92,7 @@ public class Application implements Callable { } influxClient.logoff(); - } catch (Exception e) { + } catch (IOException | InterruptedException e) { System.err.println(e.getMessage()); return 1; } diff --git a/src/main/java/biz/nellemann/hmci/InfluxClient.java b/src/main/java/biz/nellemann/hmci/InfluxClient.java index 90803b7..ae63e80 100644 --- a/src/main/java/biz/nellemann/hmci/InfluxClient.java +++ b/src/main/java/biz/nellemann/hmci/InfluxClient.java @@ -15,20 +15,21 @@ */ package biz.nellemann.hmci; -import biz.nellemann.hmci.dto.toml.InfluxConfiguration; +import static java.lang.Thread.sleep; +import java.util.ArrayList; +import java.util.List; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + import com.influxdb.client.InfluxDBClient; import com.influxdb.client.InfluxDBClientFactory; import com.influxdb.client.WriteApi; import com.influxdb.client.WriteOptions; -import com.influxdb.client.write.Point; import com.influxdb.client.domain.WritePrecision; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; +import com.influxdb.client.write.Point; -import java.util.ArrayList; -import java.util.List; - -import static java.lang.Thread.sleep; +import biz.nellemann.hmci.dto.toml.InfluxConfiguration; public final class InfluxClient { @@ -83,10 +84,10 @@ public final class InfluxClient { Runtime.getRuntime().addShutdownHook(new Thread(influxDBClient::close)); // Todo: Handle events - https://github.com/influxdata/influxdb-client-java/tree/master/client#handle-the-events - //writeApi = influxDBClient.makeWriteApi(); writeApi = influxDBClient.makeWriteApi( WriteOptions.builder() - .bufferLimit(20_000) + .batchSize(15_000) + .bufferLimit(500_000) .flushInterval(5_000) .build()); diff --git a/src/main/java/biz/nellemann/hmci/ManagementConsole.java b/src/main/java/biz/nellemann/hmci/ManagementConsole.java index c0fe494..c2fc0fe 100644 --- a/src/main/java/biz/nellemann/hmci/ManagementConsole.java +++ b/src/main/java/biz/nellemann/hmci/ManagementConsole.java @@ -15,21 +15,25 @@ */ package biz.nellemann.hmci; +import java.io.IOException; +import static java.lang.Thread.sleep; +import java.time.Duration; +import java.time.Instant; +import java.time.temporal.ChronoUnit; +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; +import java.util.concurrent.atomic.AtomicBoolean; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.fasterxml.jackson.dataformat.xml.XmlMapper; + import biz.nellemann.hmci.dto.toml.HmcConfiguration; import biz.nellemann.hmci.dto.xml.Link; import biz.nellemann.hmci.dto.xml.ManagementConsoleEntry; import biz.nellemann.hmci.dto.xml.XmlFeed; -import com.fasterxml.jackson.dataformat.xml.XmlMapper; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.time.Duration; -import java.time.Instant; -import java.time.temporal.ChronoUnit; -import java.util.*; -import java.util.concurrent.atomic.AtomicBoolean; - -import static java.lang.Thread.sleep; class ManagementConsole implements Runnable { @@ -170,7 +174,7 @@ class ManagementConsole implements Runnable { } } - } catch (Exception e) { + } catch (IOException e) { log.warn("discover() - error: {}", e.getMessage()); } diff --git a/src/main/java/biz/nellemann/hmci/RestClient.java b/src/main/java/biz/nellemann/hmci/RestClient.java index 3a3567b..cc4ff85 100644 --- a/src/main/java/biz/nellemann/hmci/RestClient.java +++ b/src/main/java/biz/nellemann/hmci/RestClient.java @@ -1,23 +1,33 @@ package biz.nellemann.hmci; -import biz.nellemann.hmci.dto.xml.LogonResponse; -import com.fasterxml.jackson.dataformat.xml.XmlMapper; -import okhttp3.*; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; +import java.io.IOException; +import java.net.MalformedURLException; +import java.net.URL; +import java.security.KeyManagementException; +import java.security.NoSuchAlgorithmException; +import java.security.SecureRandom; +import java.security.cert.X509Certificate; +import java.time.Instant; +import java.time.temporal.ChronoUnit; +import java.util.Objects; +import java.util.concurrent.TimeUnit; import javax.net.ssl.SSLContext; import javax.net.ssl.SSLSocketFactory; import javax.net.ssl.TrustManager; import javax.net.ssl.X509TrustManager; -import java.io.*; -import java.net.*; -import java.security.KeyManagementException; -import java.security.NoSuchAlgorithmException; -import java.security.SecureRandom; -import java.security.cert.X509Certificate; -import java.util.Objects; -import java.util.concurrent.TimeUnit; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.fasterxml.jackson.dataformat.xml.XmlMapper; + +import biz.nellemann.hmci.dto.xml.LogonResponse; +import okhttp3.MediaType; +import okhttp3.OkHttpClient; +import okhttp3.Request; +import okhttp3.RequestBody; +import okhttp3.Response; public class RestClient { @@ -38,6 +48,9 @@ public class RestClient { protected final String username; protected final String password; + private final static int MAX_MINUTES_BETWEEN_AUTHENTICATION = 60; // TODO: Make configurable and match HMC timeout settings + private Instant lastAuthenticationTimestamp; + public RestClient(String baseUrl, String username, String password, Boolean trustAll) { this.baseUrl = baseUrl; @@ -63,6 +76,8 @@ public class RestClient { log.error("ManagementConsole() - trace error: " + e.getMessage()); } }*/ + Thread shutdownHook = new Thread(this::logoff); + Runtime.getRuntime().addShutdownHook(shutdownHook); } @@ -70,6 +85,9 @@ public class RestClient { * Logon to the HMC and get an authentication token for further requests. */ public synchronized void login() { + if(authToken != null) { + logoff(); + } log.info("Connecting to HMC - {} @ {}", username, baseUrl); StringBuilder payload = new StringBuilder(); @@ -102,10 +120,12 @@ public class RestClient { LogonResponse logonResponse = xmlMapper.readValue(responseBody, LogonResponse.class); authToken = logonResponse.getToken(); + lastAuthenticationTimestamp = Instant.now(); log.debug("logon() - auth token: {}", authToken); } catch (Exception e) { log.warn("logon() - error: {}", e.getMessage()); + lastAuthenticationTimestamp = null; } } @@ -136,6 +156,7 @@ public class RestClient { log.warn("logoff() error: {}", e.getMessage()); } finally { authToken = null; + lastAuthenticationTimestamp = null; } } catch (MalformedURLException e) { @@ -160,10 +181,14 @@ public class RestClient { * Return a Response from the HMC * @param url to get Response from * @return Response body string + * @throws IOException */ public synchronized String getRequest(URL url) throws IOException { log.debug("getRequest() - URL: {}", url.toString()); + if (lastAuthenticationTimestamp == null || lastAuthenticationTimestamp.plus(MAX_MINUTES_BETWEEN_AUTHENTICATION, ChronoUnit.MINUTES).isBefore(Instant.now())) { + login(); + } Request request = new Request.Builder() .url(url) @@ -218,10 +243,18 @@ public class RestClient { /** * Send a POST request with a payload (can be null) to the HMC + * @param url + * @param payload + * @return Response body string + * @throws IOException */ public synchronized String postRequest(URL url, String payload) throws IOException { log.debug("sendPostRequest() - URL: {}", url.toString()); + if (lastAuthenticationTimestamp == null || lastAuthenticationTimestamp.plus(MAX_MINUTES_BETWEEN_AUTHENTICATION, ChronoUnit.MINUTES).isBefore(Instant.now())) { + login(); + } + RequestBody requestBody; if(payload != null) { requestBody = RequestBody.create(payload, MEDIA_TYPE_IBM_XML_POST); diff --git a/src/main/java/biz/nellemann/hmci/SystemEnergy.java b/src/main/java/biz/nellemann/hmci/SystemEnergy.java index f97f26a..fb28eeb 100644 --- a/src/main/java/biz/nellemann/hmci/SystemEnergy.java +++ b/src/main/java/biz/nellemann/hmci/SystemEnergy.java @@ -1,14 +1,20 @@ package biz.nellemann.hmci; -import biz.nellemann.hmci.dto.xml.Link; -import biz.nellemann.hmci.dto.xml.XmlFeed; -import com.fasterxml.jackson.dataformat.xml.XmlMapper; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - import java.io.IOException; import java.net.URI; -import java.util.*; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.fasterxml.jackson.dataformat.xml.XmlMapper; + +import biz.nellemann.hmci.dto.xml.Link; +import biz.nellemann.hmci.dto.xml.XmlFeed; class SystemEnergy extends Resource { diff --git a/src/main/java/biz/nellemann/hmci/VersionProvider.java b/src/main/java/biz/nellemann/hmci/VersionProvider.java index 0651de2..ec3a974 100644 --- a/src/main/java/biz/nellemann/hmci/VersionProvider.java +++ b/src/main/java/biz/nellemann/hmci/VersionProvider.java @@ -15,12 +15,12 @@ */ package biz.nellemann.hmci; -import picocli.CommandLine; - import java.io.IOException; import java.util.jar.Attributes; import java.util.jar.Manifest; +import picocli.CommandLine; + class VersionProvider implements CommandLine.IVersionProvider { @Override diff --git a/src/main/java/biz/nellemann/hmci/VirtualIOServer.java b/src/main/java/biz/nellemann/hmci/VirtualIOServer.java index 62a94ee..f7d0e86 100644 --- a/src/main/java/biz/nellemann/hmci/VirtualIOServer.java +++ b/src/main/java/biz/nellemann/hmci/VirtualIOServer.java @@ -1,13 +1,16 @@ package biz.nellemann.hmci; -import biz.nellemann.hmci.dto.xml.VirtualIOServerEntry; -import biz.nellemann.hmci.dto.xml.XmlEntry; -import com.fasterxml.jackson.dataformat.xml.XmlMapper; +import java.io.IOException; +import java.net.URI; +import java.net.URISyntaxException; + import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import java.net.URI; -import java.net.URISyntaxException; +import com.fasterxml.jackson.dataformat.xml.XmlMapper; + +import biz.nellemann.hmci.dto.xml.VirtualIOServerEntry; +import biz.nellemann.hmci.dto.xml.XmlEntry; public class VirtualIOServer { private final static Logger log = LoggerFactory.getLogger(VirtualIOServer.class); @@ -58,7 +61,7 @@ public class VirtualIOServer { throw new UnsupportedOperationException("Failed to deserialize VirtualIOServer"); } - } catch (Exception e) { + } catch (IOException e) { log.error("discover() - error: {}", e.getMessage()); } } diff --git a/src/main/java/biz/nellemann/hmci/dto/json/PowerUtil.java b/src/main/java/biz/nellemann/hmci/dto/json/PowerUtil.java index ab028eb..f77f9aa 100644 --- a/src/main/java/biz/nellemann/hmci/dto/json/PowerUtil.java +++ b/src/main/java/biz/nellemann/hmci/dto/json/PowerUtil.java @@ -5,6 +5,6 @@ import com.fasterxml.jackson.annotation.JsonIgnoreProperties; @JsonIgnoreProperties(ignoreUnknown = true) public final class PowerUtil { - public Number powerReading = 0.0; + public float powerReading = 0.0F; } diff --git a/src/test/groovy/biz/nellemann/hmci/LogicalPartitionTest.groovy b/src/test/groovy/biz/nellemann/hmci/LogicalPartitionTest.groovy index 6367cfc..92c4866 100644 --- a/src/test/groovy/biz/nellemann/hmci/LogicalPartitionTest.groovy +++ b/src/test/groovy/biz/nellemann/hmci/LogicalPartitionTest.groovy @@ -52,6 +52,7 @@ class LogicalPartitionTest extends Specification { } def cleanupSpec() { + serviceClient.logoff() mockServer.stop() } diff --git a/src/test/groovy/biz/nellemann/hmci/ManagedSystemTest.groovy b/src/test/groovy/biz/nellemann/hmci/ManagedSystemTest.groovy index 88f1cdc..3cf0ca6 100644 --- a/src/test/groovy/biz/nellemann/hmci/ManagedSystemTest.groovy +++ b/src/test/groovy/biz/nellemann/hmci/ManagedSystemTest.groovy @@ -42,6 +42,7 @@ class ManagedSystemTest extends Specification { } def cleanupSpec() { + serviceClient.logoff() mockServer.stop() }