More work on robustness after refactoring

This commit is contained in:
Mark Nellemann 2020-10-14 10:41:31 +02:00
parent 806e6e9631
commit 95964dabcc
7 changed files with 75 additions and 91 deletions

View file

@ -19,11 +19,11 @@ dependencies {
annotationProcessor 'info.picocli:picocli-codegen:4.5.1' annotationProcessor 'info.picocli:picocli-codegen:4.5.1'
implementation 'info.picocli:picocli:4.5.1' implementation 'info.picocli:picocli:4.5.1'
implementation 'org.jsoup:jsoup:1.13.1' implementation 'org.jsoup:jsoup:1.13.1'
implementation 'com.squareup.okhttp3:okhttp:4.8.0' implementation 'com.squareup.okhttp3:okhttp:4.9.0'
implementation 'com.squareup.moshi:moshi:1.11.0' implementation 'com.squareup.moshi:moshi:1.11.0'
implementation 'com.serjltt.moshi:moshi-lazy-adapters:2.2' implementation 'com.serjltt.moshi:moshi-lazy-adapters:2.2'
implementation 'org.tomlj:tomlj:1.0.0' implementation 'org.tomlj:tomlj:1.0.0'
implementation 'org.influxdb:influxdb-java:2.19' implementation 'org.influxdb:influxdb-java:2.20'
implementation 'org.slf4j:slf4j-api:1.7.+' implementation 'org.slf4j:slf4j-api:1.7.+'
runtimeOnly 'ch.qos.logback:logback-classic:1.+' runtimeOnly 'ch.qos.logback:logback-classic:1.+'
@ -98,7 +98,7 @@ jacocoTestCoverageVerification {
violationRules { violationRules {
rule { rule {
limit { limit {
minimum = 0.3 // FIXME: Raise when more tests are implemented minimum = 0.4 // TODO: Raise when more tests are implemented
} }
} }
} }

View file

@ -1,3 +1,3 @@
id = hmci id = hmci
group = biz.nellemann.hmci group = biz.nellemann.hmci
version = 0.2.1 version = 0.2.2

View file

@ -67,26 +67,18 @@ class HmcClient {
} }
@Override
/** public String toString() {
* Logon to the HMC and get an authentication token for further requests. return hmcId + " (" + baseUrl + ")";
*/
void login() throws Exception {
this.login(false);
} }
/** /**
* Logon to the HMC and get an authentication token for further requests. * Logon to the HMC and get an authentication token for further requests.
* @param force
*/ */
void login(Boolean force) throws Exception { synchronized void login() throws Exception {
if(authToken != null && !force) { log.debug("Connecting to HMC - " + baseUrl);
return;
}
log.info("Connecting to HMC - " + baseUrl);
StringBuilder payload = new StringBuilder(); StringBuilder payload = new StringBuilder();
payload.append("<?xml version='1.0' encoding='UTF-8' standalone='yes'?>"); payload.append("<?xml version='1.0' encoding='UTF-8' standalone='yes'?>");
@ -99,29 +91,24 @@ class HmcClient {
URL url = new URL(String.format("%s/rest/api/web/Logon", baseUrl)); URL url = new URL(String.format("%s/rest/api/web/Logon", baseUrl));
Request request = new Request.Builder() Request request = new Request.Builder()
.url(url) .url(url)
//.addHeader("Content-Type", "application/vnd.ibm.powervm.web+xml; type=LogonRequest")
.addHeader("Accept", "application/vnd.ibm.powervm.web+xml; type=LogonResponse") .addHeader("Accept", "application/vnd.ibm.powervm.web+xml; type=LogonResponse")
.addHeader("X-Audit-Memento", "hmci") .addHeader("X-Audit-Memento", "hmci")
.put(RequestBody.create(payload.toString(), MEDIA_TYPE_IBM_XML_LOGIN)) .put(RequestBody.create(payload.toString(), MEDIA_TYPE_IBM_XML_LOGIN))
.build(); .build();
Response response = client.newCall(request).execute(); Response response = client.newCall(request).execute();
if (!response.isSuccessful()) throw new IOException("Unexpected code " + response);
// Get response body and parse
String responseBody = Objects.requireNonNull(response.body()).string(); String responseBody = Objects.requireNonNull(response.body()).string();
Objects.requireNonNull(response.body()).close(); if (!response.isSuccessful()) {
log.warn("login() - Unexpected response: " + response.code());
throw new IOException("Unexpected code: " + response);
}
Document doc = Jsoup.parse(responseBody); Document doc = Jsoup.parse(responseBody);
authToken = doc.select("X-API-Session").text(); authToken = doc.select("X-API-Session").text();
log.debug("login() - Auth Token: " + authToken); log.debug("login() - Auth Token: " + authToken);
} catch (MalformedURLException e) { } catch (MalformedURLException e) {
log.error("login() - url error", e); log.error("login() - URL Error: " + e.getMessage());
throw new Exception(new Throwable("Login URL Error: " + e.getMessage())); throw e;
} catch(Exception e) {
log.error("login() - general error", e);
throw new Exception(new Throwable("Login General Error: " + e.getMessage()));
} }
} }
@ -132,7 +119,7 @@ class HmcClient {
* Logoff from the HMC and remove any session * Logoff from the HMC and remove any session
* *
*/ */
void logoff() throws IOException { synchronized void logoff() throws IOException {
if(authToken == null) { if(authToken == null) {
return; return;
@ -145,12 +132,14 @@ class HmcClient {
.addHeader("X-API-Session", authToken) .addHeader("X-API-Session", authToken)
.delete() .delete()
.build(); .build();
try {
Response response = client.newCall(request).execute(); client.newCall(request).execute();
if (!response.isSuccessful()) throw new IOException("Unexpected code " + response); } catch (IOException e) {
log.warn("logoff() error: " + e.getMessage());
} finally {
authToken = null; authToken = null;
log.debug("logoff()"); }
} }
@ -163,8 +152,7 @@ class HmcClient {
Map<String, ManagedSystem> getManagedSystems() throws Exception { Map<String, ManagedSystem> getManagedSystems() throws Exception {
URL url = new URL(String.format("%s/rest/api/uom/ManagedSystem", baseUrl)); URL url = new URL(String.format("%s/rest/api/uom/ManagedSystem", baseUrl));
Response response = getResponse(url); String responseBody = getResponse(url);
String responseBody = Objects.requireNonNull(response.body()).string();
Map<String,ManagedSystem> managedSystemsMap = new HashMap<>(); Map<String,ManagedSystem> managedSystemsMap = new HashMap<>();
// Do not try to parse empty response // Do not try to parse empty response
@ -186,7 +174,7 @@ class HmcClient {
el.select("MachineTypeModelAndSerialNumber > SerialNumber").text() el.select("MachineTypeModelAndSerialNumber > SerialNumber").text()
); );
managedSystemsMap.put(system.id, system); managedSystemsMap.put(system.id, system);
log.debug("getManagedSystems() - Found system: " + system.toString()); log.debug("getManagedSystems() - Found system: " + system);
} }
} catch(Exception e) { } catch(Exception e) {
@ -205,8 +193,7 @@ class HmcClient {
*/ */
Map<String, LogicalPartition> getLogicalPartitionsForManagedSystem(ManagedSystem system) throws Exception { Map<String, LogicalPartition> getLogicalPartitionsForManagedSystem(ManagedSystem system) throws Exception {
URL url = new URL(String.format("%s/rest/api/uom/ManagedSystem/%s/LogicalPartition", baseUrl, system.id)); URL url = new URL(String.format("%s/rest/api/uom/ManagedSystem/%s/LogicalPartition", baseUrl, system.id));
Response response = getResponse(url); String responseBody = getResponse(url);
String responseBody = Objects.requireNonNull(response.body()).string();
Map<String, LogicalPartition> partitionMap = new HashMap<String, LogicalPartition>() {}; Map<String, LogicalPartition> partitionMap = new HashMap<String, LogicalPartition>() {};
// Do not try to parse empty response // Do not try to parse empty response
@ -226,7 +213,7 @@ class HmcClient {
system system
); );
partitionMap.put(logicalPartition.id, logicalPartition); partitionMap.put(logicalPartition.id, logicalPartition);
log.debug("getLogicalPartitionsForManagedSystem() - Found partition: " + logicalPartition.toString()); log.debug("getLogicalPartitionsForManagedSystem() - Found partition: " + logicalPartition);
} }
} catch(Exception e) { } catch(Exception e) {
@ -247,8 +234,7 @@ class HmcClient {
log.debug("getPcmDataForManagedSystem() - " + system.id); log.debug("getPcmDataForManagedSystem() - " + system.id);
URL url = new URL(String.format("%s/rest/api/pcm/ManagedSystem/%s/ProcessedMetrics?NoOfSamples=1", baseUrl, system.id)); URL url = new URL(String.format("%s/rest/api/pcm/ManagedSystem/%s/ProcessedMetrics?NoOfSamples=1", baseUrl, system.id));
Response response = getResponse(url); String responseBody = getResponse(url);
String responseBody = Objects.requireNonNull(response.body()).string();
String jsonBody = null; String jsonBody = null;
// Do not try to parse empty response // Do not try to parse empty response
@ -266,7 +252,7 @@ class HmcClient {
if(link.attr("type").equals("application/json")) { if(link.attr("type").equals("application/json")) {
String href = link.attr("href"); String href = link.attr("href");
log.debug("getPcmDataForManagedSystem() - json url: " + href); log.debug("getPcmDataForManagedSystem() - json url: " + href);
jsonBody = getResponseBody(new URL(href)); jsonBody = getResponse(new URL(href));
} }
} catch(Exception e) { } catch(Exception e) {
@ -286,8 +272,7 @@ class HmcClient {
log.debug(String.format("getPcmDataForLogicalPartition() - %s @ %s", partition.id, partition.system.id)); log.debug(String.format("getPcmDataForLogicalPartition() - %s @ %s", partition.id, partition.system.id));
URL url = new URL(String.format("%s/rest/api/pcm/ManagedSystem/%s/LogicalPartition/%s/ProcessedMetrics?NoOfSamples=1", baseUrl, partition.system.id, partition.id)); URL url = new URL(String.format("%s/rest/api/pcm/ManagedSystem/%s/LogicalPartition/%s/ProcessedMetrics?NoOfSamples=1", baseUrl, partition.system.id, partition.id));
Response response = getResponse(url); String responseBody = getResponse(url);
String responseBody = Objects.requireNonNull(response.body()).string();
String jsonBody = null; String jsonBody = null;
// Do not try to parse empty response // Do not try to parse empty response
@ -305,7 +290,7 @@ class HmcClient {
if(link.attr("type").equals("application/json")) { if(link.attr("type").equals("application/json")) {
String href = link.attr("href"); String href = link.attr("href");
log.debug("getPcmDataForLogicalPartition() - json url: " + href); log.debug("getPcmDataForLogicalPartition() - json url: " + href);
jsonBody = getResponseBody(new URL(href)); jsonBody = getResponse(new URL(href));
} }
} catch(Exception e) { } catch(Exception e) {
@ -316,26 +301,13 @@ class HmcClient {
} }
/**
* Return body text from a HTTP response from the HMC
*
* @param url URL to get response body as String
* @return String with http reponse body
*/
protected String getResponseBody(URL url) throws Exception {
Response response = getResponse(url);
String body = Objects.requireNonNull(response.body()).string();
Objects.requireNonNull(response.body()).close();
return body;
}
/** /**
* Return a Response from the HMC * Return a Response from the HMC
* @param url to get Response from * @param url to get Response from
* @return Response object * @return Response body string
*/ */
private Response getResponse(URL url) throws Exception { private String getResponse(URL url) throws Exception {
return getResponse(url, 0); return getResponse(url, 0);
} }
@ -344,15 +316,16 @@ class HmcClient {
* Return a Response from the HMC * Return a Response from the HMC
* @param url to get Response from * @param url to get Response from
* @param retry number of retries for this call * @param retry number of retries for this call
* @return Response object * @return Response body string
*/ */
private Response getResponse(URL url, Integer retry) throws Exception { private String getResponse(URL url, Integer retry) throws Exception {
log.debug("getResponse() - " + url.toString()); log.debug("getResponse() - " + url.toString());
if(responseErrors > 2) { if(responseErrors > 2) {
log.warn("getResponse() - retries > 2");
responseErrors = 0; responseErrors = 0;
login(true); login();
return getResponse(url, retry++); return getResponse(url, retry++);
} }
@ -360,15 +333,19 @@ class HmcClient {
.url(url) .url(url)
.addHeader("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8") .addHeader("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8")
.addHeader("X-API-Session", authToken) .addHeader("X-API-Session", authToken)
.get() .get().build();
.build();
Response response = client.newCall(request).execute(); Response response = client.newCall(request).execute();
String body = Objects.requireNonNull(response.body()).string();
if (!response.isSuccessful()) { if (!response.isSuccessful()) {
Objects.requireNonNull(response.body()).close();
response.close();
if(response.code() == 401) { if(response.code() == 401) {
login(true); log.warn("getResponse() - 401 - login and retry.");
authToken = null;
login();
return getResponse(url, retry++); return getResponse(url, retry++);
} }
@ -381,7 +358,7 @@ class HmcClient {
throw new IOException("getResponse() - Unexpected response: " + response.code()); throw new IOException("getResponse() - Unexpected response: " + response.code());
} }
return response; return body;
} }
@ -389,7 +366,7 @@ class HmcClient {
/** /**
* Provide an unsafe (ignoring SSL problems) OkHttpClient * Provide an unsafe (ignoring SSL problems) OkHttpClient
* *
* @return * @return unsafe OkHttpClient
*/ */
private static OkHttpClient getUnsafeOkHttpClient() { private static OkHttpClient getUnsafeOkHttpClient() {
try { try {

View file

@ -51,32 +51,31 @@ class InfluxClient {
} }
void login() throws Exception { synchronized void login() throws Exception {
if(influxDB != null) { if(influxDB != null) {
return; return;
} }
try { try {
log.info("Connecting to InfluxDB - " + url); log.debug("Connecting to InfluxDB - " + url);
influxDB = InfluxDBFactory.connect(url, username, password); influxDB = InfluxDBFactory.connect(url, username, password);
createDatabase(); createDatabase();
// Enable batch writes to get better performance. // Enable batch writes to get better performance.
//BatchOptions options = BatchOptions.DEFAULTS.actions(300).flushDuration(500); //influxDB.enableBatch(BatchOptions.DEFAULTS);
influxDB.enableBatch(BatchOptions.DEFAULTS); BatchOptions options = BatchOptions.DEFAULTS.actions(100).flushDuration(500);
//influxDB.setLogLevel(InfluxDB.LogLevel.BASIC); influxDB.enableBatch(options);
batchPoints = BatchPoints.database(database).precision(TimeUnit.SECONDS).build(); batchPoints = BatchPoints.database(database).precision(TimeUnit.SECONDS).build();
} catch(Exception e) { } catch(Exception e) {
log.error(e.getMessage()); log.error("login() error - " + e.getMessage());
throw new Exception(e); throw new Exception(e);
} }
} }
void logoff() { synchronized void logoff() {
if(influxDB != null) { if(influxDB != null) {
influxDB.close(); influxDB.close();
} }
@ -91,7 +90,7 @@ class InfluxClient {
} }
void writeBatchPoints() throws Exception { synchronized void writeBatchPoints() throws Exception {
log.debug("writeBatchPoints()"); log.debug("writeBatchPoints()");
try { try {
influxDB.write(batchPoints); influxDB.write(batchPoints);
@ -122,8 +121,6 @@ class InfluxClient {
return; return;
} }
//BatchPoints batchPoints = BatchPoints.database(database).build();
getSystemMemory(system, timestamp).forEach( it -> { getSystemMemory(system, timestamp).forEach( it -> {
batchPoints.point(it); batchPoints.point(it);
}); });
@ -207,8 +204,6 @@ class InfluxClient {
return; return;
} }
//BatchPoints batchPoints = BatchPoints.database(database).build();
getPartitionAffinityScore(partition, timestamp).forEach( it -> { getPartitionAffinityScore(partition, timestamp).forEach( it -> {
batchPoints.point(it); batchPoints.point(it);
}); });

View file

@ -54,27 +54,36 @@ class Insights {
void discover() { void discover() {
configuration.hmc.forEach( configHmc -> { configuration.hmc.forEach( configHmc -> {
if(hmcClients != null && !hmcClients.containsKey(configHmc.name)) { if(!hmcClients.containsKey(configHmc.name)) {
HmcClient hmcClient = new HmcClient(configHmc); HmcClient hmcClient = new HmcClient(configHmc);
hmcClients.put(configHmc.name, hmcClient); hmcClients.put(configHmc.name, hmcClient);
log.info("discover() - Adding HMC: " + hmcClient);
} }
}); });
hmcClients.forEach(( hmcId, hmcClient) -> { hmcClients.forEach(( hmcId, hmcClient) -> {
try { try {
hmcClient.logoff();;
hmcClient.login(); hmcClient.login();
hmcClient.getManagedSystems().forEach((systemId, system) -> { hmcClient.getManagedSystems().forEach((systemId, system) -> {
// Add to list of known systems // Add to list of known systems
systems.putIfAbsent(systemId, system); if(!systems.containsKey(systemId)) {
systems.put(systemId, system);
log.info("discover() - Found ManagedSystem: " + system);
}
// Get LPAR's for this system // Get LPAR's for this system
try { try {
hmcClient.getLogicalPartitionsForManagedSystem(system).forEach((partitionId, partition) -> { hmcClient.getLogicalPartitionsForManagedSystem(system).forEach((partitionId, partition) -> {
// Add to list of known partitions // Add to list of known partitions
partitions.putIfAbsent(partitionId, partition); if(!partitions.containsKey(partitionId)) {
partitions.put(partitionId, partition);
log.info("discover() - Found LogicalPartition: " + partition);
}
}); });
} catch (Exception e) { } catch (Exception e) {
log.error("discover()", e); log.error("discover()", e);
@ -162,7 +171,10 @@ class Insights {
int executions = 0; int executions = 0;
AtomicBoolean keepRunning = new AtomicBoolean(true); AtomicBoolean keepRunning = new AtomicBoolean(true);
Thread shutdownHook = new Thread(() -> keepRunning.set(false)); Thread shutdownHook = new Thread(() -> {
keepRunning.set(false);
System.out.println("Stopping HMCi, please wait ...");
});
Runtime.getRuntime().addShutdownHook(shutdownHook); Runtime.getRuntime().addShutdownHook(shutdownHook);
do { do {

View file

@ -18,6 +18,7 @@ package biz.nellemann.hmci;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import picocli.CommandLine; import picocli.CommandLine;
import picocli.CommandLine.Option;
import picocli.CommandLine.Command; import picocli.CommandLine.Command;
import java.io.File; import java.io.File;
@ -32,8 +33,7 @@ public class Main implements Callable<Integer> {
private final static Logger log = LoggerFactory.getLogger(Main.class); private final static Logger log = LoggerFactory.getLogger(Main.class);
@SuppressWarnings("FieldMayBeFinal") @Option(names = { "-c", "--conf" }, description = "Configuration file [default: '/etc/hmci.toml'].")
@CommandLine.Option(names = { "-c", "--conf" }, description = "Configuration file [default: '/etc/hmci.toml'].")
private String configurationFile = "/etc/hmci.toml"; private String configurationFile = "/etc/hmci.toml";
public static void main(String... args) { public static void main(String... args) {

View file

@ -78,7 +78,7 @@ class HmcClientTest extends Specification {
mockServer.enqueue(new MockResponse().setBody(testJson)) mockServer.enqueue(new MockResponse().setBody(testJson))
when: when:
String jsonString = hmc.getResponseBody(new URL(mockServer.url("/rest/api/pcm/ProcessedMetrics/ManagedSystem_e09834d1-c930-3883-bdad-405d8e26e166_20200807T122600+0200_20200807T122600+0200_30.json") as String)) String jsonString = hmc.getResponse(new URL(mockServer.url("/rest/api/pcm/ProcessedMetrics/ManagedSystem_e09834d1-c930-3883-bdad-405d8e26e166_20200807T122600+0200_20200807T122600+0200_30.json") as String))
then: then:
jsonString.contains('"uuid": "e09834d1-c930-3883-bdad-405d8e26e166"') jsonString.contains('"uuid": "e09834d1-c930-3883-bdad-405d8e26e166"')
@ -92,7 +92,7 @@ class HmcClientTest extends Specification {
mockServer.enqueue(new MockResponse().setBody(testJson)) mockServer.enqueue(new MockResponse().setBody(testJson))
when: when:
String jsonString = hmc.getResponseBody(new URL(mockServer.url("/rest/api/pcm/ProcessedMetrics/LogicalPartition_2DE05DB6-8AD5-448F-8327-0F488D287E82_20200807T123730+0200_20200807T123730+0200_30.json") as String)) String jsonString = hmc.getResponse(new URL(mockServer.url("/rest/api/pcm/ProcessedMetrics/LogicalPartition_2DE05DB6-8AD5-448F-8327-0F488D287E82_20200807T123730+0200_20200807T123730+0200_30.json") as String))
then: then:
jsonString.contains('"uuid": "b597e4da-2aab-3f52-8616-341d62153559"') jsonString.contains('"uuid": "b597e4da-2aab-3f52-8616-341d62153559"')