/** * Copyright 2020 Mark Nellemann * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package biz.nellemann.hmci import groovy.util.logging.Slf4j import groovy.xml.XmlSlurper import okhttp3.MediaType import okhttp3.OkHttpClient import okhttp3.Request import okhttp3.RequestBody import okhttp3.Response import javax.net.ssl.HostnameVerifier import javax.net.ssl.SSLContext import javax.net.ssl.SSLSession import javax.net.ssl.SSLSocketFactory import javax.net.ssl.TrustManager import javax.net.ssl.X509TrustManager import java.security.SecureRandom import java.security.cert.CertificateException import java.security.cert.X509Certificate; @Slf4j class HmcClient { private final MediaType MEDIA_TYPE_IBM_XML_LOGIN = MediaType.parse("application/vnd.ibm.powervm.web+xml; type=LogonRequest"); private final String hmcId private final String baseUrl private final String username private final String password private final Boolean unsafe protected String authToken private final OkHttpClient client HmcClient(String hmcId, String baseUrl, String username, String password, Boolean unsafe = false) { this.hmcId = hmcId this.baseUrl = baseUrl this.username = username this.password = password this.unsafe = unsafe if(unsafe) { this.client = getUnsafeOkHttpClient() } else { this.client = new OkHttpClient() } } /** * Logon to the HMC and get an authentication token for further requests. * * @throws IOException */ void login() throws IOException { String payload = """\ ${username} ${password} """ URL url = new URL(String.format("%s/rest/api/web/Logon", baseUrl)) Request request = new Request.Builder() .url(url) //.addHeader("Content-Type", "application/vnd.ibm.powervm.web+xml; type=LogonRequest") .addHeader("Accept", "application/vnd.ibm.powervm.web+xml; type=LogonResponse") .addHeader("X-Audit-Memento", "hmci") .put(RequestBody.create(payload, MEDIA_TYPE_IBM_XML_LOGIN)) .build(); try { Response response = client.newCall(request).execute(); if (!response.isSuccessful()) throw new IOException("Unexpected code " + response); // Get response body and parse String responseBody = response.body.string() def xml = new XmlSlurper().parseText(responseBody) authToken = xml.toString() log.debug("login() - Auth Token: " + authToken) } catch(Exception e) { log.error(e.message) throw new Exception(e) } } /** * Logoff from the HMC and remove any session * */ void logoff() { if(!authToken) { return } URL absUrl = new URL(String.format("%s/rest/api/web/Logon", baseUrl)) Request request = new Request.Builder() .url(absUrl) .addHeader("Content-Type", "application/vnd.ibm.powervm.web+xml; type=LogonRequest") .addHeader("X-API-Session", authToken) .delete() .build(); Response response = client.newCall(request).execute(); if (!response.isSuccessful()) throw new IOException("Unexpected code " + response); authToken = null log.debug("logoff()") } /** * Return Map of ManagedSystems seen by this HMC * * @return */ Map getManagedSystems() { URL url = new URL(String.format("%s/rest/api/uom/ManagedSystem", baseUrl)) Response response = getResponse(url) String responseBody = response.body.string() Map managedSystemsMap = new HashMap() def feed = new XmlSlurper().parseText(responseBody) feed?.entry?.each { entry -> entry.content.each { content -> content.ManagedSystem.each { system -> ManagedSystem managedSystem = new ManagedSystem( hmcId, entry.id as String, system.SystemName as String, system.MachineTypeModelAndSerialNumber?.MachineType as String, system.MachineTypeModelAndSerialNumber?.Model as String, system.MachineTypeModelAndSerialNumber?.SerialNumber as String ) managedSystemsMap.put(managedSystem.id, managedSystem) log.debug("getManagedSystems() - Found system: " + managedSystem.toString()) } } } return managedSystemsMap } /** * Return Map of LogicalPartitions seen by a ManagedSystem on this HMC * @param UUID of managed system * @return */ Map getLogicalPartitionsForManagedSystem(ManagedSystem system) { URL url = new URL(String.format("%s/rest/api/uom/ManagedSystem/%s/LogicalPartition", baseUrl, system.id)) Response response = getResponse(url) String responseBody = response.body.string() Map partitionMap = new HashMap() {} def feed = new XmlSlurper().parseText(responseBody) feed?.entry?.each { entry -> //log.debug("Entry") entry.content.each { content -> //log.debug("Content") content.LogicalPartition.each { partition -> LogicalPartition logicalPartition = new LogicalPartition( partition.PartitionUUID as String, partition.PartitionName as String, partition.PartitionType as String, system ) partitionMap.put(logicalPartition.id, logicalPartition) log.debug("getLogicalPartitionsForManagedSystem() - Found partition: " + logicalPartition.toString()) } } } return partitionMap } /** * Parse XML feed to get PCM Data in JSON format * @param systemId * @return */ String getPcmDataForManagedSystem(ManagedSystem system) { log.debug("getPcmDataForManagedSystem() - " + 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 = response.body.string() String jsonBody // Parse XML and fetch JSON link def feed = new XmlSlurper().parseText(responseBody) feed?.entry?.each { entry -> String link = entry.link["@href"] if(entry.category["@term"] == "ManagedSystem") { jsonBody = getReponseBody(new URL(link)) } } return jsonBody } /** * Parse XML feed to get PCM Data in JSON format * @param systemId * @param partitionId * @return */ String getPcmDataForLogicalPartition(LogicalPartition partition) { 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)) Response response = getResponse(url) String responseBody = response.body.string() //log.debug(responseBody) String jsonBody // Parse XML and fetch JSON link def feed = new XmlSlurper().parseText(responseBody) feed?.entry?.each { entry -> String link = entry.link["@href"] if(entry.category["@term"] == "LogicalPartition") { jsonBody = getReponseBody(new URL(link)) } } return jsonBody } /** * Return body text from a HTTP response from the HMC * * @param url * @return */ protected String getReponseBody(URL url) { //log.debug("getBody() - " + url.toString()) Response response = getResponse(url) return response.body.string() } /** * Return a Response from the HMC * * @param url * @return */ private Response getResponse(URL url) { 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) .get() .build(); Response response = client.newCall(request).execute(); if (!response.isSuccessful()) { if(response.code == 401) { login() } else { throw new IOException("Unexpected code " + response) } }; return response } /** * Provide an unsafe (ignoring SSL problems) OkHttpClient * * @return */ private static OkHttpClient getUnsafeOkHttpClient() { try { // Create a trust manager that does not validate certificate chains final TrustManager[] trustAllCerts = new TrustManager[] { new X509TrustManager() { @Override public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException { } @Override public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException { } @Override public X509Certificate[] getAcceptedIssuers() { return new X509Certificate[]{}; } } }; // Install the all-trusting trust manager final SSLContext sslContext = SSLContext.getInstance("SSL"); sslContext.init(null, trustAllCerts, new SecureRandom()); // Create an ssl socket factory with our all-trusting manager final SSLSocketFactory sslSocketFactory = sslContext.getSocketFactory(); OkHttpClient.Builder builder = new OkHttpClient.Builder(); builder.sslSocketFactory(sslSocketFactory, (X509TrustManager)trustAllCerts[0]); builder.hostnameVerifier(new HostnameVerifier() { @Override public boolean verify(String hostname, SSLSession session) { return true; } }); OkHttpClient okHttpClient = builder.build(); return okHttpClient; } catch (Exception e) { throw new RuntimeException(e); } } }