package com.sohu.cache.machine;

import com.sohu.cache.constant.EmptyObjectConstant;
import com.sohu.cache.constant.SymbolConstant;
import com.sohu.cache.exception.SSHException;
import com.sohu.cache.ssh.SSHUtil;
import com.sohu.cache.util.ConstUtils;
import com.google.common.util.concurrent.AtomicLongMap;

import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.math.NumberUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * 生成一个redis可用端口
 *
 * @author: lingguo
 * @time: 2014/8/25 20:57
 */
public class PortGenerator {
    private static Logger logger = LoggerFactory.getLogger(PortGenerator.class);
    /**
     * redis port常量
     */
    private static final Integer REDIS_START_PORT = 6379;
    private static AtomicLongMap<String> redisPortHolder = AtomicLongMap.create();

    /**
     * 返回一个redis的可用端口：
     *  - 1. 通过shell查询redis当前已用的最大port；
     *  - 2. 为什么同步：防止多线程访问时获取到同样的端口；
     *  - 3. 为什么还用原子计数：连续两次调用时，如果进程还没启动，则拿到的仍然是相同的端口；
     *
     * @param ip
     * @return
     */
    public static synchronized Integer getRedisPort(final String ip) {
        if (redisPortHolder.get(ip) == 0L) {
            redisPortHolder.put(ip, REDIS_START_PORT);
        }
        String maxPortStr = "";
        try {
            int sshPort = SSHUtil.getSshPort(ip);
            maxPortStr = getMaxPortStr(ip, sshPort);
        } catch (SSHException e) {
            logger.error("cannot get max port of redis by ssh, ip: {}", ip, e);
        }

        if (StringUtils.isBlank(maxPortStr) || !StringUtils.isNumeric(maxPortStr)) {
            logger.warn("the max port of redis is invalid, maxPortStr: {}", maxPortStr);
            return new Long(redisPortHolder.getAndIncrement(ip)).intValue();
        }

        int availablePort = Integer.valueOf(maxPortStr) + 1;
        // 兼容连续调用的情况
        if (availablePort < redisPortHolder.get(ip)) {
            availablePort = new Long(redisPortHolder.getAndIncrement(ip)).intValue();
        } else {    // 正常情况，以及兼容系统重启和当前端口不可用的情形
            redisPortHolder.put(ip, availablePort + 1);
        }

        try {
            while (SSHUtil.isPortUsed(ip, availablePort)) {
                availablePort++;
            }
        } catch (SSHException e) {
            logger.error("check port error, ip: {}, port: {}", ip, availablePort, e);
        }
        redisPortHolder.put(ip, availablePort+1);
        return availablePort;
    }

    @Deprecated
    public static String getMaxPortStrOld(String ip, int sshPort) throws SSHException {
        String redisPidCmd = "ps -ef | grep redis | grep -v 'grep' |  awk -F '*:' '{print $2}' " +
                " | awk -F ' ' '{print $1}' | sort -r | head -1";
        return SSHUtil.execute(ip, sshPort, ConstUtils.USERNAME, ConstUtils.PASSWORD, redisPidCmd);
    }
    
    /**
     * 直接解析ps -ef | grep redis | grep -v 'grep'
     * @param ip
     * @param sshPort
     * @return
     * @throws SSHException
     */
     public static String getMaxPortStr(String ip, int sshPort) throws SSHException {
        String redisPidCmd = "ps -ef | grep redis | grep -v 'grep'";
        String redisProcessStr = SSHUtil.execute(ip, sshPort, ConstUtils.USERNAME, ConstUtils.PASSWORD, redisPidCmd);
        if (StringUtils.isBlank(redisProcessStr)) {
            return EmptyObjectConstant.EMPTY_STRING;
        }
        int maxPort = 0;
        String[] lines = redisProcessStr.split(SymbolConstant.ENTER);
        for (String line : lines) {
            if (StringUtils.isBlank(line)) {
                continue;
            }
            int redisServerIndex = line.indexOf("redis-server");
            int redisSentinelIndex = line.indexOf("redis-sentinel");
            if (redisServerIndex >= 0) {
                line = line.substring(redisServerIndex);
            }
            if (redisSentinelIndex >= 0) {
                line = line.substring(redisSentinelIndex);
            }
            if (redisServerIndex < 0 && redisSentinelIndex < 0) {
                continue;
            }
            String[] items = line.split(SymbolConstant.SPACE);
            if (items.length >= 2) {
                String hostPort = items[1];
                if (StringUtils.isBlank(hostPort)) {
                    continue;
                }
                String[] hostPortArr = hostPort.split(SymbolConstant.COLON);
                if (hostPortArr.length != 2) {
                    continue;
                }
                String portStr = hostPortArr[1];
                if (!NumberUtils.isDigits(portStr)) {
                    continue;
                }
                int port = NumberUtils.toInt(portStr);
                if (port > maxPort) {
                    maxPort = port;
                }
            }
        }
        return maxPort == 0 ? EmptyObjectConstant.EMPTY_STRING : String.valueOf(maxPort);
    }

}

@@ -1640,16 +1640,19 @@
 r.warn(%22
+%7B%7D 
 the max 
@@ -1693,16 +1693,20 @@
 tr: %7B%7D%22,
+ ip,
  maxPort

add ip in portgen log