content: package com.sohu.cache.web.controller;
  
  import com.sohu.cache.web.enums.RedisOperateEnum;
  import com.sohu.cache.constant.AppCheckEnum;
  import com.sohu.cache.constant.ClusterOperateResult;
  import com.sohu.cache.constant.DataFormatCheckResult;
  import com.sohu.cache.constant.ErrorMessageEnum;
  import com.sohu.cache.constant.HorizontalResult;
  import com.sohu.cache.dao.InstanceReshardProcessDao;
  import com.sohu.cache.entity.*;
  import com.sohu.cache.machine.MachineCenter;
  import com.sohu.cache.redis.RedisCenter;
  import com.sohu.cache.redis.RedisDeployCenter;
  import com.sohu.cache.stats.app.AppDailyDataCenter;
  import com.sohu.cache.stats.app.AppDeployCenter;
  import com.sohu.cache.stats.instance.InstanceDeployCenter;
  import com.sohu.cache.util.ConstUtils;
  import com.sohu.cache.util.TypeUtil;
  import com.sohu.cache.web.enums.SuccessEnum;
  import com.sohu.cache.web.util.AppEmailUtil;
  import com.sohu.cache.web.util.DateUtil;
  
  import net.sf.json.JSONArray;
  
  import org.apache.commons.lang.StringUtils;
  import org.apache.commons.lang.math.NumberUtils;
  import org.apache.commons.lang.time.DateUtils;
  import org.slf4j.Logger;
  import org.slf4j.LoggerFactory;
  import org.springframework.stereotype.Controller;
  import org.springframework.ui.Model;
  import org.springframework.web.bind.annotation.RequestMapping;
  import org.springframework.web.servlet.ModelAndView;
  
  import javax.annotation.Resource;
  import javax.servlet.http.HttpServletRequest;
  import javax.servlet.http.HttpServletResponse;
  
  import java.text.ParseException;
  import java.util.*;
  
  /**
   * 应用后台管理
   *
   * @author leifu
   * @Time 2014年7月3日
   */
  @Controller
  @RequestMapping("manage/app")
  public class AppManageController extends BaseController {
  
  	private Logger logger = LoggerFactory.getLogger(AppManageController.class);
  
  	@Resource(name = "machineCenter")
  	private MachineCenter machineCenter;
  
  	@Resource(name = "appEmailUtil")
  	private AppEmailUtil appEmailUtil;
  
  	@Resource(name = "appDeployCenter")
  	private AppDeployCenter appDeployCenter;
  
  	@Resource(name = "redisCenter")
  	private RedisCenter redisCenter;
  
  	@Resource(name = "redisDeployCenter")
  	private RedisDeployCenter redisDeployCenter;
  	
  	@Resource(name = "instanceDeployCenter")
  	private InstanceDeployCenter instanceDeployCenter;
  
  	@Resource(name = "appDailyDataCenter")
      private AppDailyDataCenter appDailyDataCenter;
  	
      @Resource(name = "instanceReshardProcessDao")
  	private InstanceReshardProcessDao instanceReshardProcessDao;
  	
  	@RequestMapping("/appDaily")
      public ModelAndView appDaily(HttpServletRequest request, HttpServletResponse response, Model model) throws ParseException {
  	    AppUser userInfo = getUserInfo(request);
          logger.warn("user {} want to send appdaily", userInfo.getName());
          if (ConstUtils.SUPER_MANAGER.contains(userInfo.getName())) {
              Date startDate;
              Date endDate;
              String startDateParam = request.getParameter("startDate");
              String endDateParam = request.getParameter("endDate");
              if (StringUtils.isBlank(startDateParam) || StringUtils.isBlank(endDateParam)) {
                  endDate = new Date();
                  startDate = DateUtils.addDays(endDate, -1);
              } else {
                  startDate = DateUtil.parseYYYY_MM_dd(startDateParam);
                  endDate = DateUtil.parseYYYY_MM_dd(endDateParam);
              }
              long appId = NumberUtils.toLong(request.getParameter("appId"));
              if (appId > 0) {
                  appDailyDataCenter.sendAppDailyEmail(appId, startDate, endDate);
              } else {
                  appDailyDataCenter.sendAppDailyEmail();
              }
              model.addAttribute("msg", "success!");
          } else {
              model.addAttribute("msg", "no power!");
          }
          return new ModelAndView("");
      }
  	
  	/**
  	 * 审核列表
  	 * 
  	 * @param status 审核状态
  	 * @param type 申请类型
  	 */
  	@RequestMapping(value = "/auditList")
  	public ModelAndView doAppAuditList(HttpServletRequest request,HttpServletResponse response, Model model,
  	        Integer status, Integer type) {
  	    //获取审核列表
  		List<AppAudit> list = appService.getAppAudits(status, type);
  
  		model.addAttribute("list", list);
  		model.addAttribute("status", status);
  		model.addAttribute("type", type);
  		model.addAttribute("checkActive", SuccessEnum.SUCCESS.value());
  
  		return new ModelAndView("manage/appAudit/list");
  	}
  
  	/**
  	 * 处理应用配置修改
  	 * 
  	 * @param appAuditId 审批id
  	 */
  	@RequestMapping(value = "/initAppConfigChange")
  	public ModelAndView doInitAppConfigChange(HttpServletRequest request,
  			HttpServletResponse response, Model model, Long appAuditId) {
  		// 申请原因
  		AppAudit appAudit = appService.getAppAuditById(appAuditId);
  		model.addAttribute("appAudit", appAudit);
  
  		// 用第一个参数存实例id
  		Long instanceId = NumberUtils.toLong(appAudit.getParam1());
  		Map<String, String> redisConfigList = redisCenter.getRedisConfigList(instanceId.intValue());
  		model.addAttribute("redisConfigList", redisConfigList);
  		model.addAttribute("instanceId", instanceId);
  
  		// 实例列表
  		List<InstanceInfo> instanceList = appService.getAppInstanceInfo(appAudit.getAppId());
  		model.addAttribute("instanceList", instanceList);
  		model.addAttribute("appId", appAudit.getAppId());
  		model.addAttribute("appAuditId", appAuditId);
  
  		// 修改配置的键值对
  		model.addAttribute("appConfigKey", appAudit.getParam2());
  		model.addAttribute("appConfigValue", appAudit.getParam3());
  
  		return new ModelAndView("manage/appAudit/initAppConfigChange");
  	}
  
  	/**
  	 * 添加应用配置修改
  	 * 
  	 * @param appId 应用id
  	 * @param appConfigKey 配置项
  	 * @param appConfigValue 配置值
  	 * @param appAuditId 审批id
  	 */
  	@RequestMapping(value = "/addAppConfigChange")
  	public ModelAndView doAddAppConfigChange(HttpServletRequest request,
  			HttpServletResponse response, Model model, Long appId,
  			String appConfigKey, String appConfigValue, Long appAuditId) {
  	    AppUser appUser = getUserInfo(request);
          logger.warn("user {} change appConfig:appId={};key={};value={},appAuditId:{}", appUser.getName(), appId, appConfigKey, appConfigValue, appAuditId);
          boolean isModify = false;
          if (appId != null && appAuditId != null && StringUtils.isNotBlank(appConfigKey) && StringUtils.isNotBlank(appConfigValue)) {
  			try {
  				isModify = appDeployCenter.modifyAppConfig(appId, appAuditId, appConfigKey, appConfigValue);
  			} catch (Exception e) {
  				logger.error(e.getMessage(), e);
  			}
  		}
          logger.warn("user {} change appConfig:appId={};key={};value={},appAuditId:{},result is:{}", appUser.getName(), appId, appConfigKey, appConfigValue, appAuditId, isModify);
  		return new ModelAndView("redirect:/manage/app/auditList");
  	}
  
  	/**
  	 * 初始化水平扩容申请
  	 */
  	@RequestMapping(value = "/initHorizontalScaleApply")
  	public ModelAndView doInitHorizontalScaleApply(HttpServletRequest request, HttpServletResponse response, Model model, Long appAuditId) {
  		AppAudit appAudit = appService.getAppAuditById(appAuditId);
  		model.addAttribute("appAudit", appAudit);
  		model.addAttribute("appId", appAudit.getAppId());
  		return new ModelAndView("manage/appAudit/initHorizontalScaleApply");
  	}
  	
  	
  
      /**
       * 添加水平扩容节点
       * 
       * @return
       */
      @RequestMapping(value = "/addHorizontalNodes")
      public ModelAndView doAddHorizontalNodes(HttpServletRequest request,
              HttpServletResponse response, Model model, String masterSizeSlave,
              Long appAuditId) {
          AppUser appUser = getUserInfo(request);
          logger.warn("user {} addHorizontalNodes:{}", appUser.getName(), masterSizeSlave);
          boolean isAdd = false;
          AppAudit appAudit = appService.getAppAuditById(appAuditId);
          // 解析配置
          String[] configArr = masterSizeSlave.split(ConstUtils.COLON);
          String masterHost = configArr[0];
          String memSize = configArr[1];
          int memSizeInt = NumberUtils.toInt(memSize);
          String slaveHost = null;
          if (configArr.length >= 3) {
              slaveHost = configArr[2];
          }
          try {
              isAdd = appDeployCenter.addHorizontalNodes(appAudit.getAppId(), masterHost, slaveHost, memSizeInt);
          } catch (Exception e) {
              logger.error(e.getMessage(), e);
          }
          logger.warn("addAppClusterSharding:{}, result is {}", masterSizeSlave, isAdd);
          model.addAttribute("status", isAdd ? 1 : 0);
          return new ModelAndView("");
      }
  
      /**
       * 检测水平扩容节点
       * @param masterSizeSlave
       * @param appAuditId
       * @return
       */
  	@RequestMapping(value = "/checkHorizontalNodes")
  	public ModelAndView doCheckHorizontalNodes(HttpServletRequest request,
  			HttpServletResponse response, Model model, String masterSizeSlave,
  			Long appAuditId) {
  	    DataFormatCheckResult dataFormatCheckResult = null;
          try {
              dataFormatCheckResult = appDeployCenter.checkHorizontalNodes(appAuditId, masterSizeSlave);
          } catch (Exception e) {
              logger.error(e.getMessage(), e);
              dataFormatCheckResult = DataFormatCheckResult.fail(ErrorMessageEnum.INNER_ERROR_MSG.getMessage());
          }
          model.addAttribute("status", dataFormatCheckResult.getStatus());
          model.addAttribute("message", dataFormatCheckResult.getMessage());
          return new ModelAndView("");
  	}
  
  	/**
  	 * 水平扩容初始化
  	 * 
  	 * @param appAuditId
  	 */
  	@RequestMapping(value = "/handleHorizontalScale")
  	public ModelAndView doHandleHorizontalScale(HttpServletRequest request,
  			HttpServletResponse response, Model model, Long appAuditId) {
  		// 1. 审批
  		AppAudit appAudit = appService.getAppAuditById(appAuditId);
  		model.addAttribute("appAudit", appAudit);
  		model.addAttribute("appId", appAudit.getAppId());
  
  		// 2. 进度
  		List<InstanceReshardProcess> instanceReshardProcessList = instanceReshardProcessDao.getByAuditId(appAudit.getId());
  		model.addAttribute("instanceReshardProcessList", instanceReshardProcessList);
  
  		// 3. 实例列表和统计
  		fillAppInstanceStats(appAudit.getAppId(), model);
  		// 4. 实例所在机器信息
  		fillAppMachineStat(appAudit.getAppId(), model);
  
  		return new ModelAndView("manage/appAudit/handleHorizontalScale");
  	}
  
  	/**
  	 * 显示reshard进度
  	 */
  	@RequestMapping(value = "/showReshardProcess")
  	public ModelAndView doShowReshardProcess(HttpServletRequest request, HttpServletResponse response, Model model) {
  	    long auditId = NumberUtils.toLong(request.getParameter("auditId"));
          List<InstanceReshardProcess> instanceReshardProcessList = instanceReshardProcessDao.getByAuditId(auditId);
          write(response, JSONArray.fromObject(instanceReshardProcessList).toString());
          return null;
  	}
  
  //	/**
  //     * 把Map组装成JsonArray
  //     * 
  //     * @param appScaleProcessMap
  //     * @return
  //     */
  //    private String filterMapToJsonArray(ConcurrentMap<Long, ReshardProcess> appScaleProcessMap) {
  //        if (MapUtils.isEmpty(appScaleProcessMap)) {
  //            return "[]";
  //        }
  //        List<Map<String, Object>> list = new ArrayList<Map<String, Object>>();
  //        for (Entry<Long, ReshardProcess> entry : appScaleProcessMap.entrySet()) {
  //            Map<String, Object> map = new HashMap<String, Object>();
  //            map.put("appId", entry.getKey());
  //            map.put("reshardSlot", entry.getValue().getReshardSlot());
  //            map.put("totalSlot", entry.getValue().getTotalSlot());
  //            map.put("status", entry.getValue().getStatus());
  //            list.add(map);
  //        }
  //        return JSONArray.fromObject(list).toString();
  //    }
  
  	/**
  	 * 水平扩容配置检查
  	 * @param sourceId 源实例ID
  	 * @param targetId 目标实例ID
  	 * @param startSlot 开始slot
  	 * @param endSlot 结束slot
  	 * @param appId 应用id
  	 * @param appAuditId 审批id
  	 * @return
  	 */
  	@RequestMapping(value = "/checkHorizontalScale")
  	public ModelAndView doCheckHorizontalScale(HttpServletRequest request, HttpServletResponse response, Model model,
  			long sourceId, long targetId, int startSlot, int endSlot, long appId, long appAuditId, int migrateType) {
  		HorizontalResult horizontalResult = appDeployCenter.checkHorizontal(appId, appAuditId, sourceId, targetId,
  				startSlot, endSlot, migrateType);
  		model.addAttribute("status", horizontalResult.getStatus());
  		model.addAttribute("message", horizontalResult.getMessage());
  		return new ModelAndView("");
  	}
  	
  	/**
  	 * 开始水平扩容
  	 * @param sourceId 源实例ID
  	 * @param targetId 目标实例ID
  	 * @param startSlot 开始slot
  	 * @param endSlot 结束slot
  	 * @param appId 应用id
  	 * @param appAuditId 审批id
  	 * @return
  	 */
  	@RequestMapping(value = "/startHorizontalScale")
  	public ModelAndView doStartHorizontalScale(HttpServletRequest request, HttpServletResponse response, Model model,
  			long sourceId, long targetId, int startSlot, int endSlot, long appId, long appAuditId, int migrateType) {
  		AppUser appUser = getUserInfo(request);
  		logger.warn("user {} horizontalScaleApply appId {} appAuditId {} sourceId {} targetId {} startSlot {} endSlot {}",
  				appUser.getName(), appId, appAuditId, sourceId, targetId, startSlot, endSlot);
  		HorizontalResult horizontalResult = appDeployCenter.startHorizontal(appId, appAuditId, sourceId, targetId,
  				startSlot, endSlot, migrateType);
          model.addAttribute("status", horizontalResult.getStatus());
  		model.addAttribute("message", horizontalResult.getMessage());
  		return new ModelAndView("");
  	}
  	
  	/**
  	 * 重试水平扩容
  	 * @param instanceReshardProcessId
  	 * @return
  	 */
      @RequestMapping(value = "/retryHorizontalScale")
      public ModelAndView retryHorizontalScale(HttpServletRequest request, HttpServletResponse response, Model model, int instanceReshardProcessId) {
          AppUser appUser = getUserInfo(request);
          logger.warn("user {} retryHorizontalScale id {}", appUser.getName(), instanceReshardProcessId);
          HorizontalResult horizontalResult = appDeployCenter.retryHorizontal(instanceReshardProcessId);
          model.addAttribute("status", horizontalResult.getStatus());
          model.addAttribute("message", horizontalResult.getMessage());
          return new ModelAndView("");
      }
  
  	/**
  	 * 处理应用扩容
  	 * 
  	 * @param appAuditId 审批id
  	 */
  	@RequestMapping(value = "/initAppScaleApply")
  	public ModelAndView doInitAppScaleApply(HttpServletRequest request, HttpServletResponse response, Model model, Long appAuditId) {
  		// 申请原因
  		AppAudit appAudit = appService.getAppAuditById(appAuditId);
  		model.addAttribute("appAudit", appAudit);
  
  		// 实例列表和统计
  		fillAppInstanceStats(appAudit.getAppId(), model);
  		// 实例所在机器信息
          fillAppMachineStat(appAudit.getAppId(), model);
  
  		long appId = appAudit.getAppId();
  		AppDesc appDesc = appService.getByAppId(appId);
          model.addAttribute("appAuditId", appAuditId);
  		model.addAttribute("appId", appAudit.getAppId());
          model.addAttribute("appDesc", appDesc);
  		
  		return new ModelAndView("manage/appAudit/initAppScaleApply");
  	}
  
  	/**
  	 * 添加扩容配置
  	 * 
  	 * @param appScaleText 扩容配置
  	 * @param appAuditId 审批id
  	 */
  	@RequestMapping(value = "/addAppScaleApply")
  	public ModelAndView doAddAppScaleApply(HttpServletRequest request,
  			HttpServletResponse response, Model model, String appScaleText,
  			Long appAuditId, Long appId) {
  	    AppUser appUser = getUserInfo(request);
          logger.error("user {} appScaleApplay : appScaleText={},appAuditId:{}", appUser.getName(), appScaleText, appAuditId);
          boolean isSuccess = false;
  		if (appAuditId != null && StringUtils.isNotBlank(appScaleText)) {
  			int mem = NumberUtils.toInt(appScaleText, 0);
  			try {
  			    isSuccess = appDeployCenter.verticalExpansion(appId, appAuditId, mem);
  			} catch (Exception e) {
  				logger.error(e.getMessage(), e);
  			}
  		} else {
  			logger.error("appScaleApplay error param: appScaleText={},appAuditId:{}", appScaleText, appAuditId);
  		}
          logger.error("user {} appScaleApplay: appScaleText={},appAuditId:{}, result is {}", appUser.getName(), appScaleText, appAuditId, isSuccess);
  		return new ModelAndView("redirect:/manage/app/auditList");
  	}
  
  	/**
  	 * 初始化部署应用
  	 * 
  	 * @param appAuditId 审批id
  	 * @return
  	 */
  	@RequestMapping(value = "/initAppDeploy")
  	public ModelAndView doInitAppDeploy(HttpServletRequest request, HttpServletResponse response, Model model, Long appAuditId) {
  		// 申请原因
  		AppAudit appAudit = appService.getAppAuditById(appAuditId);
  		model.addAttribute("appAudit", appAudit);
  
  		// 机器列表
  		List<MachineStats> machineList = machineCenter.getAllMachineStats();
  		model.addAttribute("machineList", machineList);
  		model.addAttribute("appAuditId", appAuditId);
  		model.addAttribute("appId", appAudit.getAppId());
  		model.addAttribute("appDesc", appService.getByAppId(appAudit.getAppId()));
  
  		return new ModelAndView("manage/appAudit/initAppDeploy");
  	}
  	
  	/**
       * 应用部署配置检查
       * @return
       */
      @RequestMapping(value = "/appDeployCheck")
      public ModelAndView doAppDeployCheck(HttpServletRequest request, HttpServletResponse response, Model model, String appDeployText,
              Long appAuditId) {
          DataFormatCheckResult dataFormatCheckResult = null;
          try {
              dataFormatCheckResult = appDeployCenter.checkAppDeployDetail(appAuditId, appDeployText);
          } catch (Exception e) {
              logger.error(e.getMessage(), e);
              dataFormatCheckResult = DataFormatCheckResult.fail(ErrorMessageEnum.INNER_ERROR_MSG.getMessage());
          }
          model.addAttribute("status", dataFormatCheckResult.getStatus());
          model.addAttribute("message", dataFormatCheckResult.getMessage());
          return new ModelAndView("");
      }
  
  	/**
  	 * 添加应用部署
  	 * 
  	 * @param appDeployText 部署配置
  	 * @param appAuditId 审批id
  	 * @return
  	 */
  	@RequestMapping(value = "/addAppDeploy")
  	public ModelAndView doAddAppDeploy(HttpServletRequest request,
  			HttpServletResponse response, Model model, String appDeployText,
  			Long appAuditId) {
  	    AppUser appUser = getUserInfo(request);
          logger.warn("user {} appDeploy: appDeployText={},appAuditId:{}", appUser.getName(), appDeployText, appAuditId);
          boolean isSuccess = false;
  	    if (appAuditId != null && StringUtils.isNotBlank(appDeployText)) {
  			String[] appDetails = appDeployText.split("\n");
  			// 部署service
  			isSuccess = appDeployCenter.allocateResourceApp(appAuditId, Arrays.asList(appDetails), getUserInfo(request));
  		} else {
  			logger.error("appDeploy error param: appDeployText={},appAuditId:{}", appDeployText, appAuditId);
  		}
          logger.warn("user {} appDeploy: appDeployText={},appAuditId:{}, result is {}", appUser.getName(), appDeployText, appAuditId, isSuccess);
          model.addAttribute("status", isSuccess ? 1 : 0);
          return new ModelAndView("");
  	}
  
  	/**
  	 * 通过,获取驳回申请
  	 * 
  	 * @param status 审批状态
  	 * @param appAuditId 审批id
  	 * @param refuseReason 应用id
  	 * @return
  	 */
  	@RequestMapping(value = "/addAuditStatus")
  	public ModelAndView doAddAuditStatus(HttpServletRequest request, HttpServletResponse response, Model model, Integer status, Long appAuditId, String refuseReason) {
  	    AppUser appUser = getUserInfo(request);
          logger.warn("user {} addAuditStatus: status={},appAuditId:{},refuseReason:{}", appUser.getName(), status, appAuditId, refuseReason);
  	    AppAudit appAudit = appService.getAppAuditById(appAuditId);
  		Long appId = appAudit.getAppId();
  		// 通过或者驳回并记录日志
  		appService.updateAppAuditStatus(appAuditId, appId, status, getUserInfo(request));
  
  		// 记录驳回原因
  		if (AppCheckEnum.APP_REJECT.value().equals(status)) {
  			appAudit.setRefuseReason(refuseReason);
  			appService.updateRefuseReason(appAudit, getUserInfo(request));
  		}
  
  		// 发邮件统计
  		if (AppCheckEnum.APP_PASS.value().equals(status) || AppCheckEnum.APP_REJECT.value().equals(status)) {
  			AppDesc appDesc = appService.getByAppId(appId);
  			appEmailUtil.noticeAppResult(appDesc, appService.getAppAuditById(appAuditId));
  		}
  
  		// 批准成功直接跳转
  		if (AppCheckEnum.APP_PASS.value().equals(status)) {
  			return new ModelAndView("redirect:/manage/app/auditList");
  		}
  
  		write(response, String.valueOf(SuccessEnum.SUCCESS.value()));
  		return null;
  	}
  
  	/**
  	 * 下线应用
  	 * 
  	 * @param appId
  	 * @return
  	 */
  	@RequestMapping(value = "/offLine")
  	public ModelAndView offLineApp(HttpServletRequest request,
  			HttpServletResponse response, Model model, Long appId) {
  		AppUser userInfo = getUserInfo(request);
  		logger.warn("user {} hope to offline appId: {}", userInfo.getName(), appId);
  		if (ConstUtils.SUPER_MANAGER.contains(userInfo.getName())) {
  			boolean result = appDeployCenter.offLineApp(appId);
  			model.addAttribute("appId", appId);
  			model.addAttribute("result", result);
  			if (result) {
  				model.addAttribute("msg", "操作成功");
  			} else {
  				model.addAttribute("msg", "操作失败");
  			}
  		    logger.warn("user {} offline appId: {}, result is {}", userInfo.getName(), appId, result);
  		    appEmailUtil.noticeOfflineApp(userInfo, appId, result);
  		} else {
  		    logger.warn("user {} hope to offline appId: {}, hasn't provilege", userInfo.getName(), appId);
  			model.addAttribute("result", false);
  			model.addAttribute("msg", "权限不足");
  	        appEmailUtil.noticeOfflineApp(userInfo, appId, false);
  		}
  		return new ModelAndView();
  	}
  
  	/**
  	 * 实例机器信息
  	 * @param appId
  	 * @param model
  	 */
  	private void fillAppMachineStat(Long appId, Model model){
          List<InstanceInfo> instanceList = appService.getAppInstanceInfo(appId);
          
          Map<String, MachineStats> machineStatsMap = new HashMap<String, MachineStats>();
          Map<String, Long> machineCanUseMem = new HashMap<String, Long>();
          
          for (InstanceInfo instanceInfo : instanceList) {
              if (TypeUtil.isRedisSentinel(instanceInfo.getType())) {
                  continue;
              }
              String ip = instanceInfo.getIp();
              if (machineStatsMap.containsKey(ip)) {
                  continue;
              }
              MachineStats machineStats = machineCenter.getMachineMemoryDetail(ip);
              machineStatsMap.put(ip, machineStats);
              machineCanUseMem.put(ip, machineStats.getMachineMemInfo().getLockedMem());
          }
          model.addAttribute("machineCanUseMem", machineCanUseMem);
          model.addAttribute("machineStatsMap", machineStatsMap);
  	}
  	
  	
  	/**
  	 * 应用运维
  	 * @param appId
  	 */
  	@RequestMapping("/index")
  	public ModelAndView index(HttpServletRequest request, HttpServletResponse response, Model model, Long appId) {
  		model.addAttribute("appId", appId);
  		return new ModelAndView("manage/appOps/appOpsIndex");
  	}
  
  	/**
  	 * 应用机器运维
  	 * @param appId
  	 */
  	@RequestMapping("/machine")
  	public ModelAndView appMachine(HttpServletRequest request, HttpServletResponse response, Model model, Long appId) {
  		if (appId != null && appId > 0) {
  			List<MachineStats> appMachineList = appService.getAppMachineDetail(appId);
  			model.addAttribute("appMachineList", appMachineList);
  			AppDesc appDesc = appService.getByAppId(appId);
  			model.addAttribute("appDesc", appDesc);
  		}
  		return new ModelAndView("manage/appOps/appMachine");
  	}
  
  	/**
  	 * 应用实例运维
  	 * @param appId
  	 */
  	@RequestMapping("/instance")
  	public ModelAndView appInstance(HttpServletRequest request, HttpServletResponse response, Model model, Long appId) {
  		if (appId != null && appId > 0) {
  			AppDesc appDesc = appService.getByAppId(appId);
  			model.addAttribute("appDesc", appDesc);
  			//实例信息和统计
  			fillAppInstanceStats(appId, model);
  			
  			//只有cluster类型才需要计算slot相关
              if (TypeUtil.isRedisCluster(appDesc.getType())) {
                  // 计算丢失的slot区间
                  Map<String,String> lossSlotsSegmentMap = redisCenter.getClusterLossSlots(appId);
                  model.addAttribute("lossSlotsSegmentMap", lossSlotsSegmentMap);
              }
  		}
  		return new ModelAndView("manage/appOps/appInstance");
  	}
  
  	/**
  	 * 应用详细信息和各种申请记录
  	 * @param appId
  	 */
  	@RequestMapping("/detail")
  	public ModelAndView appInfoAndAudit(HttpServletRequest request, HttpServletResponse response, Model model, Long appId) {
  		if (appId != null && appId > 0) {
  			List<AppAudit> appAuditList = appService.getAppAuditListByAppId(appId);
  			AppDesc appDesc = appService.getByAppId(appId);
  			model.addAttribute("appAuditList", appAuditList);
  			model.addAttribute("appDesc", appDesc);
  		}
  		return new ModelAndView("manage/appOps/appInfoAndAudit");
  	}
  	
  	/**
       * redisCluster节点删除: forget + shutdown
       * 
       * @param appId 应用id
       * @param forgetInstanceId 需要被forget的节点
       * @return
       */
      @RequestMapping("/clusterDelNode")
      public ModelAndView clusterDelNode(HttpServletRequest request, HttpServletResponse response, Model model, Long appId,
              int delNodeInstanceId) {
          AppUser appUser = getUserInfo(request);
          logger.warn("user {}, clusterForget: appId:{}, instanceId:{}", appUser.getName(), appId, delNodeInstanceId);
          // 检测forget条件
          ClusterOperateResult checkClusterForgetResult = null;
          try {
              checkClusterForgetResult = redisDeployCenter.checkClusterForget(appId, delNodeInstanceId);
          } catch (Exception e) {
              logger.error(e.getMessage(), e);
          }
          if (checkClusterForgetResult == null || !checkClusterForgetResult.isSuccess()) {
              model.addAttribute("success", checkClusterForgetResult.getStatus());
              model.addAttribute("message", checkClusterForgetResult.getMessage());
              return new ModelAndView("");
          }
          
          // 执行delnode:forget + shutdown
          ClusterOperateResult delNodeResult = null;
          try {
              delNodeResult = redisDeployCenter.delNode(appId, delNodeInstanceId);
          } catch (Exception e) {
              logger.error(e.getMessage(), e);
          }
          model.addAttribute("success", delNodeResult.getStatus());
          model.addAttribute("message", delNodeResult.getMessage());
          logger.warn("user {}, clusterForget: appId:{}, instanceId:{}, result is {}", appUser.getName(), appId, delNodeInstanceId, delNodeResult.getStatus());
          
          return new ModelAndView("");
          
      }
  
  	/**
  	 * redisCluster从节点failover
  	 * 
  	 * @param appId 应用id
  	 * @param slaveInstanceId 从节点instanceId
  	 * @return
  	 */
  	@RequestMapping("/clusterSlaveFailOver")
  	public void clusterSlaveFailOver(HttpServletRequest request, HttpServletResponse response, Model model, Long appId,
  			int slaveInstanceId) {
  		boolean success = false;
  		String failoverParam = request.getParameter("failoverParam");
  		logger.warn("clusterSlaveFailOver: appId:{}, slaveInstanceId:{}, failoverParam:{}", appId, slaveInstanceId, failoverParam);
  		if (appId != null && appId > 0 && slaveInstanceId > 0) {
  			try {
  				success = redisDeployCenter.clusterFailover(appId, slaveInstanceId, failoverParam);
  			} catch (Exception e) {
  				logger.error(e.getMessage(), e);
  			}
  		} else {
  			logger.error("error param clusterSlaveFailOver: appId:{}, slaveInstanceId:{}, failoverParam:{}", appId, slaveInstanceId, failoverParam);
  		}
  	    logger.warn("clusterSlaveFailOver: appId:{}, slaveInstanceId:{}, failoverParam:{}, result is {}", appId, slaveInstanceId, failoverParam, success);
  		write(response, String.valueOf(success == true ? SuccessEnum.SUCCESS.value() : SuccessEnum.FAIL.value()));
  	}
  
  	/**
  	 * 添加slave节点
  	 * 
  	 * @param appId
  	 * @param masterInstanceId
  	 * @param slaveHost
  	 * @return
  	 */
      @RequestMapping(value = "/addSlave")
      public void addSlave(HttpServletRequest request, HttpServletResponse response, Model model, long appId,
              int masterInstanceId, String slaveHost) {
          AppUser appUser = getUserInfo(request);
          logger.warn("user {} addSlave: appId:{},masterInstanceId:{},slaveHost:{}", appUser.getName(), appId, masterInstanceId, slaveHost);
          boolean success = false;
          if (appId > 0 && StringUtils.isNotBlank(slaveHost) && masterInstanceId > 0) {
              try {
                  success = redisDeployCenter.addSlave(appId, masterInstanceId, slaveHost);
              } catch (Exception e) {
                  logger.error(e.getMessage(), e);
              }
          } 
          logger.warn("user {} addSlave: appId:{},masterInstanceId:{},slaveHost:{} result is {}", appUser.getName(), appId, masterInstanceId, slaveHost, success);
          write(response, String.valueOf(success == true ? SuccessEnum.SUCCESS.value() : SuccessEnum.FAIL.value()));
      }
  
      /**
       * 添加sentinel节点
       * @param appId
       * @param sentinelHost
       * @return
       */
  	@RequestMapping(value = "/addSentinel")
  	public void addSentinel(HttpServletRequest request, HttpServletResponse response, Model model, long appId, String sentinelHost) {
          AppUser appUser = getUserInfo(request);
  		logger.warn("user {} addSentinel: appId:{}, sentinelHost:{}", appUser.getName(), appId, sentinelHost);
  	    boolean success = false;
  		if (appId > 0 && StringUtils.isNotBlank(sentinelHost)) {
  			try {
  				success = redisDeployCenter.addSentinel(appId, sentinelHost);
  			} catch (Exception e) {
  				logger.error(e.getMessage(), e);
  			}
  		}
  	    logger.warn("user {} addSentinel: appId:{}, sentinelHost:{} result is {}", appUser.getName(), appId, sentinelHost, success);
  		write(response, String.valueOf(success == true ? SuccessEnum.SUCCESS.value() : SuccessEnum.FAIL.value()));
  	}
  	
  	/**
  	 * 为失联的slot添加master节点
  	 * @param appId
  	 * @param sentinelHost
  	 */
  	@RequestMapping(value = "/addFailSlotsMaster")
      public void addFailSlotsMaster(HttpServletRequest request, HttpServletResponse response, Model model, long appId, String failSlotsMasterHost, int instanceId) {
          AppUser appUser = getUserInfo(request);
          logger.warn("user {} addFailSlotsMaster: appId:{}, instanceId {}, newMasterHost:{}", appUser.getName(), appId, instanceId, failSlotsMasterHost);
          RedisOperateEnum redisOperateEnum = RedisOperateEnum.FAIL;
          if (appId > 0 && StringUtils.isNotBlank(failSlotsMasterHost)) {
              try {
                  redisOperateEnum = redisDeployCenter.addSlotsFailMaster(appId, instanceId, failSlotsMasterHost);
              } catch (Exception e) {
                  logger.error(e.getMessage(), e);
              }
          }
          logger.warn("user {} addFailSlotsMaster: appId:{}, instanceId {}, newMasterHost:{} result is {}", appUser.getName(), appId, instanceId, failSlotsMasterHost, redisOperateEnum.getValue());
          write(response, String.valueOf(redisOperateEnum.getValue()));
      }
  
  	
  	
  	/**
  	 * sentinelFailOver操作
  	 * 
  	 * @param appId
  	 * @return
  	 */
      @RequestMapping("/sentinelFailOver")
  	public void sentinelFailOver(HttpServletRequest request, HttpServletResponse response, Model model, long appId) {
          AppUser appUser = getUserInfo(request);
  		logger.warn("user {} sentinelFailOver, appId:{}", appUser.getName(), appId);
  	    boolean success = false;
  		if (appId > 0) {
  			try {
  				success = redisDeployCenter.sentinelFailover(appId);
  			} catch (Exception e) {
  				logger.error(e.getMessage(), e);
  			}
  		} else {
  			logger.error("error param, sentinelFailOver: appId:{}", appId);
  		}
  	    logger.warn("user {} sentinelFailOver, appId:{}, result is {}", appUser.getName(), appId, success);
  		write(response, String.valueOf(success == true ? SuccessEnum.SUCCESS.value() : SuccessEnum.FAIL.value()));
  	}
      
      /**
       * 应用重要性级别
       */
      @RequestMapping(value = "/updateAppImportantLevel")
      public ModelAndView doUpdateAppImportantLevel(HttpServletRequest request, HttpServletResponse response, Model model) {
          long appId = NumberUtils.toLong(request.getParameter("appId"));
          int importantLevel = NumberUtils.toInt(request.getParameter("importantLevel"));
          SuccessEnum successEnum = SuccessEnum.FAIL;
          if (appId > 0 && importantLevel >= 0) {
              try {
                  AppDesc appDesc = appService.getByAppId(appId);
                  appDesc.setImportantLevel(importantLevel);
                  appService.update(appDesc);
                  successEnum = SuccessEnum.SUCCESS;
              } catch (Exception e) {
                  logger.error(e.getMessage(), e);
              }
          }
          model.addAttribute("status", successEnum.value());
          return new ModelAndView("");
      }
      
      /**
       * 更新应用密码
       */
      @RequestMapping(value = "/updateAppPassword")
      public ModelAndView doUpdateAppPassword(HttpServletRequest request, HttpServletResponse response, Model model) {
          long appId = NumberUtils.toLong(request.getParameter("appId"));
          String password = request.getParameter("password");
          SuccessEnum successEnum = SuccessEnum.FAIL;
          if (appId > 0) {
              try {
                  AppDesc appDesc = appService.getByAppId(appId);
                  appDesc.setPassword(password);
                  appService.update(appDesc);
                  successEnum = SuccessEnum.SUCCESS;
              } catch (Exception e) {
                  logger.error(e.getMessage(), e);
              }
          }
          model.addAttribute("status", successEnum.value());
          return new ModelAndView("");
      }
  
  }
  