package com.sohu.cache.ssh; import static com.sohu.cache.constant.BaseConstant.WORD_SEPARATOR; import static com.sohu.cache.constant.EmptyObjectConstant.EMPTY_STRING; import static com.sohu.cache.constant.SymbolConstant.COMMA; import java.text.DecimalFormat; import java.util.HashMap; import java.util.Map; import java.util.regex.Matcher; import java.util.regex.Pattern; import org.apache.commons.lang.StringUtils; import org.apache.commons.lang.math.NumberUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.sohu.cache.entity.MachineStats; import com.sohu.cache.exception.IllegalParamException; import com.sohu.cache.exception.SSHException; import com.sohu.cache.ssh.SSHTemplate.DefaultLineProcessor; import com.sohu.cache.ssh.SSHTemplate.LineProcessor; import com.sohu.cache.ssh.SSHTemplate.Result; import com.sohu.cache.ssh.SSHTemplate.SSHCallback; import com.sohu.cache.ssh.SSHTemplate.SSHSession; import com.sohu.cache.util.ConstUtils; import com.sohu.cache.util.IntegerUtil; import com.sohu.cache.util.StringUtil; /** * Created by yijunzhang on 14-6-20. */ public class SSHUtil { private static final Logger logger = LoggerFactory.getLogger(SSHUtil.class); private final static String COMMAND_TOP = "top -b -n 1 | head -5"; private final static String COMMAND_DF_LH = "df -lh"; private final static String LOAD_AVERAGE_STRING = "load average: "; private final static String COMMAND_MEM = "cat /proc/meminfo | grep -E -w 'MemTotal|MemFree|Buffers|Cached'"; private final static String MEM_TOTAL = "MemTotal"; private final static String MEM_FREE = "MemFree"; private final static String MEM_BUFFERS = "Buffers"; private final static String MEM_CACHED = "Cached"; //使用 @SSHTemplate 重构SSHUtil private final static SSHTemplate sshTemplate = new SSHTemplate(); /** * Get HostPerformanceEntity[cpuUsage, memUsage, load] by ssh.<br> * 方法返回前已经释放了所有资源,调用方不需要关心 * * @param ip * @param userName * @param password * @throws Exception * @since 1.0.0 */ public static MachineStats getMachineInfo(String ip, int port, String userName, String password) throws SSHException { if (StringUtil.isBlank(ip)) { try { throw new IllegalParamException("Param ip is empty!"); } catch (IllegalParamException e) { throw new SSHException(e.getMessage(), e); } } port = IntegerUtil.defaultIfSmallerThan0(port, ConstUtils.SSH_PORT_DEFAULT); final MachineStats systemPerformanceEntity = new MachineStats(); systemPerformanceEntity.setIp(ip); sshTemplate.execute(ip, port, userName, password, new SSHCallback() { public Result call(SSHSession session) { //解析top命令 session.executeCommand(COMMAND_TOP, new DefaultLineProcessor() { public void process(String line, int lineNum) throws Exception { if (lineNum > 5) { return; } if (1 == lineNum) { // 第一行,通常是这样: // top - 19:58:52 up 416 days, 30 min, 1 user, load average: // 0.00, 0.00, 0.00 int loadAverageIndex = line.indexOf(LOAD_AVERAGE_STRING); String loadAverages = line.substring(loadAverageIndex).replace(LOAD_AVERAGE_STRING, EMPTY_STRING); String[] loadAverageArray = loadAverages.split(","); if (3 == loadAverageArray.length) { systemPerformanceEntity.setLoad(StringUtil.trimToEmpty(loadAverageArray[0])); } } else if (3 == lineNum) { // 第三行通常是这样: // , 0.0% sy, 0.0% ni, 100.0% id, 0.0% wa, // 0.0% hi, 0.0% si // redhat:%Cpu(s): 0.0 us // centos7:Cpu(s): 0.0% us double cpuUs = getUsCpu(line); systemPerformanceEntity.setCpuUsage(String.valueOf(cpuUs)); } } }); //解析memory session.executeCommand(COMMAND_MEM, new LineProcessor() { private String totalMem; private String freeMem; private String buffersMem; private String cachedMem; public void process(String line, int lineNum) throws Exception { if (line.contains(MEM_TOTAL)) { totalMem = matchMemLineNumber(line).trim(); } else if (line.contains(MEM_FREE)) { freeMem = matchMemLineNumber(line).trim(); } else if (line.contains(MEM_BUFFERS)) { buffersMem = matchMemLineNumber(line).trim(); } else if (line.contains(MEM_CACHED)) { cachedMem = matchMemLineNumber(line).trim(); } } public void finish() { if (!StringUtil.isBlank(totalMem, freeMem, buffersMem)) { Long totalMemLong = NumberUtils.toLong(totalMem); Long freeMemLong = NumberUtils.toLong(freeMem); Long buffersMemLong = NumberUtils.toLong(buffersMem); Long cachedMemLong = NumberUtils.toLong(cachedMem); Long usedMemFree = freeMemLong + buffersMemLong + cachedMemLong; Double memoryUsage = 1 - (NumberUtils.toDouble(usedMemFree.toString()) / NumberUtils.toDouble(totalMemLong.toString()) / 1.0); systemPerformanceEntity.setMemoryTotal(String.valueOf(totalMemLong)); systemPerformanceEntity.setMemoryFree(String.valueOf(usedMemFree)); DecimalFormat df = new DecimalFormat("0.00"); systemPerformanceEntity.setMemoryUsageRatio(df.format(memoryUsage * 100)); } } }); // 统计磁盘使用状况 /** * 内容通常是这样: Filesystem 容量 已用 可用 已用% 挂载点 /dev/xvda2 5.8G 3.2G 2.4G * 57% / /dev/xvda1 99M 8.0M 86M 9% /boot none 769M 0 769M 0% * /dev/shm /dev/xvda7 68G 7.1G 57G 12% /home /dev/xvda6 2.0G 36M * 1.8G 2% /tmp /dev/xvda5 2.0G 199M 1.7G 11% /var * */ session.executeCommand(COMMAND_DF_LH, new LineProcessor() { private Map<String, String> diskUsageMap = new HashMap<String, String>(); public void process(String line, int lineNum) throws Exception { if(lineNum == 1) { return; } line = line.replaceAll(" {1,}", WORD_SEPARATOR); String[] lineArray = line.split(WORD_SEPARATOR); if (6 == lineArray.length) { String diskUsage = lineArray[4]; String mountedOn = lineArray[5]; diskUsageMap.put(mountedOn, diskUsage); } } public void finish() { systemPerformanceEntity.setDiskUsageMap(diskUsageMap); } }); return null; } }); // 统计当前网络流量 @TODO Double traffic = 0.0; systemPerformanceEntity.setTraffic(traffic.toString()); return systemPerformanceEntity; } /** * SSH 方式登录远程主机,执行命令,方法内部会关闭所有资源,调用方无须关心。 * * @param ip 主机ip * @param username 用户名 * @param password 密码 * @param command 要执行的命令 */ public static String execute(String ip, int port, String username, String password, final String command) throws SSHException { if (StringUtil.isBlank(command)) { return EMPTY_STRING; } port = IntegerUtil.defaultIfSmallerThan0(port, ConstUtils.SSH_PORT_DEFAULT); Result rst = sshTemplate.execute(ip, port, username, password, new SSHCallback() { public Result call(SSHSession session) { return session.executeCommand(command); } }); if(rst.isSuccess()) { return rst.getResult(); } return ""; } /** * @param ip * @param port * @param username * @param password * @param localPath * @param remoteDir * @return * @throws SSHException */ public static boolean scpFileToRemote(String ip, int port, String username, String password, final String localPath, final String remoteDir) throws SSHException{ Result rst = sshTemplate.execute(ip, port, username, password, new SSHCallback() { public Result call(SSHSession session) { return session.scpToDir(localPath, remoteDir, "0644"); } }); if(rst.isSuccess()) { return true; } if(rst.getExcetion() != null) { throw new SSHException(rst.getExcetion()); } return false; } /** * 重载,使用默认端口、用户名和密码 * * @param ip * @param localPath * @param remoteDir * @return * @throws SSHException */ public static boolean scpFileToRemote(String ip, String localPath, String remoteDir) throws SSHException { int sshPort = SSHUtil.getSshPort(ip); return scpFileToRemote(ip, sshPort, ConstUtils.USERNAME, ConstUtils.PASSWORD, localPath, remoteDir); } /** * 重载,使用默认端口、用户名和密码 * * @param ip * @param cmd * @return * @throws SSHException */ public static String execute(String ip, String cmd) throws SSHException { int sshPort = SSHUtil.getSshPort(ip); return execute(ip, sshPort, ConstUtils.USERNAME, ConstUtils.PASSWORD, cmd); } /** * 查看机器ip上的端口port是否已被占用; * * @param ip 机器ip * @param port 要检查的端口 * @return 如果被占用返回true,否则返回false; * @throws SSHException */ public static boolean isPortUsed(String ip, int port) throws SSHException { /** * 执行ps命令,查看端口,以确认刚才执行的shell命令是否成功,返回一般是这样的: * root 12510 12368 0 14:34 pts/0 00:00:00 redis-server *:6379 */ String psCmd = "/bin/ps -ef | grep %s | grep -v grep"; psCmd = String.format(psCmd, port); String psResponse = execute(ip, psCmd); boolean isUsed = false; if (StringUtils.isNotBlank(psResponse)) { String[] resultArr = psResponse.split(System.lineSeparator()); for (String resultLine: resultArr) { if (resultLine.contains(String.valueOf(port))) { isUsed = true; break; } } } return isUsed; } /** * 通过ip来判断ssh端口 * * @param ip * @return */ public static int getSshPort(String ip) { /** * 如果ssh默认端口不是22,请自行实现该逻辑 */ return ConstUtils.SSH_PORT_DEFAULT; } /** * 匹配字符串中的数字 * * @param content * @return */ private static String matchMemLineNumber(String content) { String result = EMPTY_STRING; if (content == null || EMPTY_STRING.equals(content.trim())) { return result; } Pattern pattern = Pattern.compile("(\\d+)"); Matcher matcher = pattern.matcher(content); if (matcher.find()) { result = matcher.group(1); } return result; } /** * 从top的cpuLine解析出us * @param cpuLine * @return */ public static double getUsCpu(String cpuLine) { if (cpuLine == null || EMPTY_STRING.equals(cpuLine.trim())) { return 0; } String[] items = cpuLine.split(COMMA); if (items.length < 1) { return 0; } String usCpuStr = items[0]; return NumberUtils.toDouble(matchCpuLine(usCpuStr)); } private static String matchCpuLine(String content) { String result = EMPTY_STRING; if (content == null || EMPTY_STRING.equals(content.trim())) { return result; } Pattern pattern = Pattern.compile("(\\d+).(\\d+)"); Matcher matcher = pattern.matcher(content); if (matcher.find()) { result = matcher.group(); } return result; } }