/*
* Copyright (c) 2008-2012 EMC Corporation
* All Rights Reserved
*/
package com.emc.storageos.vnx.xmlapi;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.StringTokenizer;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;
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 VNX CLI commands to the VNX File System using SSH.
*/
public class VNXFileSshApi {
/** The Constant SERVER_EXPORT_CMD. */
public static final String SERVER_EXPORT_CMD = "/nas/bin/server_export";
/** The Constant SERVER_MOUNT_CMD. */
public static final String SERVER_MOUNT_CMD = "/nas/bin/server_mount";
/** The Constant SERVER_UNMOUNT_CMD. */
public static final String SERVER_UNMOUNT_CMD = "/nas/bin/server_umount";
/** The Constant SERVER_INFO_CMD. */
public static final String SERVER_INFO_CMD = "/nas/bin/nas_server";
/** The Constant SERVER_USER_CMD. */
public static final String SERVER_USER_CMD = "/nas/sbin/server_user";
/** The Constant EXPORT. */
public static final String EXPORT = "EXPORT";
/** The Constant VNX_CIFS. */
public static final String VNX_CIFS = "cifs";
/** The Constant NAS_FS. */
public static final String NAS_FS = "/nas/bin/nas_fs";
/** The Constant SHARE. */
public static final String SHARE = "share";
/** The Constant SERVER_MODEL. */
public static final String SERVER_MODEL = "/nas/sbin/model";
/** The Constant _log. */
private static final Logger _log = LoggerFactory.getLogger(VNXFileSshApi.class);
/** The _host. */
private String _host;
/** The _user name. */
private String _userName;
/** The _password. */
private String _password;
/** The _resp delay. */
// Amount of time in milliseconds to wait for a response
private int _respDelay = 1000;
/** The Constant BUFFER_SIZE. */
private static final int BUFFER_SIZE = 1024;
/** The Constant DEFAULT_PORT. */
private static final int DEFAULT_PORT = 22;
/**
* The Enum SecurityTypes.
*/
// TODO: change build files to be able to access FileShareExport.SecurityTypes.
private enum SecurityTypes {
/** The sys. */
sys,
/** The krb5. */
krb5,
/** The krb5i. */
krb5i,
/** The krb5p. */
krb5p
}
/**
* Sets the host and user credentials for the VNX Device to communicate with.
*
* @param host host name or ip address
* @param user user name
* @param password user password
*/
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;
}
/**
* Sets the response delay.
*
* @param delay time in milliseconds
*/
public void setResponseDelay(int delay) {
if (delay < 0) {
_respDelay = 0;
return;
}
_respDelay = delay;
}
/**
* Format cifs cmd.
*
* @param exports the exports
* @param netBios the net bios
* @return the string
*/
private String formatCifsCmd(List<VNXFileExport> exports, String netBios) {
VNXFileExport fileExp = exports.get(0);
StringBuilder command = new StringBuilder();
command.append(" -Protocol cifs -ignore ");
String name = fileExp.getExportName();
if (null != name && !name.isEmpty()) {
command.append("-name ");
command.append(name + " ");
}
boolean permExists = false;
if (fileExp.getPermissions().equalsIgnoreCase("read")) {
command.append("-option ro");
permExists = true;
}
String maxUsers = fileExp.getMaxUsers();
if (maxUsers != null && !maxUsers.equalsIgnoreCase("-1")) {
if (permExists) {
command.append(",");
} else {
command.append("-option ");
permExists = true;
}
command.append("maxusr=");
command.append(maxUsers);
}
if (null != netBios && netBios.length() > 0) {
if (permExists) {
command.append(",");
} else {
command.append("-option ");
}
command.append("netbios=");
command.append("\"" + netBios + "\" ");
}
String comment = fileExp.getComment();
if (null != comment && !comment.isEmpty()) {
command.append(" " + "-comment ");
command.append("\"" + comment + "\" ");
}
return command.toString();
}
/**
* Format nfs cmd.
*
* @param exports the exports
* @param userInfo the user info
* @return the string
*/
private String formatNfsCmd(List<VNXFileExport> exports, Map<String, String> userInfo) {
StringBuilder options = new StringBuilder();
options.append(" -Protocol nfs -ignore -option ");
String access = null;
boolean append = false;
boolean first = true;
boolean rwOnly = false;
String rwPerm = "rw";
String accessPerm = "access";
ArrayList<String> permissions = new ArrayList<String>();
for (SecurityTypes secType : SecurityTypes.values()) {
_log.debug("Processing security type: {}", secType.name());
for (VNXFileExport exp : exports) {
permissions.add(exp.getPermissions());
}
if (permissions.size() == 1 && permissions.get(0).equalsIgnoreCase(rwPerm)) {
// Export contains just rw only.. so set it to access list
rwOnly = true;
}
for (VNXFileExport exp : exports) {
if (secType.name().equals(exp.getSecurityType())) {
_log.debug("Matching export with perms {}", exp.getPermissions());
if (append) {
options.append(",");
} else {
append = true;
}
String perms = exp.getPermissions();
if (first) {
options.append("sec=");
options.append(secType.name());
if (!perms.isEmpty()) {
options.append(":");
}
first = false;
}
if (!perms.isEmpty()) {
String translatedPerm = perms;
if (rwOnly) {
translatedPerm = accessPerm;
}
access = createAccessString(secType.name(),
translatedPerm,
exp.getClients(),
exp.getRootUserMapping(),
userInfo);
options.append(access);
}
}
}
first = true;
permissions.clear();
}
// if the command ends with -option meaning that
// 1. We are removing all export rules
// 2. VNX overwrites the export with the new command we execute
// 3. no rules with -option results error in command execution
// 4. -option should be taken out as part of command when no rules specified.
// 5. when the command ends with -option -- we can treat this command is missing the rules.
// 6. Hence -option shouldn't be the part of the command
_log.info("Validating if requested to delete all export rules");
if (options != null && options.toString() != null && options.toString().trim().endsWith("-option")) {
String command = options.toString();
command = command.replaceAll("-option", "");
options.setLength(0);
options.append(command);
}
String comment = exports.get(0).getComment();
if (comment != null && !comment.isEmpty()) {
options.append(" " + "-comment ").append("\"" + comment + "\" ");
}
return options.toString();
}
/**
* Create the command string for removing a CIFS file share.
*
* @param dataMover data mover that contains the CIFS file share
* @param fileShare name of the file share to remove.
* @param netBios netbios used
* @return formatted command string to be used by the VNX File CLI.
*/
public String formatDeleteShareCmd(String dataMover, String fileShare, String netBios) {
StringBuilder cmd = new StringBuilder();
cmd.append(dataMover);
cmd.append(" -unexport -name ");
cmd.append(fileShare);
if (netBios != null && netBios.length() > 0) {
cmd.append(" -option netbios=");
cmd.append("\"" + netBios + "\" ");
}
return cmd.toString();
}
/**
* Create the command string for exporting a file system.
*
* @param dataMover data mover that the export will reside on
* @param exports contains file export details
* @param userInfo the user info
* @param netBios the net bios
* @return formatted command required by the VNX CLI for file export
*/
public String formatExportCmd(String dataMover, List<VNXFileExport> exports, Map<String, String> userInfo, String netBios) {
// Verify that there is at least one entry in exports
if (exports.isEmpty()) {
_log.debug("There is no entry to export");
return null;
}
String mountPoint = entryPathsDiffer(exports);
// Verify that all export entries apply to the same file system or subdirectory
if (mountPoint == null) {
_log.debug("Single ssh API command is being applied to multiple paths.");
return null;
}
// Verify that all export entries apply to the same file system or subdirectory
String protocol = entryProtocolsDiffer(exports);
if (protocol == null) {
_log.debug("Single ssh API command is being applied to multiple protocols.");
return null;
}
StringBuilder options = new StringBuilder();
String result = null;
if (protocol.equalsIgnoreCase(VNX_CIFS)) {
result = formatCifsCmd(exports, netBios);
} else {
result = formatNfsCmd(exports, userInfo);
}
options.append(result);
options.append(" ");
StringBuilder cmd = new StringBuilder();
cmd.append(dataMover);
cmd.append(options.toString());
cmd.append(mountPoint);
return cmd.toString();
}
/**
* Format check share for export cmd.
*
* @param dataMover the data mover
* @param exports the exports
* @param userInfo the user info
* @param netBios the net bios
* @return the string
*/
public String formatCheckShareForExportCmd(String dataMover, List<VNXFileExport> exports, Map<String, String> userInfo, String netBios) {
// Verify that there is at least one entry in exports
if (exports.isEmpty()) {
_log.debug("There is no entry to export");
return null;
}
String mountPoint = entryPathsDiffer(exports);
// Verify that all export entries apply to the same file system or subdirectory
if (mountPoint == null) {
_log.debug("Single ssh API command is being applied to multiple paths.");
return null;
}
String exportName= exports.get(0).getExportName();
if(exportName == null) {
return null;
}
StringBuilder cmd = new StringBuilder();
cmd.append(dataMover);
cmd.append(" -list -name ");
cmd.append(exportName);
return cmd.toString();
}
/**
* Create the command string for deleting file system export.
*
* @param dataMover data mover that the export will reside on
* @param path path whose export will be deleted
* @return formatted command required by the VNX CLI for file export
*/
public String formatDeleteNfsExportCmd(String dataMover, String path) {
StringBuilder cmd = new StringBuilder();
cmd.append(" ");
cmd.append(dataMover);
cmd.append(" -unexport ");
cmd.append(" ");
cmd.append(path);
return cmd.toString();
}
/**
* Create the command string for deleting file system mount.
*
* @param dataMover data mover that the export will reside on
* @param path path whose export will be deleted
* @param protocol protocol
* @return formatted command required by the VNX CLI for file export
*/
public String formatUnMountCmd(String dataMover, String path, String protocol) {
// server_umount server_3 /testSaravRoot3
StringBuilder cmd = new StringBuilder();
cmd.append(" ");
cmd.append(dataMover);
cmd.append(" ");
cmd.append("-perm");
cmd.append(" ");
cmd.append(path);
return cmd.toString();
}
/**
* Create the command string for create file system mount.
*
* @param dataMover data mover that the export will reside on
* @param fileSystem file system that is being mounted
* @param path path whose export will be deleted
* @return formatted command required by the VNX CLI for file export
*/
public String formatMountCmd(String dataMover, String fileSystem, String path) {
// server_mount server_3 testFS /testFS
StringBuilder cmd = new StringBuilder();
cmd.append(" ");
cmd.append(dataMover);
cmd.append(" ");
cmd.append(fileSystem);
cmd.append(" ");
cmd.append(path);
return cmd.toString();
}
/**
* Entry paths differ.
*
* @param exports the exports
* @return the string
*/
private String entryPathsDiffer(List<VNXFileExport> exports) {
HashSet<String> paths = new HashSet<>();
for (VNXFileExport exp : exports) {
paths.add(exp.getMountPoint());
}
if (paths.size() == 1) {
return paths.iterator().next();
}
return null;
}
/**
* Entry protocols differ.
*
* @param exports the exports
* @return the string
*/
private String entryProtocolsDiffer(List<VNXFileExport> exports) {
HashSet<String> protocols = new HashSet<>();
for (VNXFileExport exp : exports) {
protocols.add(exp.getProtocol());
}
if (protocols.size() == 1) {
return protocols.iterator().next();
}
return null;
}
/**
* Create an access string for VNX File exports.
*
* @param security security method: sys, krb5, krb5i, or krb5p
* @param perm access mode depending on security method: ro, rw=, ro=, root=, access=, etc.
* @param clients list of endpoints to be exported to
* @param anon mapping for an unknown or root user.
* @param userInfo the user info
* @return formatted access string to be used by the VNX CLI.
*/
public String createAccessString(String security, String perm, List<String> clients, String anon, Map<String, String> userInfo) {
StringBuilder access = new StringBuilder();
if (!perm.isEmpty()) {
if (perm.equals("ro")) {
access.append(perm);
}
if (!clients.isEmpty()) {
if (!perm.equals("ro")) {
access.append(perm);
}
access.append("=");
Iterator it = clients.iterator();
while (it.hasNext()) {
access.append(it.next());
if (it.hasNext()) {
access.append(":");
}
}
}
}
if (!anon.isEmpty() && security.equalsIgnoreCase(SecurityTypes.sys.name())) {
if (!anon.equalsIgnoreCase("nobody")) {
if (access.length() > 0) {
access.append(",anon=");
} else {
access.append("anon=");
}
if (anon.equalsIgnoreCase("root")) {
access.append("0");
} else {
try {
Integer.parseInt(anon);
access.append(anon);
} catch (NumberFormatException nfe) {
String uid = userInfo.get(anon);
if (uid != null && !uid.isEmpty()) {
access.append(uid);
} else {
// Illegal value for anon (not a UID or account name)
throw new IllegalArgumentException("Illegal Root User Mapping");
}
}
}
}
}
return access.toString();
}
/**
* Executes a command on the VNX File CLI.
*
* @param command command to execute on the VNX File CLI.
* @param request payload for the command
* @return result of executing the command.
*/
public XMLApiResult executeSsh(String command, String request) {
XMLApiResult result = new XMLApiResult();
if ((_host == null) || (_userName == null) || (_password == null)) {
_log.error("Invalid connection parameter");
result.setCommandFailed();
return result;
}
String cmd = "export NAS_DB=/nas;" + 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
}
if (!command.equalsIgnoreCase(SERVER_USER_CMD)) {
if (st.hasMoreTokens()) {
st.nextToken();
}
}
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) {
_log.error("Exception occured while closing input stream due to ", ignored);
}
}
if (channel != null) {
channel.disconnect();
}
if (session != null) {
session.disconnect();
}
}
return result;
}
/**
* Gets the fs mountpath map.
*
* @param dmName the dm name
* @return the fs mountpath map
*/
public Map<String, String> getFsMountpathMap(String dmName) {
Map<String, String> fsMountpathMap = new ConcurrentHashMap<String, String>();
try {
if (dmName == null) {
return null;
}
XMLApiResult result = this.executeSshRetry(VNXFileSshApi.SERVER_MOUNT_CMD, dmName);
// Parse message to get map
String[] entries = result.getMessage().split("\n");
for (String entry : entries) {
String[] entryElements = entry.split(" ");
if (entryElements.length > 2) {
String fileName = entry.split(" ")[0];
String path = entry.split(" ")[2];
if (path != null && (!path.startsWith("/root"))) {
fsMountpathMap.put(fileName, path);
_log.info("Adding File Name {} and Path {}", fileName, path);
} else {
_log.info("Skipping File Name {} and Path {}", fileName, path);
}
}
}
} catch (Exception ex) {
_log.error("VNX File getFsMountpathMap failed for Data Mover {} due to {}", dmName, ex);
}
return fsMountpathMap;
}
/**
* Gets the VDM interfaces.
*
* @param vdmName the vdm name
* @return the VDM interfaces
*/
public Map<String, String> getVDMInterfaces(String vdmName) {
Map<String, String> vdmIntfs = new ConcurrentHashMap<String, String>();
try {
// Prepare arguments for CLI command
// nas_server -info -vdm vdm_3_a
StringBuilder data = new StringBuilder();
data.append(" -info -vdm ");
data.append(vdmName);
// Execute command
XMLApiResult result = executeSshRetry(VNXFileSshApi.SERVER_INFO_CMD, data.toString());
if (result.isCommandSuccess()) {
// Parse message to get Interfaces and properties
String[] propList = result.getMessage().split("[\n]");
for (String prop : propList) {
if (prop != null && prop.startsWith(" interface")) {
_log.debug("Vdm Interface : " + prop);
String[] attrs = prop.split("=");
_log.debug("Vdm Interface : " + attrs[1]);
String[] intInfo = attrs[1].split(":");
String intName = intInfo[0].trim();
String capability = intInfo[1];
vdmIntfs.put(intName, capability);
_log.info("VDM interface {" + intName + "} - Capability -[" + capability + "]");
}
}
}
} catch (Exception ex) {
StringBuilder message = new StringBuilder();
message.append("VNX File getVDMInterfaces failed for Data Mover" + vdmName);
message.append(", due to {}");
_log.error(message.toString(), ex);
}
return vdmIntfs;
}
/**
* Gets the NFS exports for path.
*
* @param dmName the dm name
* @param path the path
* @return the NFS exports for path
*/
public Map<String, Map<String, String>> getNFSExportsForPath(String dmName, String path) {
Map<String, Map<String, String>> pathExportMap = new ConcurrentHashMap<String, Map<String, String>>();
try {
// Prepare arguments for CLI command
StringBuilder data = new StringBuilder();
data.append(dmName);
data.append(" -list ");
// Execute command
XMLApiResult result = this.executeSshRetry(VNXFileSshApi.SERVER_EXPORT_CMD, data.toString());
// Parse message to get export properties
String[] propList = result.getMessage().split("[\n]");
for (int i = 1; i < propList.length; i++) {
String exp = propList[i];
Map<String, String> fsExportInfoMap = new ConcurrentHashMap<String, String>();
String expPath = "";
if (exp.contains(path)) {
_log.info("Processing export path {} because it contains {}", exp, path);
String[] expList = exp.split("[ \n]");
// loose the double quotes from either ends
expPath = expList[1].substring(1, expList[1].length() - 1);
for (String prop : expList) {
String[] tempStr = prop.split("=");
if (tempStr.length > 1) {
String val = fsExportInfoMap.get(tempStr[0]);
if (val == null) {
fsExportInfoMap.put(tempStr[0], tempStr[1]);
} else {
fsExportInfoMap.put(tempStr[0], val + ":" + tempStr[1]);
}
}
}
pathExportMap.put(expPath, fsExportInfoMap);
} else {
_log.info("Ignoring export path {} because it doesnt contain {}", exp, path);
}
}
} catch (Exception ex) {
StringBuilder message = new StringBuilder();
message.append("VNX File getNFSExportsForPath failed for Data Mover" + dmName);
message.append(", path " + path);
message.append(", due to {}");
_log.error(message.toString(), ex);
}
return pathExportMap;
}
/**
* Gets the NFS exports for path.
*
* @param dmName the dm name
* @return the NFS exports for path
*/
public Map<String, Map<String, String>> getNFSExportsForPath(String dmName) {
Map<String, Map<String, String>> pathExportMap = new ConcurrentHashMap<String, Map<String, String>>();
try {
// Prepare arguments for CLI command
StringBuilder data = new StringBuilder();
data.append(dmName);
data.append(" -list ");
// Execute command
XMLApiResult result = this.executeSshRetry(VNXFileSshApi.SERVER_EXPORT_CMD, data.toString());
// Parse message to get export properties
String[] propList = result.getMessage().split("[\n]");
if (propList == null || propList.length < 1) {
// no exports found
return pathExportMap;
}
for (int i = 1; i < propList.length; i++) {
String exp = propList[i];
Map<String, String> fsExportInfoMap = new ConcurrentHashMap<String, String>();
String expPath = "";
String[] expList = exp.split("[ \n]");
// loose the double quotes from either ends
expPath = expList[1].substring(1, expList[1].length() - 1);
for (String prop : expList) {
String[] tempStr = prop.split("=");
if (tempStr.length > 1) {
String val = fsExportInfoMap.get(tempStr[0]);
if (val == null) {
fsExportInfoMap.put(tempStr[0], tempStr[1]);
} else {
fsExportInfoMap.put(tempStr[0], val + ":" + tempStr[1]);
}
}
}
pathExportMap.put(expPath, fsExportInfoMap);
}
} catch (Exception ex) {
StringBuilder message = new StringBuilder();
message.append("VNX File getNFSExportsForPath failed for Data Mover" + dmName);
message.append(", due to {}");
_log.error(message.toString(), ex);
}
return pathExportMap;
}
/**
* Gets the fs export info.
*
* @param dmName the dm name
* @param path the path
* @return the fs export info
*/
public Map<String, String> getFsExportInfo(String dmName, String path) {
Map<String, String> fsExportInfoMap = new ConcurrentHashMap<String, String>();
try {
// Prepare arguments for CLI command
StringBuilder data = new StringBuilder();
data.append(dmName);
data.append(" -list ");
data.append(path);
// Execute command
XMLApiResult result = this.executeSshRetry(VNXFileSshApi.SERVER_EXPORT_CMD, data.toString());
// Parse message to get export properties
String[] propList = result.getMessage().split("[ \n]");
for (String prop : propList) {
String[] tempStr = prop.split("=");
if (tempStr.length > 1) {
String val = fsExportInfoMap.get(tempStr[0]);
if (val == null) {
fsExportInfoMap.put(tempStr[0], tempStr[1]);
} else {
fsExportInfoMap.put(tempStr[0], val + ":" + tempStr[1]);
}
}
}
} catch (Exception ex) {
StringBuilder message = new StringBuilder();
message.append("VNX File getFsExportInfo failed for Data Mover" + dmName);
message.append(", path " + path);
message.append(", due to {}");
_log.error(message.toString(), ex);
}
return fsExportInfoMap;
}
/**
* Gets the CIFS exports for path.
*
* @param dmName the dm name
* @return the CIFS exports for path
*/
public Map<String, Map<String, String>> getCIFSExportsForPath(String dmName) {
Map<String, Map<String, String>> pathExportMap = new ConcurrentHashMap<String, Map<String, String>>();
try {
// Prepare arguments for CLI command
StringBuilder data = new StringBuilder();
data.append(dmName);
data.append(" -Protocol cifs ");
data.append(" -list ");
// Execute command
XMLApiResult result = this.executeSshRetry(VNXFileSshApi.SERVER_EXPORT_CMD, data.toString());
// Parse message to get export properties
String[] propList = result.getMessage().split("[\n]");
if (propList == null || propList.length < 1) {
// no exports found
return pathExportMap;
}
for (int i = 1; i < propList.length; i++) {
String exp = propList[i];
Map<String, String> fsExportInfoMap = new ConcurrentHashMap<String, String>();
String expPath = "";
String[] expList = exp.split("[ \n]");
// loose the double quotes from either ends
// For CIFS exports - share path will be followed by share name
if (expList[0].equalsIgnoreCase(SHARE)) {
expPath = expList[2].substring(1, expList[2].length() - 1);
String shareName = expList[1].substring(1, expList[1].length() - 1);
fsExportInfoMap.put(SHARE, shareName);
} else {
continue;
}
for (String prop : expList) {
String[] tempStr = prop.split("=");
if (tempStr.length > 1) {
String val = fsExportInfoMap.get(tempStr[0]);
if (val == null) {
fsExportInfoMap.put(tempStr[0], tempStr[1]);
} else {
fsExportInfoMap.put(tempStr[0], val + ":" + tempStr[1]);
}
}
}
pathExportMap.put(expPath, fsExportInfoMap);
}
} catch (Exception ex) {
StringBuilder message = new StringBuilder();
message.append("VNX File getNFSExportsForPath failed for Data Mover" + dmName);
message.append(", due to {}");
_log.error(message.toString(), ex);
}
return pathExportMap;
}
/**
* Gets the user info.
*
* @param dmName the dm name
* @return the user info
*/
public Map<String, String> getUserInfo(String dmName) {
Map<String, String> userInfo = new ConcurrentHashMap<String, String>();
try {
// Prepare arguments for CLI command
StringBuilder data = new StringBuilder();
data.append(dmName);
data.append(" -list ");
// Execute command
XMLApiResult result = this.executeSshRetry(VNXFileSshApi.SERVER_USER_CMD, data.toString());
if (result.isCommandSuccess()) {
// Parse message to get user properties
String[] propList = result.getMessage().split("[ \n]");
boolean firstRow = true;
for (String prop : propList) {
_log.info("User Info : " + prop);
if (firstRow) {
firstRow = false;
continue;
}
// <UserAccount uid="117" gid="217" comment="Bourne Testing" md5="false"
// desPasswordState="locked" user="user_rss" mover="2"/>
String[] tempStr = prop.split(":");
if (tempStr.length > 1) {
String userName = tempStr[0];
String uid = tempStr[2];
userInfo.put("user", userName);
userInfo.put("uid", uid);
_log.info("User {} uuid {}", userName, uid);
break;
}
}
}
} catch (Exception ex) {
StringBuilder message = new StringBuilder();
message.append("VNX File getUserInfo failed for Data Mover" + dmName);
message.append(", due to {}");
_log.error(message.toString(), ex);
}
return userInfo;
}
/**
* Gets the model info.
*
* @return the model info
*/
public String getModelInfo() {
String modelStr = new String("VNX7500");
try {
// Prepare arguments for CLI command
StringBuilder data = new StringBuilder();
data.append("");
// Execute command
XMLApiResult result = this.executeSshRetry(VNXFileSshApi.SERVER_USER_CMD, data.toString());
if (result.isCommandSuccess()) {
// Parse message to get user properties
String tmpModelStr = result.getMessage();
if (tmpModelStr != null && tmpModelStr.startsWith("VNX")) {
modelStr = tmpModelStr;
}
}
} catch (Exception ex) {
StringBuilder message = new StringBuilder();
message.append("VNX File getModel failed ");
message.append(", due to {}");
_log.error(message.toString(), ex);
}
return modelStr;
}
// nas_fs -name testSAPCliThinFS -type uxfs -create size=100M pool="clarsas_archive" -auto_extend yes -thin yes
// -hwm 90% -max_size 10G
/**
* Format create fs.
*
* @param name the name
* @param type the type
* @param initialSizeInMB the initial size in mb
* @param finalSizeInMB the final size in mb
* @param pool the pool
* @param desc the desc
* @param thin the thin
* @param id the id
* @return the string
*/
// nas_fs -name testSAPCliThickFS -type uxfs -create size=100M pool="clarsas_archive" -auto_extend no -thin no
public String formatCreateFS(String name, String type, String initialSizeInMB, String finalSizeInMB,
String pool, String desc, boolean thin, String id) {
StringBuilder cmd = new StringBuilder();
cmd.append(" -name ");
cmd.append(name);
cmd.append(" -type ");
cmd.append(type);
cmd.append(" -create ");
pool = "'" + pool + "'";
if (thin) {
cmd.append(" size=" + initialSizeInMB + "M"); // Defaut passed in MB
cmd.append(" pool=" + pool);
cmd.append(" -thin ");
cmd.append("yes");
cmd.append(" -auto_extend ");
cmd.append("yes");
cmd.append(" -hwm ");
cmd.append("90%");
cmd.append(" -max_size ");
cmd.append(finalSizeInMB + "M");// Defaut passed in MB
} else {
cmd.append(" size=" + finalSizeInMB + "M"); // Defaut passed in MB
cmd.append(" pool=" + pool);
cmd.append(" -thin ");
cmd.append("no");
cmd.append(" -auto_extend ");
cmd.append("no");
}
// only if id is not null and not empty
if (id != null && !(id.isEmpty())) {
cmd.append(" -o ");
cmd.append("id=" + id);
}
// added for eNAS
cmd.append(" -option ");
cmd.append(" slice=y ");
cmd.append(" log_type=split ");
return cmd.toString();
// nas_fs -name sebastian_test -type uxfs -create size=10M pool=tsi_pool1
// log_type=common fast_clone_level=1 -auto_extend yes -thin yes -max_size 100M -o id=132
}
/**
* Gets the FS size info.
*
* @param fsName the fs name
* @return the FS size info
*/
// nas_fs -size ThinFS
public String getFSSizeInfo(String fsName) {
String fsSize = "0";
String[] fsInfoSizeList;
try {
// Prepare arguments for CLI command
StringBuilder cmd = new StringBuilder();
cmd.append(" -size ");
cmd.append(fsName);
// Execute command
XMLApiResult fsInfoResult = this.executeSshRetry(VNXFileSshApi.NAS_FS,
cmd.toString());
// Parse message to get file system size properties
if (fsInfoResult.isCommandSuccess()) {
String[] propList = fsInfoResult.getMessage().split("[\n]");
if (propList == null || propList.length < 1) {
// no FS Size found
return fsSize;
} else {
for (String prop : propList) {
if (fsInfoResult.getMessage().contains("volume:")) {
if (prop.startsWith("volume:")) {
fsInfoSizeList = prop.split(" ");
fsSize = fsInfoSizeList[3];
break;
} else {
continue;
}
} else {
fsInfoSizeList = prop.split(" ");
fsSize = fsInfoSizeList[2];
break;
}
}
}
}
} catch (Exception ex) {
StringBuilder message = new StringBuilder();
message.append("VNX File getFsSizeInfo failed for file system"
+ fsName);
message.append(", due to {}");
_log.error(message.toString(), ex);
}
return fsSize;
}
/**
* Execute ssh retry.
*
* @param command the command
* @param request the request
* @return the XML api result
*/
// Retry executeSsh Command
public XMLApiResult executeSshRetry(String command, String request) {
XMLApiResult reTryResult = new XMLApiResult();
try {
int maxRetry = 3;
while (maxRetry > 0) {
// Execute command
reTryResult = this.executeSsh(command, request);
String message = reTryResult.getMessage();
if (reTryResult.isCommandSuccess()) {
// reTryResult successful
break;
} else if (message != null
&& !message.isEmpty()
&& (message.contains("unable to acquire lock(s)") ||
message.contains("NAS_DB locked object is stale") ||
message.contains("Temporarily no Data Mover is available"))) {
try {
// Delaying execution since NAS_DB object is locked till
// current execution complete
TimeUnit.SECONDS.sleep(10);
} catch (InterruptedException e) {
_log.error(
"Exception occurred while delaying file system creation command due to {}",
e);
}
maxRetry--;
} else {
// reTryResult failed
break;
}
}
} catch (Exception ex) {
StringBuilder message = new StringBuilder();
message.append("VNX File executeSshReTry failed for create file system");
message.append(", due to {}");
_log.error(message.toString(), ex);
}
return reTryResult;
}
}