hmci/src/main/groovy/biz/nellemann/hmci/HmcClient.groovy

416 lines
14 KiB
Groovy
Raw Normal View History

2020-08-18 11:49:48 +00:00
/**
* Copyright 2020 Mark Nellemann <mark.nellemann@gmail.com>
*
* 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.
*/
2020-08-07 06:13:48 +00:00
package biz.nellemann.hmci
import biz.nellemann.hmci.Configuration.HmcObject
import com.squareup.moshi.Moshi
import groovy.transform.CompileDynamic
import groovy.transform.CompileStatic
2020-08-07 06:13:48 +00:00
import groovy.util.logging.Slf4j
import groovy.xml.XmlSlurper
import okhttp3.*
import org.jsoup.Jsoup
import org.jsoup.nodes.Document
import org.jsoup.nodes.Element
import org.jsoup.select.Elements
import javax.net.ssl.*
2020-08-07 06:13:48 +00:00
import java.security.SecureRandom
import java.security.cert.CertificateException
import java.security.cert.X509Certificate
2020-08-07 06:13:48 +00:00
@Slf4j
@CompileStatic
2020-08-10 13:44:14 +00:00
class HmcClient {
2020-08-07 06:13:48 +00:00
2020-08-08 11:24:48 +00:00
private final MediaType MEDIA_TYPE_IBM_XML_LOGIN = MediaType.parse("application/vnd.ibm.powervm.web+xml; type=LogonRequest");
2020-08-07 06:13:48 +00:00
private final String hmcId
2020-08-07 06:13:48 +00:00
private final String baseUrl
private final String username
private final String password
private final Boolean unsafe
2020-08-07 06:13:48 +00:00
protected Integer responseErrors = 0
2020-08-07 06:13:48 +00:00
protected String authToken
private final OkHttpClient client
HmcClient(HmcObject configHmc) {
this.hmcId = configHmc.name
this.baseUrl = configHmc.url
this.username = configHmc.username
this.password = configHmc.password
this.unsafe = configHmc.unsafe
2020-08-07 06:13:48 +00:00
if(unsafe) {
this.client = getUnsafeOkHttpClient()
} else {
this.client = new OkHttpClient()
}
2020-08-07 06:13:48 +00:00
}
/**
* Logon to the HMC and get an authentication token for further requests.
*
* @throws IOException
*/
//@CompileDynamic
void login(Boolean force = false) throws IOException {
if(authToken && !force) {
return
}
2020-08-07 06:13:48 +00:00
log.info("Connecting to HMC - " + baseUrl);
2020-08-07 06:13:48 +00:00
String payload = """\
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<LogonRequest xmlns="http://www.ibm.com/xmlns/systems/power/firmware/web/mc/2012_10/" schemaVersion="V1_0">
<UserID>${username}</UserID>
<Password>${password}</Password>
</LogonRequest>"""
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);
2020-08-07 06:13:48 +00:00
// Get response body and parse
String responseBody = response.body().string();
response.body().close()
2020-08-07 06:13:48 +00:00
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)
}
2020-08-07 06:13:48 +00:00
}
/**
* Logoff from the HMC and remove any session
*
*/
2020-08-07 06:13:48 +00:00
void logoff() {
2020-08-14 07:34:44 +00:00
if(!authToken) {
return
}
2020-08-07 06:13:48 +00:00
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()")
2020-08-07 06:13:48 +00:00
}
/**
* Return Map of ManagedSystems seen by this HMC
*
* @return
*/
//@CompileDynamic
2020-08-10 13:44:14 +00:00
Map<String, ManagedSystem> getManagedSystems() {
2020-08-07 06:13:48 +00:00
URL url = new URL(String.format("%s/rest/api/uom/ManagedSystem", baseUrl))
Response response = getResponse(url)
String responseBody = response.body().string()
2020-08-10 13:44:14 +00:00
Map<String,ManagedSystem> managedSystemsMap = new HashMap<String, ManagedSystem>()
2020-08-07 06:13:48 +00:00
// Do not try to parse empty response
if(responseBody.empty || responseBody.size() < 1) {
responseErrors++
return managedSystemsMap
}
try {
Document doc = Jsoup.parse(responseBody);
Elements managedSystems = doc.select("ManagedSystem|ManagedSystem") // doc.select("img[src$=.png]");
for(Element el : managedSystems) {
ManagedSystem system = new ManagedSystem(
hmcId,
el.select("Metadata > Atom > AtomID").text() as String,
el.select("SystemName").text() as String,
el.select("MachineTypeModelAndSerialNumber > MachineType").text() as String,
el.select("MachineTypeModelAndSerialNumber > Model").text() as String,
el.select("MachineTypeModelAndSerialNumber > SerialNumber").text() as String,
)
managedSystemsMap.put(system.id, system)
log.info("getManagedSystems() - Found system: " + system.toString())
2020-08-07 06:13:48 +00:00
}
} catch(Exception e) {
log.warn("getManagedSystems() - xml parse error", e);
2020-08-07 06:13:48 +00:00
}
2020-08-10 13:44:14 +00:00
return managedSystemsMap
2020-08-07 06:13:48 +00:00
}
/**
* Return Map of LogicalPartitions seen by a ManagedSystem on this HMC
2020-08-07 06:13:48 +00:00
* @param UUID of managed system
* @return
*/
//@CompileDynamic
Map<String, LogicalPartition> getLogicalPartitionsForManagedSystem(ManagedSystem system) {
URL url = new URL(String.format("%s/rest/api/uom/ManagedSystem/%s/LogicalPartition", baseUrl, system.id))
2020-08-07 06:13:48 +00:00
Response response = getResponse(url)
String responseBody = response.body().string()
2020-08-10 13:44:14 +00:00
Map<String, LogicalPartition> partitionMap = new HashMap<String, LogicalPartition>() {}
// Do not try to parse empty response
if(responseBody.empty || responseBody.size() < 1) {
responseErrors++
return partitionMap
}
try {
Document doc = Jsoup.parse(responseBody);
Elements logicalPartitions = doc.select("LogicalPartition|LogicalPartition") // doc.select("img[src$=.png]");
for(Element el : logicalPartitions) {
LogicalPartition logicalPartition = new LogicalPartition(
el.select("PartitionUUID").text() as String,
el.select("PartitionName").text() as String,
el.select("PartitionType").text() as String,
system
)
partitionMap.put(logicalPartition.id, logicalPartition)
log.info("getLogicalPartitionsForManagedSystem() - Found partition: " + logicalPartition.toString())
2020-08-07 06:13:48 +00:00
}
} catch(Exception e) {
log.warn("getLogicalPartitionsForManagedSystem() - xml parse error", e);
2020-08-07 06:13:48 +00:00
}
2020-08-10 13:44:14 +00:00
return partitionMap
2020-08-07 06:13:48 +00:00
}
/**
* Parse XML feed to get PCM Data in JSON format
* @param systemId
* @return
*/
//@CompileDynamic
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))
2020-08-07 06:13:48 +00:00
Response response = getResponse(url)
String responseBody = response.body().string()
String jsonBody
// Do not try to parse empty response
if(responseBody.empty || responseBody.size() < 1) {
responseErrors++
return jsonBody
}
try {
Document doc = Jsoup.parse(responseBody);
Element entry = doc.select("entry").first();
Element link = entry.select("link[href]").first();
if(link.attr("type") == "application/json") {
String href = (String) link.attr("href");
log.debug("getPcmDataForManagedSystem() - json url: " + href);
jsonBody = getResponseBody(new URL(href));
2020-08-07 06:13:48 +00:00
}
} catch(Exception e) {
log.warn("getPcmDataForManagedSystem() - xml parse error", e);
2020-08-07 06:13:48 +00:00
}
return jsonBody
2020-08-07 06:13:48 +00:00
}
/**
* Parse XML feed to get PCM Data in JSON format
* @param systemId
* @param partitionId
* @return
*/
//@CompileDynamic
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))
2020-08-07 06:13:48 +00:00
Response response = getResponse(url)
String responseBody = response.body().string()
String jsonBody
// Do not try to parse empty response
if(responseBody.empty || responseBody.size() < 1) {
responseErrors++
return jsonBody
}
try {
Document doc = Jsoup.parse(responseBody);
Element entry = doc.select("entry").first();
Element link = entry.select("link[href]").first();
if(link.attr("type") == "application/json") {
String href = (String) link.attr("href");
log.debug("getPcmDataForLogicalPartition() - json url: " + href);
jsonBody = getResponseBody(new URL(href));
}
} catch(Exception e) {
log.warn("getPcmDataForLogicalPartition() - xml parse error", e);
}
return jsonBody
}
/**
* Return body text from a HTTP response from the HMC
*
* @param url
* @return
*/
protected String getResponseBody(URL url) {
Response response = getResponse(url)
String body = response.body().string()
response.body().close()
return body
2020-08-07 06:13:48 +00:00
}
/**
* Return a Response from the HMC
*
* @param url
* @return
*/
//@CompileDynamic
private Response getResponse(URL url, Integer retry = 0) {
2020-08-07 06:13:48 +00:00
if(responseErrors > 2) {
responseErrors = 0
login(true)
return getResponse(url, retry++)
}
2020-08-07 06:13:48 +00:00
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();
2020-08-14 07:34:44 +00:00
if (!response.isSuccessful()) {
response.body().close()
if(response.code() == 401) {
login(true)
return getResponse(url, retry++)
2020-08-14 07:34:44 +00:00
}
if(retry < 2) {
log.warn("getResponse() - Retrying due to unexpected response: " + response.code())
return getResponse(url, retry++)
}
log.error("getResponse() - Unexpected response: " + response.code())
throw new IOException("getResponse() - Unexpected response: " + response.code())
2020-08-14 07:34:44 +00:00
};
2020-08-07 06:13:48 +00:00
return response
}
/**
* Provide an unsafe (ignoring SSL problems) OkHttpClient
*
* @return
*/
2020-08-07 06:13:48 +00:00
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);
}
}
}