/*
* Copyright (c) 2015 EMC Corporation
* All Rights Reserved
*/
package com.emc.storageos.isilon.restapi;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.StringTokenizer;
import java.util.concurrent.ConcurrentHashMap;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.jcraft.jsch.Channel;
import com.jcraft.jsch.ChannelExec;
import com.jcraft.jsch.JSch;
import com.jcraft.jsch.Session;
/**
* Responsible for sending Isilon Cluster CLI commands to the Isilon cluster using SSH.
*/
public class IsilonSshApi {
private static final Logger _log = LoggerFactory.getLogger(IsilonSshApi.class);
private String _host;
private String _userName;
private String _password;
// Amount of time in milliseconds to wait for a response
private int _respDelay = 1000;
private static final int BUFFER_SIZE = 1024;
private static final int DEFAULT_PORT = 22;
public void setConnParams(String host, String user, String password) {
_host = host;
_userName = user;
_password = password;
}
/**
* Clear the connection parameters.
*/
public void clearConnParams() {
_host = null;
_userName = null;
_password = null;
}
/**
*
* @param delay time in milliseconds
*/
public void setResponseDelay(int delay) {
if (delay < 0) {
_respDelay = 0;
return;
}
_respDelay = delay;
}
/**
* Executes a command on the Isilon CLI.
*
* @param command command to execute on the Isilon CLI.
* @param request payload for the command
* @return result of executing the command.
*/
public IsilonXMLApiResult executeSsh(String command, String request) {
IsilonXMLApiResult result = new IsilonXMLApiResult();
if ((_host == null) || (_userName == null) || (_password == null)) {
_log.error("Invalid connection parameter");
result.setCommandFailed();
return result;
}
String cmd = "isi " + command + " " + request;
_log.info("executeSsh: cmd: " + cmd);
InputStream in = null;
Session session = null;
Channel channel = null;
try {
java.util.Properties config = new java.util.Properties();
config.put("StrictHostKeyChecking", "no");
JSch jsch = new JSch();
session = jsch.getSession(_userName, _host, DEFAULT_PORT);
session.setPassword(_password);
session.setConfig(config);
session.connect();
channel = session.openChannel("exec");
((ChannelExec) channel).setCommand(cmd);
channel.setInputStream(null);
in = channel.getInputStream();
channel.connect();
byte[] tmp = new byte[BUFFER_SIZE];
StringBuilder cmdResults = new StringBuilder();
while (true) {
while (in.available() > 0) {
int i = in.read(tmp, 0, BUFFER_SIZE);
if (i < 0)
break;
cmdResults.append(new String(tmp, 0, i));
}
if (channel.isClosed()) {
_log.info("Ssh exit status: " + channel.getExitStatus());
result.setMessage(cmdResults.toString());
// Set the command result status.
if (channel.getExitStatus() == 0) {
StringTokenizer st = new StringTokenizer(cmdResults.toString());
if (st.hasMoreTokens()) {
st.nextToken(); // data mover name
}
String res = "";
if (st.hasMoreTokens()) {
res = st.nextToken(); // contains status or result.
}
if (res.equalsIgnoreCase("done")) {
result.setCommandSuccess();
} else if (res.equalsIgnoreCase("error")) {
result.setCommandFailed();
} else {
result.setCommandSuccess();
}
} else {
result.setCommandFailed();
}
break;
}
try {
Thread.sleep(_respDelay);
} catch (InterruptedException e) {
_log.error("VNX File executeSsh Communication thread interrupted for command: " + cmd, e);
}
}
_log.info("executeSsh: Done");
} catch (Exception e) {
_log.error("VNX File executeSsh connection failed while attempting to execute: " + cmd, e);
result.setCommandFailed();
result.setMessage(e.getMessage());
} finally {
if (in != null) {
try {
in.close();
} catch (IOException ignored) {
}
}
if (channel != null) {
channel.disconnect();
}
if (session != null) {
session.disconnect();
}
}
return result;
}
/**
* get the network pool list
*
* @return
*/
public Map<String, List<String>> getNetworkPools() {
String command = "networks list pools";
String request = "-v";
Map<String, List<String>> networkpoolMap = new ConcurrentHashMap<String, List<String>>();
try {
IsilonXMLApiResult result = this.executeSsh(command, request);
// Parse message to get map
String[] entries = result.getMessage().split("\n");
String accessZone = "";
for (String entry : entries) {
String[] entryElements = entry.split(":");
if (entryElements.length >= 2) {
String key = entryElements[0].trim();
String values = entryElements[1].trim();
// first check for access zone
if (key.equalsIgnoreCase("Access Zone")) {
// get the access zone name
String[] entrySubElements = values.split(" ");
if (entrySubElements.length >= 2) {
accessZone = entrySubElements[0].trim();
List<String> accessZonesTemp = networkpoolMap.get(accessZone);
if (accessZonesTemp == null) {
accessZonesTemp = new ArrayList<>();
networkpoolMap.put(accessZone, accessZonesTemp);
}
}
}
// next sequence's check for zone and finally set access zone emtpy for next iteration
if (key.equalsIgnoreCase("Zone") && !accessZone.isEmpty()) {
List<String> accesszones = networkpoolMap.get(accessZone);
if (null != accesszones) {
accesszones.add(values);
networkpoolMap.put(accessZone, accesszones);
accessZone = "";
}
}
}
}
} catch (Exception ex) {
_log.error("Isilon getNetworkPools is failed {} due to {}", command, ex);
}
return networkpoolMap;
}
public Double getClusterSize() {
String command = "storagepool nodepools";
String request = "list -v";
Double maxCapacity = 0.0;
Map<String, String> clusterSizeMap = new ConcurrentHashMap<String, String>();
try {
IsilonXMLApiResult result = this.executeSsh(command, request);
// Parse message to get map
String[] entries = result.getMessage().split("\n");
for (String entry : entries) {
String[] entryElements = entry.split(":");
if (entryElements.length >= 2) {
String key = entryElements[0].trim();
String values = entryElements[1].trim();
clusterSizeMap.put(key, values);
_log.info("Adding File Name {} and Path {}", key, values);
}
}
// calcuate max capacity
String vhsValue = clusterSizeMap.get("Virtual Hot Spare Bytes");
String totalBytes = clusterSizeMap.get("Total Bytes");
Double dtotalBytes = getCapacityInGB(totalBytes);
Double dvhsByes = getCapacityInGB(vhsValue);
maxCapacity = dtotalBytes - dvhsByes;
_log.info("Max capacity value : {}", maxCapacity);
return maxCapacity;
} catch (Exception ex) {
_log.error("Isilon cluster map is failed {} due to {}", command, ex);
}
return maxCapacity;
}
/**
* storage capacity into gb
*
* @param data
* @return
*/
public Double getCapacityInGB(final String data) {
Double bytes = 0.0;
if (data != null && !data.isEmpty()) {
char bitFormat = data.charAt(data.length() - 1);
Double totalBytes = Double.parseDouble(data.substring(0, data.length() - 1));
if (bitFormat == 'P') {
bytes = (totalBytes * 1024) * 1024;
} else if (bitFormat == 'T') {
bytes = totalBytes * 1024;
} else if (bitFormat == 'G') {
bytes = totalBytes;
} else {
bytes = 0.0;
}
}
return bytes;
}
}