From bcd2b84e9f2fd0c058f0da0e91b4329c2361a513 Mon Sep 17 00:00:00 2001 From: Mark Nellemann Date: Sat, 26 Nov 2022 10:47:10 +0100 Subject: [PATCH] Work on reading and updating PCM preferences to enable energymonitoring. --- CHANGELOG.md | 11 +-- doc/hmci.toml | 7 +- .../java/biz/nellemann/hmci/Application.java | 10 +-- .../biz/nellemann/hmci/ManagedSystem.java | 77 ++++++++++--------- .../biz/nellemann/hmci/ManagementConsole.java | 21 ++--- .../java/biz/nellemann/hmci/RestClient.java | 40 ++++++---- .../dto/xml/ManagedSystemPcmPreference.java | 45 +++++++++++ .../biz/nellemann/hmci/dto/xml/Metadata.java | 21 +++++ .../biz/nellemann/hmci/dto/xml/XmlEntry.java | 13 ++++ .../2-managed-system-pcm-preferences.xml | 43 +++++++++++ src/test/resources/hmci.toml | 10 +-- 11 files changed, 206 insertions(+), 92 deletions(-) create mode 100644 src/main/java/biz/nellemann/hmci/dto/xml/ManagedSystemPcmPreference.java create mode 100644 src/main/java/biz/nellemann/hmci/dto/xml/Metadata.java create mode 100644 src/test/resources/2-managed-system-pcm-preferences.xml diff --git a/CHANGELOG.md b/CHANGELOG.md index 2fd3acb..2feeb7c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,11 +2,9 @@ All notable changes to this project will be documented in this file. -## [1.3.5] - 2022-11-08 - -## [1.3.4] - 2022-10-24 -### Changed -- Updated 3rd party dependencies +## [1.4.0] - 2011-12-xx +- Major rewrite of toml+xml+json de-serialization +- Changes to configuration file format - please look at [doc/hmci.toml](doc/hmci.toml) as example. ## [1.3.3] - 2022-09-20 ### Added @@ -29,8 +27,7 @@ All notable changes to this project will be documented in this file. ### Added - Options to include/exclude Managed Systems and/or Logical Partitions. -[1.3.5]: https://bitbucket.org/mnellemann/hmci/branches/compare/v1.3.5%0Dv1.3.4 -[1.3.4]: https://bitbucket.org/mnellemann/hmci/branches/compare/v1.3.4%0Dv1.3.3 +[1.4.0]: https://bitbucket.org/mnellemann/hmci/branches/compare/v1.4.0%0Dv1.3.3 [1.3.3]: https://bitbucket.org/mnellemann/hmci/branches/compare/v1.3.3%0Dv1.3.0 [1.3.0]: https://bitbucket.org/mnellemann/hmci/branches/compare/v1.3.0%0Dv1.2.8 [1.2.8]: https://bitbucket.org/mnellemann/hmci/branches/compare/v1.2.8%0Dv1.2.7 diff --git a/doc/hmci.toml b/doc/hmci.toml index cc947e9..0da7f8f 100644 --- a/doc/hmci.toml +++ b/doc/hmci.toml @@ -13,6 +13,7 @@ password = "" database = "hmci" + ### ### Define one or more HMC's to query for metrics ### Each entry must be named [hmc.] @@ -21,7 +22,7 @@ database = "hmci" # HMC to query for data and metrics [hmc.site1] -url = "https://10.10.10.10:12443" +url = "https://10.10.10.5:12443" username = "hmci" password = "hmcihmci" refresh = 30 # How often to query HMC for data - in seconds @@ -32,10 +33,10 @@ energy = true # Collect energy metrics on supported systems # Another HMC example #[hmc.site2] -#url = "https://10.10.10.30:12443" +#url = "https://10.10.20.5:12443" #username = "user" #password = "password" -#trace = "/tmp/hmci-trace" # When present, store JSON metrics files from HMC into this folder +#trace = "/tmp/hmci-trace" # When present, store JSON metrics files from HMC into this folder #excludeSystems = [ 'notThisSystem' ] # Collect metrics from all systems except those listed here #includeSystems = [ 'onlyThisSystems' ] # Collcet metrics from no systems but those listed here #excludePartitions = [ 'skipThisPartition' ] # Collect metrics from all partitions except those listed here diff --git a/src/main/java/biz/nellemann/hmci/Application.java b/src/main/java/biz/nellemann/hmci/Application.java index 659e650..55b36f4 100644 --- a/src/main/java/biz/nellemann/hmci/Application.java +++ b/src/main/java/biz/nellemann/hmci/Application.java @@ -32,7 +32,7 @@ import java.util.concurrent.Callable; defaultValueProvider = biz.nellemann.hmci.DefaultProvider.class) public class Application implements Callable { - @Option(names = { "-c", "--conf" }, description = "Configuration file [default: ${DEFAULT-VALUE}].", paramLabel = "") + @Option(names = { "-c", "--conf" }, description = "Configuration file [default: ${DEFAULT-VALUE}].", paramLabel = "", defaultValue = "/etc/hmci.toml") private File configurationFile; @Option(names = { "-d", "--debug" }, description = "Enable debugging [default: false].") @@ -85,14 +85,6 @@ public class Application implements Callable { } }); - /* - for(Configuration.HmcConfiguration configHmc : configuration.getHmc()) { - Thread t = new Thread(new ManagementConsole(configHmc, influxClient)); - t.setName(configHmc.name); - t.start(); - threadList.add(t); - }*/ - for (Thread thread : threadList) { thread.join(); } diff --git a/src/main/java/biz/nellemann/hmci/ManagedSystem.java b/src/main/java/biz/nellemann/hmci/ManagedSystem.java index 34dfae4..6bd278c 100644 --- a/src/main/java/biz/nellemann/hmci/ManagedSystem.java +++ b/src/main/java/biz/nellemann/hmci/ManagedSystem.java @@ -15,10 +15,9 @@ */ package biz.nellemann.hmci; -import biz.nellemann.hmci.dto.json.SystemUtil; -import biz.nellemann.hmci.dto.json.Temperature; import biz.nellemann.hmci.dto.xml.*; import com.fasterxml.jackson.core.JsonParseException; +import com.fasterxml.jackson.databind.SerializationFeature; import com.fasterxml.jackson.dataformat.xml.XmlMapper; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -27,8 +26,6 @@ import java.io.IOException; import java.net.URI; import java.net.URISyntaxException; import java.util.*; -import java.util.concurrent.ExecutionException; -import java.util.concurrent.TimeoutException; class ManagedSystem extends Resource { @@ -46,6 +43,7 @@ class ManagedSystem extends Resource { protected ManagedSystemEntry entry; + protected ManagedSystemPcmPreference pcmPreference; protected SystemEnergy systemEnergy; private String uriPath; @@ -81,9 +79,20 @@ class ManagedSystem extends Resource { } public void setDoEnergy(Boolean doEnergy) { - this.doEnergy = doEnergy; - // TODO: Enable energy command. - systemEnergy = new SystemEnergy(restClient, this); + + if(pcmPreference == null) { + return; + } + + if(pcmPreference.energyMonitoringCapable && !pcmPreference.energyMonitorEnabled) { + // TODO: Try to enable + } + + if(pcmPreference.energyMonitorEnabled) { + this.doEnergy = doEnergy; + systemEnergy = new SystemEnergy(restClient, this); + } + } public void discover() { @@ -183,54 +192,46 @@ class ManagedSystem extends Resource { } + public void getPcmPreferences() { + log.info("getPcmPreferences()"); - /* - void enableEnergyMonitoring() { - - log.trace("enableEnergyMonitoring() - {}", system); try { - URL url = new URL(String.format("%s/rest/api/pcm/ManagedSystem/%s/preferences", baseUrl, system.id)); - String responseBody = sendGetRequest(url); + String urlPath = String.format("/rest/api/pcm/ManagedSystem/%s/preferences", id); + String xml = restClient.getRequest(urlPath); // Do not try to parse empty response - if(responseBody == null || responseBody.length() <= 1) { - responseErrors++; - log.warn("enableEnergyMonitoring() - empty response, skipping: {}", system); + if(xml == null || xml.length() <= 1) { + log.warn("getPcmPreferences() - no data."); return; } - Document doc = Jsoup.parse(responseBody, "", Parser.xmlParser()); - doc.outputSettings().escapeMode(Entities.EscapeMode.xhtml); - doc.outputSettings().prettyPrint(false); - doc.outputSettings().charset("US-ASCII"); - Element entry = doc.select("feed > entry").first(); - Element link1 = Objects.requireNonNull(entry).select("EnergyMonitoringCapable").first(); - Element link2 = entry.select("EnergyMonitorEnabled").first(); + XmlMapper xmlMapper = new XmlMapper(); + XmlFeed xmlFeed = xmlMapper.readValue(xml, XmlFeed.class); - if(Objects.requireNonNull(link1).text().equals("true")) { - log.debug("enableEnergyMonitoring() - EnergyMonitoringCapable == true"); - if(Objects.requireNonNull(link2).text().equals("false")) { - //log.warn("enableEnergyMonitoring() - EnergyMonitorEnabled == false"); - link2.text("true"); + if(xmlFeed.getEntry().getContent() == null){ + log.warn("getPcmPreferences() - no content."); + log.info(xml); + return; + } - Document content = Jsoup.parse(Objects.requireNonNull(doc.select("Content").first()).html(), "", Parser.xmlParser()); - content.outputSettings().escapeMode(Entities.EscapeMode.xhtml); - content.outputSettings().prettyPrint(false); - content.outputSettings().charset("UTF-8"); - String updateXml = content.outerHtml(); - - sendPostRequest(url, updateXml); + if(xmlFeed.getEntry().getContent().isManagedSystemPcmPreference()) { + pcmPreference = xmlFeed.getEntry().getContent().getManagedSystemPcmPreference(); + if(pcmPreference.energyMonitoringCapable && !pcmPreference.energyMonitorEnabled) { + log.warn("getPcmPreferences() - TODO: Enable energyMonitor"); + //pcmPreference.energyMonitorEnabled = true; + //xmlMapper.configure(SerializationFeature.WRITE_SINGLE_ELEM_ARRAYS_UNWRAPPED,true); + //String updateXml = xmlMapper.writeValueAsString(pcmPreference); + //restClient.postRequest(urlPath, updateXml); } } else { - log.warn("enableEnergyMonitoring() - EnergyMonitoringCapable == false"); + throw new UnsupportedOperationException("Failed to deserialize ManagedSystemPcmPreference"); } } catch (Exception e) { - log.debug("enableEnergyMonitoring() - Error: {}", e.getMessage()); + log.debug("getPcmPreferences() - Error: {}", e.getMessage()); } } - */ // System details diff --git a/src/main/java/biz/nellemann/hmci/ManagementConsole.java b/src/main/java/biz/nellemann/hmci/ManagementConsole.java index 660ebc1..6889c73 100644 --- a/src/main/java/biz/nellemann/hmci/ManagementConsole.java +++ b/src/main/java/biz/nellemann/hmci/ManagementConsole.java @@ -26,6 +26,7 @@ import org.slf4j.LoggerFactory; import java.io.File; import java.time.Duration; import java.time.Instant; +import java.time.temporal.ChronoUnit; import java.util.*; import java.util.concurrent.atomic.AtomicBoolean; @@ -67,10 +68,10 @@ class ManagementConsole implements Runnable { if(traceDir.canWrite()) { Boolean doTrace = true; } else { - log.warn("HmcInstance() - can't write to trace dir: " + traceDir.toString()); + log.warn("ManagementConsole() - can't write to trace dir: " + traceDir.toString()); } } catch (Exception e) { - log.error("HmcInstance() - trace error: " + e.getMessage()); + log.error("ManagementConsole() - trace error: " + e.getMessage()); } } this.excludeSystems = configuration.excludeSystems; @@ -84,8 +85,8 @@ class ManagementConsole implements Runnable { public void run() { log.trace("run()"); - int executions = 0; + Instant lastDiscover = Instant.now(); restClient.login(); discover(); @@ -93,8 +94,8 @@ class ManagementConsole implements Runnable { Instant instantStart = Instant.now(); try { refresh(); - if (++executions > discoverValue) { // FIXME: Change to time based logic - executions = 0; + if(instantStart.isAfter(lastDiscover.plus(discoverValue, ChronoUnit.MINUTES))) { + lastDiscover = instantStart; discover(); } } catch (Exception e) { @@ -126,7 +127,6 @@ class ManagementConsole implements Runnable { // Logout of HMC restClient.logoff(); - } @@ -163,12 +163,14 @@ class ManagementConsole implements Runnable { ManagedSystem managedSystem = new ManagedSystem(restClient, link.getHref()); managedSystem.setExcludePartitions(excludePartitions); managedSystem.setIncludePartitions(includePartitions); - managedSystem.setDoEnergy(doEnergy); managedSystem.discover(); // Only continue for powered-on operating systems if(managedSystem.entry != null && Objects.equals(managedSystem.entry.state, "operating")) { + managedSystem.getPcmPreferences(); + managedSystem.setDoEnergy(doEnergy); + // Check exclude / include if (!excludeSystems.contains(managedSystem.name) && includeSystems.isEmpty()) { managedSystems.add(managedSystem); @@ -193,19 +195,18 @@ class ManagementConsole implements Runnable { managedSystems.forEach( (system) -> { if(system.entry == null){ - log.warn("refresh() - system.entry == null"); + log.warn("refresh() - no data."); return; } system.refresh(); - influxClient.write(system.getDetails(), system.getTimestamp(),"server_details"); influxClient.write(system.getMemoryMetrics(), system.getTimestamp(),"server_memory"); influxClient.write(system.getProcessorMetrics(), system.getTimestamp(),"server_processor"); influxClient.write(system.getPhysicalProcessorPool(), system.getTimestamp(),"server_physicalProcessorPool"); influxClient.write(system.getSharedProcessorPools(), system.getTimestamp(),"server_sharedProcessorPool"); - if(doEnergy) { + if(system.systemEnergy != null) { system.systemEnergy.refresh(); if(system.systemEnergy.metric != null) { influxClient.write(system.systemEnergy.getPowerMetrics(), system.getTimestamp(), "server_energy_power"); diff --git a/src/main/java/biz/nellemann/hmci/RestClient.java b/src/main/java/biz/nellemann/hmci/RestClient.java index 0902c4c..3a46322 100644 --- a/src/main/java/biz/nellemann/hmci/RestClient.java +++ b/src/main/java/biz/nellemann/hmci/RestClient.java @@ -32,9 +32,7 @@ public class RestClient { private final static int WRITE_TIMEOUT = 30; private final static int READ_TIMEOUT = 180; - //protected final HttpClient httpClient; protected String authToken; - protected final String baseUrl; protected final String username; protected final String password; @@ -44,8 +42,6 @@ public class RestClient { this.baseUrl = baseUrl; this.username = username; this.password = password; - //this.httpClient = getHttpClient(trustAll); - //httpClient.start(); if (trustAll) { this.httpClient = getUnsafeOkHttpClient(); } else { @@ -151,7 +147,7 @@ public class RestClient { * @param url to get Response from * @return Response body string */ - private String getRequest(URL url) throws IOException { + public synchronized String getRequest(URL url) throws IOException { log.trace("getRequest() - URL: {}", url.toString()); @@ -172,16 +168,8 @@ public class RestClient { log.warn("getRequest() - 401 - login and retry."); // Let's login again and retry - authToken = null; login(); - - try (Response responseRetry = httpClient.newCall(request).execute()) { - log.debug("getRequest() - in retry: {}", Objects.requireNonNull(responseRetry.body()).string()); - if (responseRetry.isSuccessful()) { - return Objects.requireNonNull(responseRetry.body()).string(); - } - return null; - } + return retryGetRequest(url); } log.error("getRequest() - Unexpected response: {}", response.code()); @@ -194,6 +182,26 @@ public class RestClient { } + private String retryGetRequest(URL url) throws IOException { + + log.debug("retryGetRequest() - URL: {}", url.toString()); + + Request request = new Request.Builder() + .url(url) + .addHeader("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8") + .addHeader("X-API-Session", (authToken == null ? "" : authToken)) + .get().build(); + + String responseBody = null; + try (Response responseRetry = httpClient.newCall(request).execute()) { + if(responseRetry.isSuccessful()) { + responseBody = responseRetry.body().string(); + } + } + return responseBody; + } + + /** * Send a POST request with a payload (can be null) to the HMC * @param url @@ -201,9 +209,9 @@ public class RestClient { * @return * @throws IOException */ - public String postRequest(URL url, String payload) throws IOException { + public synchronized String postRequest(URL url, String payload) throws IOException { - log.trace("sendPostRequest() - URL: {}", url.toString()); + log.info("sendPostRequest() - URL: {}", url.toString()); RequestBody requestBody; if(payload != null) { requestBody = RequestBody.create(payload, MediaType.get("application/xml")); diff --git a/src/main/java/biz/nellemann/hmci/dto/xml/ManagedSystemPcmPreference.java b/src/main/java/biz/nellemann/hmci/dto/xml/ManagedSystemPcmPreference.java new file mode 100644 index 0000000..a561a11 --- /dev/null +++ b/src/main/java/biz/nellemann/hmci/dto/xml/ManagedSystemPcmPreference.java @@ -0,0 +1,45 @@ +package biz.nellemann.hmci.dto.xml; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty; +import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlRootElement; + +@JsonIgnoreProperties(ignoreUnknown = true) +@JacksonXmlRootElement(localName = "ManagedSystemPcmPreference:ManagedSystemPcmPreference") +public class ManagedSystemPcmPreference { + + @JacksonXmlProperty(isAttribute = true) + private final String schemaVersion = "V1_0"; + + @JacksonXmlProperty(isAttribute = true, localName = "xmlns:ManagedSystemPcmPreference") + private final String xmlns = "http://www.ibm.com/xmlns/systems/power/firmware/pcm/mc/2012_10/"; + + @JsonProperty("Metadata") + public Metadata metadata; + + @JsonProperty("SystemName") + public String systemName; + + //public MachineTypeModelAndSerialNumber machineTypeModelSerialNumber; + + @JsonProperty("EnergyMonitoringCapable") + public Boolean energyMonitoringCapable = false; + + @JsonProperty("LongTermMonitorEnabled") + public Boolean longTermMonitorEnabled = false; + + @JsonProperty("AggregationEnabled") + public Boolean aggregationEnabled = false; + + @JsonProperty("ShortTermMonitorEnabled") + public Boolean shortTermMonitorEnabled; + + @JsonProperty("ComputeLTMEnabled") + public Boolean computeLTMEnabled; + + @JsonProperty("EnergyMonitorEnabled") + public Boolean energyMonitorEnabled; + + +} diff --git a/src/main/java/biz/nellemann/hmci/dto/xml/Metadata.java b/src/main/java/biz/nellemann/hmci/dto/xml/Metadata.java new file mode 100644 index 0000000..1d0bd3a --- /dev/null +++ b/src/main/java/biz/nellemann/hmci/dto/xml/Metadata.java @@ -0,0 +1,21 @@ +package biz.nellemann.hmci.dto.xml; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonProperty; + +@JsonIgnoreProperties(ignoreUnknown = true) +public class Metadata { + + @JsonProperty("Atom") + public Atom atom; + + @JsonIgnoreProperties(ignoreUnknown = true) + public class Atom { + + @JsonProperty("AtomID") + public String atomID; + + @JsonProperty("AtomCreated") + public String atomCreated; + } +} diff --git a/src/main/java/biz/nellemann/hmci/dto/xml/XmlEntry.java b/src/main/java/biz/nellemann/hmci/dto/xml/XmlEntry.java index 36c60ed..eaaa74d 100644 --- a/src/main/java/biz/nellemann/hmci/dto/xml/XmlEntry.java +++ b/src/main/java/biz/nellemann/hmci/dto/xml/XmlEntry.java @@ -57,6 +57,7 @@ public class XmlEntry implements Serializable { return managementConsoleEntry != null; } + @JsonProperty("ManagedSystem") private ManagedSystemEntry managedSystemEntry; @@ -69,6 +70,18 @@ public class XmlEntry implements Serializable { } + @JsonProperty("ManagedSystemPcmPreference") + private ManagedSystemPcmPreference managedSystemPcmPreference; + + public ManagedSystemPcmPreference getManagedSystemPcmPreference() { + return managedSystemPcmPreference; + } + + public boolean isManagedSystemPcmPreference() { + return managedSystemPcmPreference != null; + } + + @JsonAlias("VirtualIOServer") private VirtualIOServerEntry virtualIOServerEntry; diff --git a/src/test/resources/2-managed-system-pcm-preferences.xml b/src/test/resources/2-managed-system-pcm-preferences.xml new file mode 100644 index 0000000..833133a --- /dev/null +++ b/src/test/resources/2-managed-system-pcm-preferences.xml @@ -0,0 +1,43 @@ + + + b597e4da-2aab-3f52-8616-341d62153559 + Performance and Capacity Monitoring Preferences + + + + + 103bfcca-ea8f-48bc-b946-e8928343a6f7 + 2022-11-26T08:48:26.340+01:00 + Performance and Capacity Monitoring Preferences + 2022-11-26T08:48:26.340+01:00 + + IBM Power Systems Management Console + + + + + + b597e4da-2aab-3f52-8616-341d62153559 + 1669448906337 + + + Server-9009-42A-SN21F64EV + + + + + 9009 + 42A + 21F64EV + + true + true + true + false + false + true + + + + + diff --git a/src/test/resources/hmci.toml b/src/test/resources/hmci.toml index ada3043..09e118d 100644 --- a/src/test/resources/hmci.toml +++ b/src/test/resources/hmci.toml @@ -21,19 +21,11 @@ includeSystems = [ 'onlyThisSys', 'andOnlyThisSys' ] excludePartitions = [ 'notThisPartition' ] includePartitions = [ 'onlyThisPartition' ] - -# SVC -[svc] -name = "site2" -url = "https://10.32.64.182:7443" -username = "superuser" -password = "Password" - # Example [hmc.site2] url = "https://10.10.20.20:12443" username = "viewer" password = "someSecret" -unsafe = false +trust = false energy = true trace = "/tmp/pcm-files"