content: package com.sohu.cache.web.controller;
  
  
  import java.util.ArrayList;
  import java.util.Arrays;
  import java.util.HashSet;
  import java.util.List;
  import java.util.Map;
  import java.util.Random;
  import java.util.Set;
  
  import javax.annotation.Resource;
  import javax.servlet.http.HttpServletRequest;
  import javax.servlet.http.HttpServletResponse;
  
  import org.apache.commons.collections.CollectionUtils;
  import org.apache.commons.lang.StringUtils;
  import org.apache.commons.lang.math.NumberUtils;
  import org.springframework.stereotype.Controller;
  import org.springframework.ui.Model;
  import org.springframework.web.bind.annotation.RequestMapping;
  import org.springframework.web.servlet.ModelAndView;
  
  import com.sohu.cache.constant.CommandResult;
  import com.sohu.cache.constant.MachineInfoEnum;
  import com.sohu.cache.constant.AppDataMigrateEnum;
  import com.sohu.cache.constant.AppDataMigrateResult;
  import com.sohu.cache.constant.RedisMigrateToolConstant;
  import com.sohu.cache.entity.AppDataMigrateSearch;
  import com.sohu.cache.entity.AppDataMigrateStatus;
  import com.sohu.cache.entity.AppDesc;
  import com.sohu.cache.entity.AppUser;
  import com.sohu.cache.entity.InstanceInfo;
  import com.sohu.cache.entity.MachineInfo;
  import com.sohu.cache.machine.MachineCenter;
  import com.sohu.cache.redis.RedisCenter;
  import com.sohu.cache.stats.app.AppDataMigrateCenter;
  import com.sohu.cache.util.ConstUtils;
  import com.sohu.cache.util.TypeUtil;
  import com.sohu.cache.web.service.AppService;
  
  /**
   * 应用数据迁移入口
   * 
   * @author leifu
   * @Date 2016-6-8
   * @Time 下午11:10:34
   */
  @Controller
  @RequestMapping("/data/migrate")
  public class AppDataMigrateController extends BaseController {
  
      @Resource(name = "appDataMigrateCenter")
      private AppDataMigrateCenter appDataMigrateCenter;
      
      @Resource(name = "appService")
      private AppService appService;
      
      @Resource(name = "machineCenter")
      private MachineCenter machineCenter;
      
      @Resource(name = "redisCenter")
      private RedisCenter redisCenter;
      
      private static Set<String> MIGRATE_SAMPLE_USEFUL_LINES = new HashSet<String>();
      static {
          MIGRATE_SAMPLE_USEFUL_LINES.add("Checked keys");
          MIGRATE_SAMPLE_USEFUL_LINES.add("Inconsistent value keys");
          MIGRATE_SAMPLE_USEFUL_LINES.add("Inconsistent expire keys");
          MIGRATE_SAMPLE_USEFUL_LINES.add("Other check error keys");
          MIGRATE_SAMPLE_USEFUL_LINES.add("Checked OK keys");
      }
      
  
      /**
       * 初始化界面
       * @return
       */
      @RequestMapping(value = "/init")
      public ModelAndView init(HttpServletRequest request, HttpServletResponse response, Model model) {
          List<MachineInfo> machineInfoList = machineCenter.getMachineInfoByType(MachineInfoEnum.TypeEnum.REDIS_MIGRATE_TOOL);
          model.addAttribute("machineInfoList", machineInfoList);
          return new ModelAndView("migrate/init");
      }
  
      /**
       * 检查配置
       * @return
       */
      @RequestMapping(value = "/check")
      public ModelAndView check(HttpServletRequest request, HttpServletResponse response, Model model) {
          //相关参数
          String migrateMachineIp = request.getParameter("migrateMachineIp");
          String sourceRedisMigrateIndex = request.getParameter("sourceRedisMigrateIndex");
          AppDataMigrateEnum sourceRedisMigrateEnum = AppDataMigrateEnum.getByIndex(NumberUtils.toInt(sourceRedisMigrateIndex, -1));
          String sourceServers = request.getParameter("sourceServers");
          String targetRedisMigrateIndex = request.getParameter("targetRedisMigrateIndex");
          AppDataMigrateEnum targetRedisMigrateEnum = AppDataMigrateEnum.getByIndex(NumberUtils.toInt(targetRedisMigrateIndex, -1));
          String targetServers = request.getParameter("targetServers");
          String redisSourcePass = request.getParameter("redisSourcePass");
          String redisTargetPass = request.getParameter("redisTargetPass");
  
          //检查返回结果
          AppDataMigrateResult redisMigrateResult = appDataMigrateCenter.check(migrateMachineIp, sourceRedisMigrateEnum, sourceServers, targetRedisMigrateEnum, targetServers, redisSourcePass, redisTargetPass);
          model.addAttribute("status", redisMigrateResult.getStatus());
          model.addAttribute("message", redisMigrateResult.getMessage());
          return new ModelAndView("");
      }
  
      /**
       * 开始迁移
       * @return
       */
      @RequestMapping(value = "/start")
      public ModelAndView start(HttpServletRequest request, HttpServletResponse response, Model model) {
          //相关参数
          String migrateMachineIp = request.getParameter("migrateMachineIp");
          String sourceRedisMigrateIndex = request.getParameter("sourceRedisMigrateIndex");
          AppDataMigrateEnum sourceRedisMigrateEnum = AppDataMigrateEnum.getByIndex(NumberUtils.toInt(sourceRedisMigrateIndex, -1));
          String sourceServers = request.getParameter("sourceServers");
          String targetRedisMigrateIndex = request.getParameter("targetRedisMigrateIndex");
          AppDataMigrateEnum targetRedisMigrateEnum = AppDataMigrateEnum.getByIndex(NumberUtils.toInt(targetRedisMigrateIndex, -1));
          String targetServers = request.getParameter("targetServers");
          long sourceAppId = NumberUtils.toLong(request.getParameter("sourceAppId"));
          long targetAppId = NumberUtils.toLong(request.getParameter("targetAppId"));
          String redisSourcePass = request.getParameter("redisSourcePass");
          String redisTargetPass = request.getParameter("redisTargetPass");
  
          AppUser appUser = getUserInfo(request);
          long userId = appUser == null ? 0 : appUser.getId();
  
          // 不需要对格式进行检验,check已经做过了，开始迁移
          boolean isSuccess = appDataMigrateCenter.migrate(migrateMachineIp, sourceRedisMigrateEnum, sourceServers,
                  targetRedisMigrateEnum, targetServers, sourceAppId, targetAppId, redisSourcePass, redisTargetPass, userId);
  
          model.addAttribute("status", isSuccess ? 1 : 0);
          return new ModelAndView("");
      }
      
      /**
       * 停掉迁移任务
       * @return
       */
      @RequestMapping(value = "/stop")
      public ModelAndView stop(HttpServletRequest request, HttpServletResponse response, Model model) {
          //任务id：查到任务相关信息
          long id = NumberUtils.toLong(request.getParameter("id"));
          AppDataMigrateResult stopMigrateResult = appDataMigrateCenter.stopMigrate(id);
          model.addAttribute("status", stopMigrateResult.getStatus());
          model.addAttribute("message", stopMigrateResult.getMessage());
          return new ModelAndView("");
      }
      
      /**
       * 查看迁移日志
       * @return
       */
      @RequestMapping(value = "/log")
      public ModelAndView log(HttpServletRequest request, HttpServletResponse response, Model model) {
          //任务id：查到任务相关信息
          long id = NumberUtils.toLong(request.getParameter("id"));
          int pageSize = NumberUtils.toInt(request.getParameter("pageSize"), 0);
          if (pageSize == 0) {
              pageSize = 100;
          }
          
          String log = appDataMigrateCenter.showDataMigrateLog(id, pageSize);
          model.addAttribute("logList", Arrays.asList(log.split(ConstUtils.NEXT_LINE)));
          return new ModelAndView("migrate/log");
      }
      
      /**
       * 查看迁移日志
       * @return
       */
      @RequestMapping(value = "/config")
      public ModelAndView config(HttpServletRequest request, HttpServletResponse response, Model model) {
          //任务id：查到任务相关信息
          long id = NumberUtils.toLong(request.getParameter("id"));
          String config = appDataMigrateCenter.showDataMigrateConf(id);
          model.addAttribute("configList", Arrays.asList(config.split(ConstUtils.NEXT_LINE)));
          return new ModelAndView("migrate/config");
      }
      
      /**
       * 查看迁移进度
       * @return
       */
      @RequestMapping(value = "/process")
      public ModelAndView showProcess(HttpServletRequest request, HttpServletResponse response, Model model) {
          long id = NumberUtils.toLong(request.getParameter("id"));
          Map<RedisMigrateToolConstant, Map<String, Object>> migrateToolStatMap = appDataMigrateCenter.showMiragteToolProcess(id);
          model.addAttribute("migrateToolStatMap", migrateToolStatMap);
          return new ModelAndView("migrate/process");
      }
      
      /**
       * 查看迁移进度
       * @return
       */
      @RequestMapping(value = "/checkData")
      public ModelAndView checkData(HttpServletRequest request, HttpServletResponse response, Model model) {
          long id = NumberUtils.toLong(request.getParameter("id"));
          int nums = 1000 + new Random().nextInt(2000);
          //为了方便，直接传入命令
          CommandResult commandResult = appDataMigrateCenter.sampleCheckData(id, nums);
          String message = commandResult.getResult();
          List<String> checkDataResultList = new ArrayList<String>();
          checkDataResultList.add("一共随机检验了" + nums + "个key" + ",检查结果如下:");
          String[] lineArr = message.split(ConstUtils.NEXT_LINE);
          for (String line : lineArr) {
              if (StringUtils.isBlank(line)) {
                  continue;
              }
              // 行数太多显示会有问题
              if (lineArr.length > 100 && !isUsefulLine(line)) {
                  continue;
              }
              //message格式显示有点问题
              line = line.replace("[0m", "");
              line = line.replace("[31m", "");
              line = line.replace("[33m", "");
              checkDataResultList.add(line.trim());
          }
          model.addAttribute("checkDataResultList", checkDataResultList);
          model.addAttribute("checkDataCommand", commandResult.getCommand());
          return new ModelAndView("migrate/checkData");
      }
  
      private boolean isUsefulLine(String line) {
          for (String usefulLine : MIGRATE_SAMPLE_USEFUL_LINES) {
              if (line.contains(usefulLine)) {
                  return true;
              }
          }
          return false;
      }
  
      /**
       * 查看迁移列表(包含历史)
       * @return
       */
      @RequestMapping(value = "/list")
      public ModelAndView list(HttpServletRequest request, HttpServletResponse response, Model model, AppDataMigrateSearch appDataMigrateSearch) {
          List<AppDataMigrateStatus> appDataMigrateStatusList = appDataMigrateCenter.search(appDataMigrateSearch);
          model.addAttribute("appDataMigrateStatusList", appDataMigrateStatusList);
          model.addAttribute("appDataMigrateSearch", appDataMigrateSearch);
          return new ModelAndView("migrate/list");
      }
      
      /**
       * 通过应用id获取可用的Redis实例信息
       * @return
       */
      @RequestMapping(value = "/appInstanceList")
      public ModelAndView appInstanceList(HttpServletRequest request, HttpServletResponse response, Model model) {
          String appIdStr = request.getParameter("appId");
          long appId = NumberUtils.toLong(appIdStr);
          AppDesc appDesc = appService.getByAppId(appId);
          StringBuffer instances = new StringBuffer();
          List<InstanceInfo> instanceList = appService.getAppInstanceInfo(appId);
          if (CollectionUtils.isNotEmpty(instanceList)) {
              for (int i = 0; i < instanceList.size(); i++) {
                  InstanceInfo instanceInfo = instanceList.get(i);
                  if (instanceInfo == null) {
                      continue;
                  }
                  if (instanceInfo.isOffline()) {
                      continue;
                  }
                  // 如果是sentinel类型的应用只出master
                  if (TypeUtil.isRedisSentinel(appDesc.getType())) {
                      if (TypeUtil.isRedisSentinel(instanceInfo.getType())) {
                          continue;
                      }
                      if (!redisCenter.isMaster(appId, instanceInfo.getIp(), instanceInfo.getPort())) {
                          continue;
                      }
                  }
                  instances.append(instanceInfo.getIp() + ":" + instanceInfo.getPort());
                  if (i != instanceList.size() - 1) {
                      instances.append(ConstUtils.NEXT_LINE);
                  }
              }
          }
          model.addAttribute("instances", instances.toString());
          model.addAttribute("appType", appDesc == null ? -1 : appDesc.getType());
          return new ModelAndView("");
      }
      
  }
  