package com.bocloud.ims.service.resource.server;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.net.URLDecoder;
import java.util.*;
import java.util.concurrent.TimeUnit;

import org.apache.commons.lang3.StringUtils;
import org.apache.poi.ss.usermodel.Workbook;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.amqp.core.AmqpTemplate;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Service;
import org.springframework.util.Assert;
import org.springframework.web.multipart.MultipartFile;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.bocloud.cmp.entity.enums.*;
import com.bocloud.cmp.entity.enums.installation.InstallStatus;
import com.bocloud.cmp.model.*;
import com.bocloud.common.enums.BoCloudService;
import com.bocloud.common.enums.OSCategory;
import com.bocloud.common.enums.ResourceCatalog;
import com.bocloud.common.model.BocloudResult;
import com.bocloud.common.model.OperateResult;
import com.bocloud.common.model.RequestContext;
import com.bocloud.common.utils.IDFactory;
import com.bocloud.common.utils.JSONTools;
import com.bocloud.common.utils.MapTools;
import com.bocloud.ims.common.utils.constant.Constant;
import com.bocloud.ims.entity.resource.*;
import com.bocloud.ims.interfaces.resource.ServerService;
import com.bocloud.ims.repository.resource.*;
import com.bocloud.ims.service.internal.worker.RequestWorkerService;
import com.bocloud.ims.service.model.cmdb.CmdbInfo;
import com.bocloud.ims.service.utils.*;
import com.bocloud.ims.service.utils.excel.BoStringUtils;
import com.bocloud.ims.service.utils.excel.ExcelFactory;
import com.bocloud.ims.service.utils.excel.ExportHelper;
import com.bocloud.ims.service.utils.excel.ImportHelper;
import com.bocloud.service.core.CurrentService;
import com.bocloud.service.lock.BocloudACLock;
import com.bocloud.service.lock.LockFactory;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;

@Service("hostServerService")
public class HostServerServiceImpl extends ServerServiceImpl implements ServerService {
    private static Logger logger = LoggerFactory.getLogger(HostServerServiceImpl.class);

    @Autowired
    private ServerRepository serverRepository;
    @Autowired
    private FileRepository fileRepository;
    @Autowired
    private ServerConfigRepository serverConfigRepository;
    @Autowired
    private ServerAssetsRepository serverAssetsRepository;
    @Autowired
    private ResourceRepository resourceRepository;
    @Autowired
    private DataCenterRepository dataCenterRepository;
    @Autowired
    private RoomRepository roomRepository;
    @Autowired
    private RackRepository rackRepository;
    @Autowired
    private CmdbMessagePublisher cmdbMessagePublisher;
    @Autowired
    private ResourceEventPublisher resourceEventPublisher;
    @Autowired
    private ThreadPoolTaskExecutor taskExecutor;
    @Autowired
    private RequestWorkerService requestWorker;
    @Autowired
    private AmqpTemplate amqpTemplate;
    @Autowired
    @Qualifier("ServerService")
    private ServerService serverService;

    public HostServerServiceImpl(CurrentService currentService) {
        super(currentService);
    }

    @Override
    public BocloudResult plan(Long id, RequestContext context) {
        String path = Server.class.getSimpleName() + "_" + id;
        try (BocloudACLock lock = lockFactory.getACLock(path)) {
            Assert.isTrue(lock.acquire(10, TimeUnit.SECONDS), "请求超时");
            Server server = this.serverRepository.query(id);
            if (null == server) {
                logger.warn("Server data does not exist!");
                return new BocloudResult(false, "数据不存在");
            }
            if (server.getRackhdNodeId() == null) {
                logger.warn("物理机器{}不能被修改为裸金属服务器.", server.getName());
                return new BocloudResult(false, "物理机器" + server.getName() + "不能被修改为裸金属服务器.");
            }
            server.setStatus(InstallStatus.INSTALL_SUCCEED.getText());
            server.setAssetCategory(ServerAssetCategory.BARE_METAL.name());
            server.setMenderId(context.getTarget());
            server.setGmtModify(new Date());
            this.serverRepository.update(server);
            return new BocloudResult(true, "操作成功");
        } catch (Exception e) {
            logger.error("Modify server fail:", e);
            return new BocloudResult(false, "操作失败");
        }
    }

    @Override
    public BocloudResult<String> create(String params, RequestContext context, Boolean isImport, String instance) {
        try {
            JSONObject object = JSONTools.isJSONObj(params);
            if (null == object) {
                return new BocloudResult(true, "数据不能为空!");
            }
            String configStr = object.getString("config");
            String assetsStr = object.getString("assets");
            Server server = JSONObject.parseObject(params, Server.class);
            if (null == server) {
                logger.warn("Server parameters error!");
                return new BocloudResult(false, "添加主机失败：参数有误");
            }
            String osCategory = server.getOsCategory();
            if (this.serverRepository.exists(server.getManageIp())) {
                logger.warn("Server ip has exist！");
                return new BocloudResult(false, "添加主机失败：IP已存在");
            }
            if (isImport) {
                BocloudResult result =
                    createServer(context, isImport, configStr, assetsStr, server, osCategory, instance);
                if (result.isSuccess()) {
                    return new BocloudResult(true, server.getName() + "，新增成功");
                }
                return new BocloudResult(false, server.getName() + "，新增失败" + "【" + result.getMessage() + "】");
            } else {
                taskExecutor
                    .execute(() -> createServer(context, isImport, configStr, assetsStr, server, osCategory, instance));
                return new BocloudResult(true, "创建任务已经下发，正在执行……");
            }
        } catch (Exception e) {
            logger.error("Create server fail:", e);
            return new BocloudResult(false, "添加主机失败");
        }
    }

    private BocloudResult createServer(RequestContext context, Boolean isImport, String serverConfigStr,
        String serverAssetsStr, Server server, String osCategory, String instance) {
        String operate = "platform.server";
        BocloudResult result = null;
        try {
            ServerConfig config = JSONObject.parseObject(serverConfigStr, ServerConfig.class);
            ServerAssets assets = JSONObject.parseObject(serverAssetsStr, ServerAssets.class);
            if (null == config) {
                config = new ServerConfig();
            }
            if (null == assets) {
                assets = new ServerAssets();
            }
            String systemType = null;
            boolean connectToServer = true;
            if (StringUtils.isEmpty(config.getUsername()) || StringUtils.isEmpty(config.getPassword())
                || null == config.getSshPort()) {
                connectToServer = false;
                result = new BocloudResult(true, "do not connect to server");
            }
            String status = BocloudStatus.Server.UNKNOWN.name();
            if (!StringUtils.isEmpty(osCategory)) {
                switch (osCategory) {
                    case "LINUX":
                    case "AIX":
                        if (connectToServer) {
                            result = ServerHelper.fromSSH(config.getUsername(), config.getPassword(),
                                server.getManageIp(), config.getSshPort(), logger);
                            if (result.isSuccess()) {
                                ServerConfig obj = (ServerConfig)result.getData();
                                config = (ServerConfig)ClassHelper.merger(config, obj);
                                status = BocloudStatus.Server.RUNNING.name();
                            }
                        }
                        systemType = OsCategoryUtils.getValidOSCategoryName(osCategory);
                        break;
                    case "WINDOWS":
                        if (connectToServer) {
                            result = ServerHelper.fromTelnet(config.getUsername(), config.getPassword(),
                                server.getManageIp(), config.getSshPort(), logger);
                            status = BocloudStatus.Server.RUNNING.name();
                        }
                        systemType = OSCategory.WINDOWS.name();
                        break;
                    default:
                        result = new BocloudResult(false, "暂不支持操作系统类型[" + osCategory + "]");
                        break;
                }
            }
            String message = "添加主机成功";
            if (result.isSuccess()) {
                server.setCreatorId(context.getTarget());
                server.setMenderId(context.getTarget());
                server.setStatus(status);
                server.setPowerState(status);
                server.setResourceId(IDFactory.instance().uuid());
                server.setDiscovered(false);
                serverRepository.save(server);
                config.setServerId(server.getId());
                assets.setServerId(server.getId());
                serverConfigRepository.save(config);
                serverAssetsRepository.save(assets);
                // 保存资源
                server.setConfig(config);
                List<String> privateIps = Lists.newArrayList();
                if (!StringUtils.isEmpty(server.getManageIp())) {
                    privateIps.add(server.getManageIp());
                }
                Resource resource = new Resource(ResourceCategory.Computer.name(), ResourceCatalog.PHYSICAL.name(),
                    server.getId(), JSONObject.toJSONString(privateIps), systemType, null);
                resource.setUsername(config.getUsername());
                resource.setPassword(config.getPassword());
                resource.preSave(server.getName(), server.getStatus(), context.getTarget(), context.getTenant());
                resource.setInventory(JSONObject.toJSONString(server));
                resourceRepository.save(resource);
                // 同步到cmdb
                List<CmdbInfo> infos = Lists.newArrayList();
                infos.add(CmdbInfoConvertor.getInfo(server, config, null, null));
                cmdbMessagePublisher.send(CmdbInfoConvertor.getModel(BoCloudService.IMS, infos));
            } else {
                message = "添加主机失败：" + result.getMessage();
            }
            resourceEventPublisher.send(new OperateResult(result.isSuccess(), message, operate, null,
                OperateResult.OperateCategory.ResourceEvent, context));
            // 执行批量导入时，返回执行结果
            if (result.isSuccess()) {
                return new BocloudResult(true, message);
            }
            return new BocloudResult(false, message);
        } catch (Exception e) {
            logger.error("create server fail:", e);
            resourceEventPublisher.send(new OperateResult(false, "添加主机失败", operate, null,
                OperateResult.OperateCategory.ResourceEvent, context));
            return new BocloudResult(false, result, "添加主机失败");
        }
    }

    private void updateServer(RequestContext context, ServerConfig config, Server server) {
        String operate = "platform.server";
        BocloudResult result = null;
        try {
            String status = BocloudStatus.Server.UNKNOWN.name();
            switch (server.getOsCategory()) {
                case "LINUX":
                case "AIX":
                    result = ServerHelper.fromSSH(config.getUsername(), config.getPassword(), server.getManageIp(),
                        config.getSshPort(), logger);
                    if (result.isSuccess()) {
                        ServerConfig obj = (ServerConfig)result.getData();
                        config = (ServerConfig)ClassHelper.merger(config, obj);
                        status = BocloudStatus.Server.RUNNING.name();
                    }
                    break;
                case "WINDOWS":
                    result = ServerHelper.fromTelnet(config.getUsername(), config.getPassword(), server.getManageIp(),
                        config.getSshPort(), logger);
                    status = BocloudStatus.Server.RUNNING.name();
                    break;
                default:
                    result = new BocloudResult(false, "暂不支持操作系统类型[" + server.getOsCategory() + "]");
                    break;
            }
            if (result.isSuccess()) {
                server.setStatus(status);
                server.setPowerState(status);
                serverRepository.update(server);
                serverConfigRepository.update(config);
            }
            Resource resource = this.resourceRepository.query(ResourceCategory.Computer.name(),
                ResourceCatalog.PHYSICAL.name(), server.getId());
            if (null != resource) {
                List<String> privateIps = Lists.newArrayList();
                privateIps.add(server.getManageIp());
                resource.setName(server.getName());
                resource.setUsername(config.getUsername());
                resource.setPassword(config.getPassword());
                resource.setPrivateIps(JSONObject.toJSONString(privateIps));
                resource.setStatus(server.getStatus());
                resourceRepository.update(resource);
            }
            // 同步到cmdb
            List<CmdbInfo> infos = Lists.newArrayList();
            infos.add(CmdbInfoConvertor.getInfo(server, config, "modify", null));
            cmdbMessagePublisher.send(CmdbInfoConvertor.getModel(BoCloudService.IMS, infos));
            resourceEventPublisher.send(
                new OperateResult(true, "更新主机成功", operate, null, OperateResult.OperateCategory.ResourceEvent, context));
        } catch (Exception e) {
            logger.error("create server fail:", e);
            resourceEventPublisher.send(new OperateResult(false, "更新主机失败", operate, null,
                OperateResult.OperateCategory.ResourceEvent, context));
        }
    }

    @Autowired
    private LockFactory lockFactory;

    @Override
    public BocloudResult modify(String params, RequestContext context) {
        JSONObject object = JSONTools.isJSONObj(params);
        ServerBean bean = JSONObject.parseObject(params, ServerBean.class);
        ServerConfigBean configBean = JSONObject.parseObject(object.getString("config"), ServerConfigBean.class);
        ServerAssetsBean assetsBean = JSONObject.parseObject(object.getString("assets"), ServerAssetsBean.class);

        String path = Server.class.getSimpleName() + "_" + bean.getId();
        try (BocloudACLock lock = lockFactory.getACLock(path)) {
            Assert.isTrue(lock.acquire(10, TimeUnit.SECONDS), "请求超时");
            Server server = this.serverRepository.query(bean.getId());
            if (null == server) {
                logger.warn("Server data does not exist!");
                return new BocloudResult(false, "数据不存在");
            }
            ServerConfig config = null;
            if (configBean != null) {
                if (null == configBean.getId()) {
                    config = new ServerConfig();
                    org.springframework.beans.BeanUtils.copyProperties(configBean, config);
                    config.setServerId(bean.getId());
                    serverConfigRepository.save(config);
                } else {
                    config = serverConfigRepository.query(configBean.getId());
                    org.springframework.beans.BeanUtils.copyProperties(configBean, config);
                    serverConfigRepository.update(config);
                }
            }
            ServerAssets assets = null;
            if (assetsBean != null) {
                if (null == assetsBean.getId()) {
                    assets = new ServerAssets();
                    org.springframework.beans.BeanUtils.copyProperties(assetsBean, assets);
                    assets.setServerId(bean.getId());
                    serverAssetsRepository.save(assets);
                } else {
                    assets = serverAssetsRepository.query(assetsBean.getId());
                    org.springframework.beans.BeanUtils.copyProperties(assetsBean, assets);
                    serverAssetsRepository.update(assets);
                }
            }
            BeanUtils.copyProperties(bean, server);
            server.setMenderId(context.getTarget());
            this.serverRepository.update(server);
            ServerConfig serverConfig = config;
            if (StringUtils.isEmpty(config.getUsername()) || StringUtils.isEmpty(config.getPassword())
                || null == config.getSshPort() || StringUtils.isEmpty(server.getOsCategory())) {
                return new BocloudResult(true, "修改成功");
            }
            taskExecutor.execute(() -> updateServer(context, serverConfig, server));
            return new BocloudResult(true, "修改任务已经下发，正在执行……");

        } catch (Exception e) {
            logger.error("Modify server fail:", e);
            return new BocloudResult(false, "修改失败");
        }
    }

    /**
     * 根据uuid修改物理主机
     *
     * @author sxp
     * @since Mar 7, 2018
     * @since 2018-03-08 modify by sxp 对于错误uuid或历史遗留数据无需处理，并视为正常情况
     */
    @Override
    public BocloudResult modifyByUuid(String params, String uuid, RequestContext context) {
        try {
            if (StringUtils.isEmpty(uuid)) {
                return new BocloudResult(false, "修改物理主机失败，原因：uuid的值为空");
            }
            Long id = serverRepository.queryIdByUuid(uuid);
            if (id == null) {
                return new BocloudResult(true, "系统中不存在uuid为[" + uuid + "]的物理主机信息，无需修改");
            }

            // begin--追加server,server_config,server_assets的id属性
            JSONObject json = JSONTools.isJSONObj(params);
            if (null == json) {
                return new BocloudResult(false, "根据uuid修改物理主机时数据不能为空!");
            }

            String serverStr = json.getString("server");
            String serverConfigStr = json.getString("serverConfig");
            String serverAssetsStr = json.getString("serverAssets");

            Server server = JSONObject.parseObject(serverStr, Server.class);
            ServerConfig config = JSONObject.parseObject(serverConfigStr, ServerConfig.class);
            ServerAssets assets = JSONObject.parseObject(serverAssetsStr, ServerAssets.class);

            if (server != null) {
                server.setId(id);
            }
            if (config != null) {
                config.setServerId(id);
                ServerConfig configOfDb = serverConfigRepository.getBySid(id);
                if (configOfDb != null) {
                    config.setId(configOfDb.getId());
                }
            }
            if (assets != null) {
                assets.setServerId(id);
                ServerAssets assetsOfDb = serverAssetsRepository.getBySid(id);
                if (assetsOfDb != null) {
                    assets.setId(assetsOfDb.getId());
                }
            }

            json.put("server", server);
            json.put("serverConfig", config);
            json.put("serverAssets", assets);
            params = json.toJSONString();
            // end----追加server,server_config,server_assets的id属性
            return modify(params, context);
        } catch (Exception e) {
            logger.error("修改物理主机失败," + e.getMessage(), e);
            return new BocloudResult(false, "修改物理主机失败");
        }
    }

    @Override
    public BocloudResult inRack(RackDevice rackDevice, RequestContext context) {
        String path = Server.class.getSimpleName() + "_" + rackDevice.getDeviceId();
        try (BocloudACLock lock = lockFactory.getACLock(path)) {
            Assert.isTrue(lock.acquire(10, TimeUnit.SECONDS), "请求超时");
            this.serverRepository.belong(rackDevice, context.getTarget());
            return new BocloudResult(true, "添加成功");
        } catch (Exception e) {
            logger.error("Assign rack to server fail:", e);
            return new BocloudResult(false, "添加失败");
        }
    }

    @Override
    public BocloudResult outRack(Long id, RequestContext context) {
        String path = Server.class.getSimpleName() + "_" + id;
        try (BocloudACLock lock = lockFactory.getACLock(path)) {
            Assert.isTrue(lock.acquire(10, TimeUnit.SECONDS), "请求超时");
            this.serverRepository.release(id, context.getTarget());
            return new BocloudResult(true, "移除成功");
        } catch (Exception e) {
            logger.error("Free server from rack fail:", e);
            return new BocloudResult(false, "移除失败");
        }
    }

    @Override
    public BocloudResult freePMs() {
        List<Server> list = null;
        try {
            list = this.serverRepository.listFree();
        } catch (Exception e) {
            logger.error("Query free server list fail:", e);
            return new BocloudResult(false, "");
        }
        return new BocloudResult(true, list, "查询成功");
    }

    private Map<String, Object> buildWorkerParams(String host, String user, String pasd, Integer port,
        NodeAction action) {
        final String ipmiPassword = "ipmiPassword";
        Map<String, Object> paramMap = new HashMap<>(2);
        Map<String, Object> module = new HashMap<>(2);
        Map<String, Object> decrypt = new HashMap<>(1);
        Map<String, Object> args = MapTools.simpleMap(Constant.NAME, host);
        args.put(Constant.USER, user);
        args.put(Constant.KEY, "{{ " + ipmiPassword + " }}");
        args.put(Constant.PORT, port);
        switch (action) {
            case on:
                args.put(Constant.STATE, NodeAction.on.name());
                module.put(Constant.NAME, Constant.IPMI_POWER);
                break;
            case off:
                args.put(Constant.STATE, NodeAction.off.name());
                module.put(Constant.NAME, Constant.IPMI_POWER);
                break;
            case shutdown:
                args.put(Constant.STATE, NodeAction.shutdown.name());
                module.put(Constant.NAME, Constant.IPMI_POWER);
                break;
            case reset:
                args.put(Constant.STATE, NodeAction.reset.name());
                module.put(Constant.NAME, Constant.IPMI_POWER);
                break;
            case boot:
                args.put(Constant.STATE, NodeAction.boot.name());
                module.put(Constant.NAME, Constant.IPMI_POWER);
                break;
            case networkBoot:
                args.put(Constant.BOOTDEV, BootOption.NETWORK.name().toLowerCase());
                module.put(Constant.NAME, Constant.IPMI_BOOT);
                break;
            case hdBoot:
                args.put(Constant.BOOTDEV, BootOption.HD.name().toLowerCase());
                module.put(Constant.NAME, Constant.IPMI_BOOT);
                break;
            case opticalBoot:
                args.put(Constant.BOOTDEV, BootOption.OPTIONAL.name().toLowerCase());
                module.put(Constant.NAME, Constant.IPMI_BOOT);
                break;
            case resetBoot:
                args.put(Constant.BOOTDEV, BootOption.DEFAULT.name().toLowerCase());
                module.put(Constant.NAME, Constant.IPMI_BOOT);
                break;
            default:
                throw new RuntimeException("不支持的物理机操作类型" + action);
        }
        module.put(Constant.ARGS, args);
        decrypt.put(ipmiPassword, pasd);
        module.put(Constant.WORKER_DECRYPT, decrypt);

        paramMap.put(Constant.MODULE, module);
        List<Map<String, Object>> targets = Lists.newArrayList();
        targets.add(MapTools.simpleMap(Constant.HOST, Constant.LOCALHOST));
        paramMap.put(Constant.TARGETS, targets);
        return paramMap;
    }

    private String parseAction(NodeAction action) {
        String actionString = null;
        switch (action) {
            case on:
                actionString = "启动";
                break;
            case off:
                actionString = "停止";
                break;
            case reset:
                actionString = "重启";
                break;
            default:
                actionString = action.name();
                break;
        }
        return actionString;
    }

    private BocloudResult action(Server server, NodeAction action) {
        try {
            ServerConfig config = server.getConfig();
            if (StringUtils.isEmpty(config.getIpmiAddress()) || StringUtils.isEmpty(config.getIpmiAccount())
                || StringUtils.isEmpty(config.getIpmiPassword()) || null == config.getIpmiPort()) {
                return new BocloudResult(false, "物理机[" + server.getName() + "]ipmi信息异常，请编辑ipmi信息");
            }
            // 以任务的形式下发给worker,对物理机进行开机，关机，重启操作
            Map<String, Object> workerContext = MapTools.simpleMap("action", action);
            workerContext.put("server", server);
            Map<String, Object> param = buildWorkerParams(config.getIpmiAddress(), config.getIpmiAccount(),
                config.getIpmiPassword(), config.getIpmiPort(), action);
            BocloudResult invokeResult = requestWorker.invoke(param, this::notify, workerContext);
            String operate = parseAction(action);
            if (invokeResult.isSuccess()) {
                String status = "";
                switch (action) {
                    case on:
                        status = "STARTING";
                        break;
                    case off:
                        status = "STOPPING";
                        break;
                    case reset:
                        status = "RESTARTING";
                        break;
                    default:
                        break;
                }
                // server.setStatus(status);
                server.setPowerState(status);
                serverRepository.update(server);
                return new BocloudResult(true, operate + "任务下发成功");
            }
            logger.warn(operate + "任务下发失败:{}", invokeResult.getMessage());
            return new BocloudResult(invokeResult.isSuccess(), operate + "任务下发失败");
        } catch (Exception e) {
            logger.error("任务下发异常:", e);
            return new BocloudResult(false, "任务下发异常");
        }
    }

    private void sendMessage(boolean success, NotifyMessage message, String content) {
        // 站内信和邮件发送
        amqpTemplate.convertAndSend("event.notice.queue", message);
        // 界面消息
        OperateResult operateResult = new OperateResult(success, content, "platform.server", null,
            OperateResult.OperateCategory.ResourceEvent, null);
        resourceEventPublisher.send(operateResult);
    }

    public void notify(Map<String, Object> workerContext, BocloudResult result) {
        NodeAction action = (NodeAction)workerContext.get("action");
        Server host = (Server)workerContext.get("server");
        Server server = null;
        try {
            server = this.serverRepository.query(host.getId());
        } catch (Exception e) {
            logger.error("query server error:", e);
        }
        String content;
        String prefix = parseAction(action);
        if (server == null) {
            content = prefix + "失败, 物理机[" + host.getName() + "]已经不存在";
            logger.warn(content);
            RequestContext context = new RequestContext(RequestContext.Catalog.Manager, 1L, null, null, null);
            NotifyMessage message =
                new NotifyMessage(context, prefix + "情况通知", content, "管理员您好，" + content, true, false);
            sendMessage(false, message, content);
            return;
        }
        content = prefix + (result.isSuccess() ? "成功" : "失败[" + result.getMessage() + "]");
        String subject = "物理主机执行启停操作情况通知";
        RequestContext context =
            new RequestContext(RequestContext.Catalog.Manager, server.getMenderId(), null, null, null);
        NotifyMessage message = new NotifyMessage(context, subject, content, "您好，" + content, true, false);
        // 更新物理机的状态
        switch (action) {
            case on:
            case off:
            case reset:
                String status = BocloudStatus.Server.STOPPED.name();
                if (result.isSuccess()) {
                    if (action == NodeAction.on || action == NodeAction.reset) {
                        status = BocloudStatus.Server.RUNNING.name();
                    }
                } else {
                    if (action == NodeAction.off || action == NodeAction.reset) {
                        status = BocloudStatus.Server.RUNNING.name();
                    }
                }
                // server.setStatus(status);
                server.setPowerState(status);
                try {
                    serverRepository.update(server);
                    // 同步到cmdb
                    List<CmdbInfo> infos = Lists.newArrayList();
                    infos.add(CmdbInfoConvertor.getInfo(server, server.getConfig(), "modify", null));
                    cmdbMessagePublisher.send(CmdbInfoConvertor.getModel(BoCloudService.IMS, infos));
                } catch (Exception e) {
                    logger.error("执行启停操作失败:", e);
                    content = "执行启停操作异常";
                    message = new NotifyMessage(context, subject, content, "您好，" + content, true, false);
                }
                break;
            default:
                logger.error("action的值不合法");
                break;
        }
        sendMessage(result.isSuccess(), message, content);

    }

    @Override
    public BocloudResult start(Long id, RequestContext context) {
        String path = Server.class.getSimpleName() + "_" + id;
        try (BocloudACLock lock = lockFactory.getACLock(path)) {
            Assert.isTrue(lock.acquire(10, TimeUnit.SECONDS), "请求超时");
            Server server = this.serverRepository.getServerBean(id);
            if (null == server) {
                logger.warn("Server data does not exist!");
                return new BocloudResult(false, server, "该服务器不存在");
            }
            CloudProvider type = serverService.getCloudVendorType(server);
            ServerConfig config = serverConfigRepository.getBySid(id);
            if (null == config) {
                logger.warn("Server config data does not exist!");
                return new BocloudResult(false, server, "该服务器配置信息有缺失");
            }
            server.setConfig(config);
            server.setMenderId(context.getTarget());
            return action(server, NodeAction.on);
        } catch (Exception e) {
            logger.error("Start server fail:", e);
            return new BocloudResult(false, "启动失败");
        }
    }

    @Override
    public BocloudResult stop(Long id, RequestContext context) {
        String path = Server.class.getSimpleName() + "_" + id;
        try (BocloudACLock lock = lockFactory.getACLock(path)) {
            Assert.isTrue(lock.acquire(10, TimeUnit.SECONDS), "请求超时");
            Server server = this.serverRepository.getServerBean(id);
            if (null == server) {
                logger.warn("Server data does not exist!");
                return new BocloudResult(false, server, "该服务器不存在");
            }
            ServerConfig config = serverConfigRepository.getBySid(id);
            if (null == config) {
                logger.warn("Server config data does not exist!");
                return new BocloudResult(false, server, "该服务器配置信息有缺失");
            }
            server.setConfig(config);
            server.setMenderId(context.getTarget());
            return action(server, NodeAction.off);
        } catch (Exception e) {
            logger.error("Stop server fail:", e);
            return new BocloudResult(false, "关闭失败");
        }
    }

    @Override
    public BocloudResult restart(Long id, RequestContext context) {
        try {
            Server server = this.serverRepository.getServerBean(id);
            if (null == server) {
                logger.warn("Server data does not exist!");
                return new BocloudResult(false, server, "该服务器不存在");
            }
            ServerConfig config = serverConfigRepository.getBySid(id);
            if (null == config) {
                logger.warn("Server config data does not exist!");
                return new BocloudResult(false, server, "该服务器配置信息有缺失");
            }
            server.setConfig(config);
            server.setMenderId(context.getTarget());
            return action(server, NodeAction.reset);
        } catch (Exception e) {
            logger.error("Restart server fail:", e);
            return new BocloudResult(false, "重启失败");
        }
    }

    @Override
    public BocloudResult batchStart(String params, RequestContext context) {
        List<Long> idArray = JSONArray.parseArray(params, Long.class);
        taskExecutor.execute(() -> {
            String operate = "platform.server";
            try {
                String failServerName = "";
                if (null != idArray && idArray.size() != 0) {
                    for (Long id : idArray) {
                        Server server = serverRepository.query(id);
                        if (null == server || !"STOPPED".equalsIgnoreCase(server.getPowerState())) {
                            continue;
                        }
                        BocloudResult result = start(id, context);// 停止物理机
                        if (!result.isSuccess()) {// 该物理机停止失败
                            // 查询停止失败的物理机
                            failServerName += server.getName() + " ";
                        }
                    }
                }
                if (!StringUtils.isEmpty(failServerName)) {
                    resourceEventPublisher.send(new OperateResult(false, "物理机[" + failServerName + "]启动失败", operate,
                        null, OperateResult.OperateCategory.ResourceEvent, context));
                }
            } catch (Exception e) {
                logger.error("batch stop server fail:", e);
                resourceEventPublisher.send(new OperateResult(false, "批量启动失败", operate, null,
                    OperateResult.OperateCategory.ResourceEvent, context));
            }
        });
        return new BocloudResult(true, "批量启动任务已下发，正在执行...");
    }

    @Override
    public BocloudResult batchStop(String params, RequestContext context) {
        String[] idArray = params.split(",");// 获取要停止的虚拟机的id
        taskExecutor.execute(() -> {
            String operate = "platform.server";
            try {
                String failServerName = "";
                if (null != idArray && idArray.length != 0) {
                    for (String id : idArray) {
                        Server server = serverRepository.query(Long.parseLong(id));
                        if (null == server || !"RUNNING".equalsIgnoreCase(server.getPowerState())) {
                            continue;
                        }
                        BocloudResult result = stop(Long.parseLong(id), context);// 停止物理机
                        if (result.isSuccess()) {// 该物理机停止失败
                            if (null != result.getData()) {
                                resourceEventPublisher.send(new OperateResult(true, "物理机[" + server.getName() + "]停止成功",
                                    operate, null, OperateResult.OperateCategory.ResourceEvent, context));
                            }
                        } else {
                            // 查询停止失败的物理机
                            failServerName += server.getName() + " ";
                        }
                    }
                }
                if (!StringUtils.isEmpty(failServerName)) {
                    resourceEventPublisher.send(new OperateResult(false, "物理机[" + failServerName + "]停止失败", operate,
                        null, OperateResult.OperateCategory.ResourceEvent, context));
                }
            } catch (Exception e) {
                logger.error("batch stop server fail:", e);
                resourceEventPublisher.send(new OperateResult(false, "批量停止失败", operate, null,
                    OperateResult.OperateCategory.ResourceEvent, context));
            }
        });
        return new BocloudResult(true, "批量停止任务已下发，正在执行...");
    }

    @Override
    public BocloudResult batchRemove(String params, RequestContext context) {
        String[] idArray = params.split(",");// 获取要删除的物理机的id
        taskExecutor.execute(new Runnable() {
            @Override
            public void run() {
                String operate = "platform.server";
                try {
                    // 失败物理机机名称
                    String failServerName = "";
                    String successServerName = "";
                    if (null != idArray && idArray.length > 0) {
                        for (String id : idArray) {
                            Server server = serverRepository.query(Long.parseLong(id));
                            if (null == server) {
                                continue;
                            }
                            BocloudResult result = remove(Long.parseLong(id), false, context);// 删除物理机
                            if (result.isSuccess()) {
                                if (null != result.getData()) {
                                    successServerName += server.getName() + " ";
                                }
                            } else {
                                failServerName += server.getName() + " ";
                            }
                        }
                    }
                    if (!StringUtils.isEmpty(successServerName)) {
                        resourceEventPublisher
                            .send(new OperateResult(false, "物理机[" + successServerName.trim() + "]删除成功", operate, null,
                                OperateResult.OperateCategory.ResourceEvent, context));
                    }
                    if (!StringUtils.isEmpty(failServerName)) {
                        resourceEventPublisher.send(new OperateResult(false, "物理机[" + failServerName.trim() + "]删除失败",
                            operate, null, OperateResult.OperateCategory.ResourceEvent, context));
                    }
                } catch (Exception e) {
                    logger.error("batch remove server fail:", e);
                    resourceEventPublisher.send(new OperateResult(false, "批量删除失败", operate, null,
                        OperateResult.OperateCategory.ResourceEvent, context));
                }
            }
        });
        return new BocloudResult(true, "批量删除任务已下发，正在执行...");
    }

    /**
     * 更新物理主机状态
     *
     * @param params
     *            参数
     * @param uuid
     *            物理主机的uuid
     * @return 操作结果
     * @author sxp
     * @since Mar 12, 2018
     */
    @Override
    public BocloudResult updateStatus(String params, String uuid) {
        try {
            if (StringUtils.isEmpty(uuid)) {
                return new BocloudResult(false, "更新物理主机状态失败，原因：uuid的值为空");
            }
            Long id = serverRepository.queryIdByUuid(uuid);
            if (id == null) {
                return new BocloudResult(true, "系统中不存在uuid为[" + uuid + "]的物理主机信息，无需更新");
            }

            JSONObject json = JSONTools.isJSONObj(params);
            if (null == json) {
                return new BocloudResult(false, "根据uuid更新物理主机状态时数据不能为空!");
            }

            String newStatus = json.getString("status");
            if (StringUtils.isEmpty(newStatus)) {
                return new BocloudResult(false, "更新物理主机状态失败，原因：status参数内容为空");
            }

            Server server = this.serverRepository.getServerBean(id);
            ServerConfig config = serverConfigRepository.getBySid(id);
            if (null == server) {
                return new BocloudResult(true, server, "系统中不存在id为[" + id + "]的物理主机信息，无需更新");
            }
            if (config == null) {
                return new BocloudResult(true, server, "系统中不存在id为[" + id + "]的物理主机配置信息，无需更新");
            }
            String status = server.getStatus();

            if (!newStatus.equals(status)) {
                server.setStatus(newStatus);
                server.setPowerState(newStatus);
                serverRepository.update(server);

                // 同步到cmdb
                List<CmdbInfo> infos = Lists.newArrayList();
                infos.add(CmdbInfoConvertor.getInfo(server, config, "modify", null));
                cmdbMessagePublisher.send(CmdbInfoConvertor.getModel(BoCloudService.IMS, infos));
            }

            return new BocloudResult(true, "更新物理主机状态成功");
        } catch (Exception e) {
            logger.error("更新物理主机状态失败," + e.getMessage(), e);
            return new BocloudResult(false, "更新物理主机状态失败");
        }
    }

    @Override
    public BocloudResult<Map<String, Object>> download(Map<String, Object> paramsMap) {
        // 下载日志文件
        if (BoStringUtils.isNotEmpty(paramsMap.get("fileName"))) {
            return downloadLogFile(paramsMap);
        } else {
            // 下载模板或数据
            return downloadExcel(paramsMap);
        }
    }

    /**
     * 下载日志文件
     *
     * @param paramsMap
     * @return
     */
    private BocloudResult<Map<String, Object>> downloadLogFile(Map<String, Object> paramsMap) {
        Map<String, Object> resultMap = Maps.newHashMap();
        File file;
        try {
            file = fileRepository.queryByUuid(BoStringUtils.toString(paramsMap.get("fileName")));
            if (file == null) {
                return new BocloudResult(false, "文件不存在，无法下载...");
            }
            byte[] ba = file.getFileBytes();
            resultMap.put("fileName",
                paramsMap.get("fileName") + (file.getFileName().endsWith("xlsx") ? ".xlsx" : "xls"));
            resultMap.put("data", Base64.getEncoder().encodeToString(ba));
        } catch (Exception e1) {
            logger.error("下载失败", e1);
            return new BocloudResult(false, "内部错误，无法下载...");
        }

        return new BocloudResult(true, resultMap, "下载成功");
    }

    /**
     * 下载excel模板
     *
     * @param paramsMap
     * @return
     */
    private BocloudResult<Map<String, Object>> downloadExcel(Map<String, Object> paramsMap) {
        Workbook workbook = null;
        try {
            // 校验前端传入的excel的版本（2000或2007），生成后缀。当前为前端写死为2007版本
            String fileName = "物理主机.xls";
            if ("2007".equals(BoStringUtils.toString(paramsMap.get("excelType")))) {
                fileName = fileName + "x";
            }
            workbook = new ExcelFactory(fileName).newWorkbook();
            ExportHelper exportHelper = new ExportHelper(dataCenterRepository, roomRepository, rackRepository);
            // 生成模板
            exportHelper.generateSheet(workbook);
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            workbook.write(baos);
            byte[] ba = baos.toByteArray();
            Map<String, Object> resultMap = Maps.newHashMap();
            resultMap.put("fileName", fileName);
            resultMap.put("data", Base64.getEncoder().encodeToString(ba));
            return new BocloudResult(true, resultMap, "下载成功");
        } catch (Exception e) {
            logger.error("Get error message：", e);
            return new BocloudResult(false, "下载模板失败：" + e.getMessage());
        }
    }

    /**
     * 批量导入主机资源
     */
    @Override
    public BocloudResult upload(Map<String, Object> paramsMap, MultipartFile excel, Long contextId, String instance)
        throws IOException {
        String uuid = IDFactory.instance().uuid();
        String fileName = URLDecoder.decode(excel.getOriginalFilename(), "UTF-8");
        ExcelFactory excelFactory;
        excelFactory = new ExcelFactory(excel.getInputStream(), fileName);
        Workbook wb = excelFactory.newWorkbook();
        try {
            taskExecutor.execute(() -> {
                ImportHelper importHelper = new ImportHelper(this, fileRepository, amqpTemplate, dataCenterRepository,
                    roomRepository, rackRepository, resourceEventPublisher);
                importHelper.readExcel(wb, uuid, contextId, fileName, instance);
            });
        } catch (Exception e) {
            logger.error("批量导入主机失败", e);
            return new BocloudResult(false, "批量导入主机失败");
        }
        return new BocloudResult(true, uuid, "批量导入主机任务已下发");
    }

    /**
     * 修改机房机柜
     *
     * @param id
     * @param rackId
     * @param context
     * @return
     */
    @Override
    public BocloudResult patch(Long id, Long rackId, RequestContext context) {
        String path = Server.class.getSimpleName() + "_" + id;
        try (BocloudACLock lock = lockFactory.getACLock(path)) {
            Assert.isTrue(lock.acquire(10, TimeUnit.SECONDS), "请求超时");
            Server server = this.serverRepository.query(id);
            if (null == server) {
                logger.warn("Server data does not exist!");
                return new BocloudResult(false, "数据不存在");
            }
            server.setRackId(rackId);
            server.setMenderId(context.getTarget());
            this.serverRepository.update(server);
            return new BocloudResult(true, "修改成功");
        } catch (Exception e) {
            logger.error("Modify server fail:", e);
            return new BocloudResult(false, "修改失败");
        }
    }
}
