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

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.bocloud.cmp.driver.hmc.HmcPartitionDriver;
import com.bocloud.cmp.entity.enums.CloudProvider;
import com.bocloud.cmp.model.NotifyMessage;
import com.bocloud.cmp.model.Target;
import com.bocloud.cmp.model.hmc.sdk.module.LogicalPartitionEntry;
import com.bocloud.common.encrypt.AESEncrptor;
import com.bocloud.common.encrypt.DESEncrptor;
import com.bocloud.common.model.BocloudResult;
import com.bocloud.common.model.OperateResult;
import com.bocloud.common.model.RequestContext;
import com.bocloud.common.utils.MapTools;
import com.bocloud.ims.common.enums.HmcStatus;
import com.bocloud.ims.entity.resource.CloudVendor;
import com.bocloud.ims.entity.resource.hmc.*;
import com.bocloud.ims.entity.resource.hmc.params.HmcNimInstallParams;
import com.bocloud.ims.interfaces.resource.hmc.HmcNimService;
import com.bocloud.ims.repository.resource.CloudVendorRepository;
import com.bocloud.ims.repository.resource.hmc.*;
import com.bocloud.ims.service.internal.worker.RequestWorkerService;
import com.bocloud.ims.service.utils.ClassHelper;
import com.bocloud.ims.service.utils.ResourceEventPublisher;
import com.bocloud.ims.service.utils.ServerHelper;
import com.bocloud.service.core.CurrentService;
import org.apache.curator.framework.recipes.leader.LeaderLatchListener;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.amqp.core.AmqpTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Service;
import org.springframework.util.concurrent.ListenableFuture;
import org.springframework.util.concurrent.ListenableFutureCallback;

import javax.annotation.PostConstruct;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.*;
import java.util.concurrent.CancellationException;
import java.util.concurrent.Future;
import java.util.stream.Collectors;

@Service("nimService")
public class HmcNimServiceImpl implements HmcNimService {

    private static final Logger logger = LoggerFactory.getLogger(HmcNimServiceImpl.class);

    private static final String STATUS_COMMAND = "lsnim -l {{nimName}} |grep -v {{nimName}}";
    private static final String LIST_COMMAND = "lsnim -l";
    private static final String COMMAND1 =
            // if client already defined, then undefine client
            "lsnim -l {{nimName}} ; " +
                    " if [[ $? -eq 0 ]]; then " +
                    "    echo nim client is already defined, undefine it...;" +
                    "    nim -Fo reset {{nimName}} ;" +
                    "    nim -Fo deallocate -a subclass=all {{nimName}};" +
                    "    nim -o remove {{nimName}};" +
                    "    hostent -d {{lparIp}} ;" +
                    " fi;"
                    // add client to /etc/hosts
                    + " hostent -a {{lparIp}} -h {{lparName}};"
                    // define client to nim server
                    + " nim -o define -t standalone -a platform=chrp -a netboot_kernel=64 -a connect=shell -a if1=\"{{nimNet}} {{lparName}} 0 ent0\" -a cable_type1=tp {{nimName}};"
                    // define client install task to nim server
                    + " nim -o bos_inst -a source=mksysb -a boot_client=no -a mksysb={{nimMksysb}} -a spot={{nimSpot}} -a bosinst_data={{nimBosinst}} -a accept_licenses=yes {{nimName}};";

    private static final String COMMAND2 = "lpar_netboot -i -T on -v -x -f -D -t ent -s auto -d auto -S {{nimHost}} -G {{lparGateway}} -C {{lparIp}} -K {{lparMask}} {{lparName}} {{profileName}} {{serverName}};";
    private static final String COMMAND3 =
            "nim -Fo reset {{nimName}}; " +
                    "nim -Fo deallocate -a subclass=all {{nimName}};" +
                    "nim -o remove {{nimName}}; " +
                    "hostent -d {{lparIp}} ";
    private final Map<Long, Future> futureHolder = new HashMap<>();
    @Autowired
    private ThreadPoolTaskExecutor taskExecutor;
    @Autowired
    private CurrentService currentService;
    @Autowired
    private RequestWorkerService requestWorker;
    @Autowired
    private AmqpTemplate amqpTemplate;
    @Autowired
    private ResourceEventPublisher resourceEventPublisher;
    @Autowired
    private HmcNimServerConfig nimServerConfig;
    @Autowired
    private HmcPartitionRepository hmcPartitionRepository;
    @Autowired
    private HmcProfileRepository hmcProfileRepository;
    @Autowired
    private HmcServerRepository hmcServerRepository;
    @Autowired
    private CloudVendorRepository cloudVendorRepository;
    @Autowired
    private HmcProfileNetworkAdapterRepository hmcProfileNetworkAdapterRepository;
    @Autowired
    private HmcProfileFibreAdapterRepository hmcProfileFibreAdapterRepository;
    @Autowired
    private HmcPartitionVfcAdapterRepository hmcPartitionVfcAdapterRepository;
    @Autowired
    private HmcProfileScsiAdapterRepository hmcProfileScsiAdapterRepository;
    @Autowired
    private HmcPartitionScsiAdapterRepository hmcPartitionScsiAdapterRepository;
    @Autowired
    private HmcVirtualDiskRepository hmcVirtualDiskRepository;
    @Autowired
    private HmcPhysicalDiskRepository hmcPhysicalDiskRepository;
    @Autowired
    private HmcPartitionDriver hmcPartitionDriver;

    private String getErrMsgFromWorkerResult(BocloudResult workerResult) {
        JSONArray dataArray = Optional.ofNullable(workerResult.getData()).map(Object::toString).map(JSON::parseArray).orElse(new JSONArray());
        return dataArray.stream().map(JSON::toJSONString).map(JSON::parseObject)
                .map(o -> o.getString("message")).collect(Collectors.joining("\r\n"));
    }

    private String getNimNameFromPartitionName(String partitionName) {
        return partitionName.replace(".", "_");
    }

    private boolean checkIScsiAndFcForInstall(HmcPartition partition, HmcProfile profile) {
        // 光纤通道的检查结果变量，先认为光纤通道满足要求
        boolean checkFcResult = true;
        // 1. 首先要有光纤通道
        List<HmcProfileFibreAdapter> hmcProfileFibreAdapters = hmcProfileFibreAdapterRepository.queryByProfileId(profile.getId());
        if (hmcProfileFibreAdapters.size() == 0) {
            logger.warn("分区{}的概要文件{}里面没有定义虚拟FC适配器!", partition.getName(), profile.getName());
            checkFcResult = false;
        }
        // 2. 如果有光纤通道，还要再检查光纤通道在VIOS是否存在对应关系
        if (checkFcResult) {
            List<HmcPartitionVfcAdapter> hmcViosFcAdapters = new ArrayList<>();
            for (HmcProfileFibreAdapter hmcProfileFibreAdapter : hmcProfileFibreAdapters) {
                HmcPartition vios = hmcPartitionRepository.getByHmcPartitionId(hmcProfileFibreAdapter.getConnectingPartitionId(), partition.getServerId());
                if (vios == null) {
                    logger.error("分区{}的概要文件{}里面定义虚拟光纤适配器对应的VIOS未找到!", partition.getName(), profile.getName());
                    return false;
                }
                HmcPartitionVfcAdapter hmcViosFcAdapter = hmcPartitionVfcAdapterRepository.queryMappingByViosIdAndSlot(vios.getId(), hmcProfileFibreAdapter.getConnectingVirtualSlotNumber());
                if (hmcViosFcAdapter != null) {
                    hmcViosFcAdapters.add(hmcViosFcAdapter);
                }
            }
            if (hmcViosFcAdapters.size() == 0) {
                logger.warn("分区{}的概要文件{}里面定义的虚拟FC适配器没有对应VIOS的虚拟FC适配器!", partition.getName(), profile.getName());
                checkFcResult = false;
            }
        }

        // 如果光纤通道检查通过就不检查ISCSI了，直接返回成功
        if (checkFcResult) {
            return true;
        }

        // 如果profile里的光纤通道没有检查通过，检查下iscsi是否满足装机要求

        // ISCSI的检查结果变量，先认为ISCSI满足要求
        boolean checkIscsiResult = true;
        // 1. 检查profile里面是否有iscsi适配器
        List<HmcProfileScsiAdapter> hmcProfileScsiAdapters = hmcProfileScsiAdapterRepository.queryByProfileId(profile.getId());
        if (hmcProfileScsiAdapters.size() == 0) {
            logger.warn("分区{}的概要文件{}里面没有定义虚拟SCSI适配器!", partition.getName(), profile.getName());
            checkIscsiResult = false;
        }
        // 2. 如果有ISCSI，还要检查iscsi适配器是否在VIOS存在对应
        List<HmcPartitionScsiAdapter> hmcViosScsiAdapters = new ArrayList<>();
        if (checkIscsiResult) {
            for (HmcProfileScsiAdapter hmcProfileScsiAdapter : hmcProfileScsiAdapters) {
                HmcPartition vios = hmcPartitionRepository.getByHmcPartitionId(hmcProfileScsiAdapter.getRemotePartitionId(), partition.getServerId());
                if (vios == null) {
                    logger.error("分区{}的概要文件{}里面定义虚拟SCSI适配器对应的VIOS未找到!", partition.getName(), profile.getName());
                    return false;
                }
                HmcPartitionScsiAdapter hmcViosScsiAdapter = hmcPartitionScsiAdapterRepository.query(vios.getId(), hmcProfileScsiAdapter.getRemoteSlotNumber(), "Server");
                if (hmcViosScsiAdapter != null) {
                    hmcViosScsiAdapters.add(hmcViosScsiAdapter);
                }
            }
            if (hmcViosScsiAdapters.size() == 0) {
                logger.warn("分区{}的概要文件{}里面定义的虚拟SCSI适配器没有对应VIOS的虚拟SCSI适配器!", partition.getName(), profile.getName());
                checkIscsiResult = false;
            }
        }
        // 3. 有ISCSI且在VIOS有对应的ISCSI适配器，还要检查iscsi是否分配了lv/pv
        if (checkIscsiResult) {
            List<HmcVirtualDisk> virtualDisks = new ArrayList<>();
            List<HmcPhysicalDisk> physicalDisks = new ArrayList<>();
            for (HmcPartitionScsiAdapter hmcViosScsiAdapter : hmcViosScsiAdapters) {
                List<HmcVirtualDisk> viosVirtualDisks = hmcVirtualDiskRepository.queryByViosId(hmcViosScsiAdapter.getPartitionId());
                for (HmcVirtualDisk viosVirtualDisk : viosVirtualDisks) {
                    if (hmcViosScsiAdapter.getAdapterId().equals(viosVirtualDisk.getViosSlotId())) {
                        virtualDisks.add(viosVirtualDisk);
                    }
                }
                List<HmcPhysicalDisk> viosPhysicalDisks = hmcPhysicalDiskRepository.query(hmcViosScsiAdapter.getPartitionId(), null);
                for (HmcPhysicalDisk viosPhysicalDisk : viosPhysicalDisks) {
                    if (hmcViosScsiAdapter.getAdapterId().equals(viosPhysicalDisk.getViosSlotId())) {
                        physicalDisks.add(viosPhysicalDisk);
                    }
                }
            }
            if (virtualDisks.size() + physicalDisks.size() == 0) {
                logger.warn("分区{}的概要文件{}里面定义的虚拟SCSI适配器没有对应虚拟磁盘或物理磁盘!", partition.getName(), profile.getName());
                checkIscsiResult = false;
            }
        }

        // ISCSI检查通过，返回成功
        if (checkIscsiResult) {
            return true;
        }

        return false;
    }

    private Map<String, Object> buildWorkerParam(Target target, String command) {
        Map<String, Object> module = new HashMap<>();
        module.put("name", "raw");
        module.put("args", command);

        Map<String, Object> param = new HashMap<>();
        param.put("module", module);
        param.put("targets", Collections.singleton(target));
        param.put("timeout", 3600);
        return param;
    }

    private void sendMessage(boolean success, String caption, String content, Long vendorId, RequestContext context) {
        // 站内信和邮件发送
        NotifyMessage message = new NotifyMessage(context, caption, content, null, true, false);
        amqpTemplate.convertAndSend("event.notice.queue", message);
        // 界面消息
        OperateResult operateResult = new OperateResult(success, content, "nim.aix.install", MapTools.simpleMap("vendorId", vendorId), OperateResult.OperateCategory.ResourceEvent, context);
        resourceEventPublisher.send(operateResult);
    }

    private void serverConfigCallback(final Map<String, Object> workerContext, BocloudResult workerResult) {
        logger.debug("--callback--< {}", JSON.toJSONString(workerResult));

        RequestContext user = (RequestContext) workerContext.get("context");
        HmcServer server = (HmcServer) workerContext.get("server");
        HmcPartition partition = (HmcPartition) workerContext.get("partition");
        HmcProfile profile = (HmcProfile) workerContext.get("profile");
        String hmcHost = (String) workerContext.get("hmcHost");
        String hmcUser = (String) workerContext.get("hmcUser");
        String hmcPasd = (String) workerContext.get("hmcPasd");
        String ipAddr = (String) workerContext.get("ipAddr");
        String gateway = (String) workerContext.get("gateway");
        String netmask = (String) workerContext.get("netmask");
        if (workerResult.isFailed()) {
            String workerResultErrMsg = getErrMsgFromWorkerResult(workerResult);
            logger.error("worker对nim服务器执行配置任务失败：{}", workerResultErrMsg);
            clearInstallStatus(partition.getId(), false, workerResultErrMsg);
            String msg = String.format("分区%s系统安装任务失败：%s", partition.getName(), workerResultErrMsg);
            sendMessage(false, "aix系统安装情况通知", msg, server.getVendorId(), user);
            return;
        }

        Target hmcTarget = new Target(hmcHost, 22, hmcUser, hmcPasd);
        String command = COMMAND2
                .replace("{{nimHost}}", nimServerConfig.getHostname())
                .replace("{{lparIp}}", ipAddr)
                .replace("{{lparMask}}", netmask)
                .replace("{{lparGateway}}", gateway)
                .replace("{{lparName}}", partition.getName())
                .replace("{{profileName}}", profile.getName())
                .replace("{{serverName}}", server.getName());
        Map<String, Object> workerParam = buildWorkerParam(hmcTarget, command);

        // 调用worker
        logger.info("正在调用worker下网络启动任务...");
        logger.debug("--worker--> {}", JSON.toJSONString(workerParam));
        BocloudResult bocloudResult = requestWorker.invoke(workerParam, this::netStartCallback, workerContext);
        logger.debug("--result--< {}]", JSON.toJSONString(bocloudResult));
        if (bocloudResult.isFailed()) {
            logger.info("正在清空nim服务器...");
            clearNim(partition, ipAddr);
            logger.error("对HMC下发网络启动任务失败：{}", bocloudResult.getMessage());
            clearInstallStatus(partition.getId(), false, bocloudResult.getMessage());
            String msg = String.format("分区%s系统安装任务失败：%s", partition.getName(), bocloudResult.getMessage());
            sendMessage(false, "aix系统安装情况通知", msg, server.getVendorId(), user);
            return;
        }

        // 调用worker成功，这里就啥也不干了，等待worker回调
        logger.info("调用worker下发网络启动任务成功，等待worker执行完成后回调...");
    }

    private void netStartCallback(final Map<String, Object> workerContext, BocloudResult workerResult) {
        logger.debug("--callback--< {}", JSON.toJSONString(workerResult));

        RequestContext user = (RequestContext) workerContext.get("context");
        HmcServer server = (HmcServer) workerContext.get("server");
        HmcPartition partition = (HmcPartition) workerContext.get("partition");
        HmcProfile profile = (HmcProfile) workerContext.get("profile");
        String hmcHost = (String) workerContext.get("hmcHost");
        String hmcUser = (String) workerContext.get("hmcUser");
        String hmcPasd = (String) workerContext.get("hmcPasd");
        String ipAddr = (String) workerContext.get("ipAddr");
        String gateway = (String) workerContext.get("gateway");
        String netmask = (String) workerContext.get("netmask");
        if (workerResult.isFailed() || workerResult.getData() == null || workerResult.getData().toString().contains("!EA0")) {
            String workerResultErrMsg = getErrMsgFromWorkerResult(workerResult);
            logger.info("正在清空nim服务器...");
            clearNim(partition, ipAddr);
            logger.error("worker对hmc服务器执行网络启动任务失败：{}", workerResultErrMsg);
            clearInstallStatus(partition.getId(), false, workerResultErrMsg);
            String msg = String.format("分区%s系统安装任务失败：%s", partition.getName(), workerResultErrMsg);
            sendMessage(false, "aix系统安装情况通知", msg, server.getVendorId(), user);
            return;
        }

        // 给用户发消息
        String msg = String.format("分区%s执行网络启动成功, 操作系统自动安装中...", partition.getName());
        sendMessage(true, "aix系统安装情况通知", msg, server.getVendorId(), user);
        logger.info("网络启动成功, 操作系统自动安装中...");

        // 启动一个线程，循环检查系统安装进度
        ListenableFuture<Boolean> future = taskExecutor.submitListenable(() -> checkInstallStatus(partition, server, user));
        future.addCallback(new CheckInstallStatusCallBack(partition, server, user));
        futureHolder.put(partition.getId(), future);
    }

    private class CheckInstallStatusCallBack implements ListenableFutureCallback<Boolean> {

        private final HmcPartition partition;
        private final HmcServer server;
        private final RequestContext context;

        private CheckInstallStatusCallBack(HmcPartition partition, HmcServer server, RequestContext context) {
            this.partition = partition;
            this.server = server;
            this.context = context;
        }

        @Override
        public void onFailure(Throwable e) {
            // 清空futureHolder
            futureHolder.remove(partition.getId());
            if (e instanceof CancellationException) {
                logger.warn("分区{}系统安装interrupt", partition.getName());
            } else {
                String err = String.format("分区%s系统安装失败：%s", partition.getName(), e.getMessage());
                sendMessage(false, "aix系统安装情况通知", err, server.getVendorId(), context);
                logger.error(err, e);
            }
        }

        @Override
        public void onSuccess(Boolean success) {
            // 清空futureHolder
            futureHolder.remove(partition.getId());
            // 系统安装结束，给用户发个消息
            String msg = String.format("分区%s系统安装%s", partition.getName(), success ? "成功" : "失败");
            sendMessage(success, "aix系统安装情况通知", msg, server.getVendorId(), context);
            if (success) {
                logger.info(msg);
            } else {
                logger.error(msg);
            }
        }
    }

    private void cleanNimCallback(final Map<String, Object> workerContext, BocloudResult workerResult) {
        logger.debug("--callback--< {}", JSON.toJSONString(workerResult));

        HmcPartition partition = (HmcPartition) workerContext.get("partition");
        if (workerResult.isSuccess()) {
            logger.info("成功清理分区{}的安装任务", partition.getName());
        } else {
            String workerResultErrorMessage = getErrMsgFromWorkerResult(workerResult);
            logger.error("清理分区{}的安装任务失败：{}", partition.getName(), workerResultErrorMessage);
        }
    }

    private Boolean checkInstallStatus(HmcPartition partition, HmcServer server, RequestContext context) {
        long start = System.currentTimeMillis();
        int errCnt = 0;
        boolean success = false;
        while (System.currentTimeMillis() - start <= 1000 * 60 * 60 * 5 && errCnt <= 10) {
            if (Thread.currentThread().isInterrupted()) {
                logger.warn("当前线程interrupt，任务提前结束");
                return false;
            }
            // 获取安装进度
            String nimName = getNimNameFromPartitionName(partition.getName());
            String command = STATUS_COMMAND.replace("{{nimName}}", nimName);
            logger.debug("--command--> {}", command);
            String password = new AESEncrptor().encrypt(nimServerConfig.getPassword(), null);
            BocloudResult bocloudResult = ServerHelper.executeBySSH(
                    nimServerConfig.getUsername(), password, nimServerConfig.getHostname(), nimServerConfig.getPort(), command
            );
            logger.debug("--result--< {}]", JSON.toJSONString(bocloudResult));
            if (bocloudResult.isSuccess()) {
                if (bocloudResult.getData().toString().contains("no NIM object")) {
                    logger.error("分区{}在nim服务器中的安装任务丢失", partition.getName());
                    errCnt++;
                } else {
                    JSONObject object = ClassHelper.getJson(bocloudResult.getData().toString(), "=");
                    if (object.get("info") == null && object.get("cpuid") != null &&
                            "ready for a NIM operation".equals(object.get("Cstate")) &&
                            "success".equals(object.get("Cstate_result"))) {
                        success = true;
                        break;
                    } else {
                        // 尚未安装完成，更新下安装进度
                        try {
                            int percent = Integer.parseInt(object.getString("info").split(" ")[2].split("%")[0]);
                            setInstallStatus(partition.getId(), percent);
                            logger.info("分区{}系统安装进度：{}%", partition.getName(), percent);
                        } catch (Exception e) {
                            logger.info("分区{}系统安装进度：{}", partition.getName(), object.getString("info"));
                        }
                    }
                }
            } else {
                logger.error("获取分区{}的安装进度失败：{}", partition.getName(), bocloudResult.getMessage());
                errCnt++;
            }

            try {
                Thread.sleep(10 * 1000);
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            }
        }

        logger.info("正在清空nim服务器...");
        clearNim(partition, partition.getIp());
        clearInstallStatus(partition.getId(), success, success ? "成功" : "失败");
        return success;
    }

    private HmcPartition setInstallStatus(long partitionId, int percent) {
        HmcPartition partition = hmcPartitionRepository.getById(partitionId);
        if (partition == null) {
            throw new NullPointerException("未找到id为" + partitionId + "的逻辑分区");
        }

        JSONObject prop = new JSONObject();
        prop.put("info", "系统安装中");
        prop.put("percent", percent);

        partition.setStatus(HmcStatus.PARTITION.INSTALLING.getName());
        partition.setProps(prop.toJSONString());
        hmcPartitionRepository.update(partition);
        return partition;
    }

    private HmcPartition setIp(long partitionId, String ip) {
        HmcPartition partition = hmcPartitionRepository.getById(partitionId);
        if (partition == null) {
            throw new NullPointerException("未找到id为" + partitionId + "的逻辑分区");
        }

        partition.setIp(ip);
        hmcPartitionRepository.update(partition);
        return partition;
    }

    private void clearInstallStatus(long partitionId, boolean result, String info) {
        Optional.ofNullable(hmcPartitionRepository.getById(partitionId)).ifPresent(partition -> {
            JSONObject prop = new JSONObject();
            prop.put("result", result);
            prop.put("info", info);
            partition.setProps(prop.toString());
            hmcPartitionRepository.update(partition);

            HmcServer server = hmcServerRepository.getServerById(partition.getServerId());
            CloudVendor cloudVendor = cloudVendorRepository.query(server.getVendorId());
            BocloudResult bocloudResult = hmcPartitionDriver.getPartitionAttribute(cloudVendor.getUuid(), partition.getUuid(), false);
            if (bocloudResult.isSuccess()) {
                LogicalPartitionEntry partitionEntry = (LogicalPartitionEntry) bocloudResult.getData();
                partition.setIp(partitionEntry.getContent().getLogicalPartition().getResourceMonitoringIPAddress());
                partition.setStatus(partitionEntry.getContent().getLogicalPartition().getPartitionState());
                hmcPartitionRepository.update(partition);
            }
        });
    }

    private void clearNim(HmcPartition partition, String ip) {
        String password = new AESEncrptor().encrypt(nimServerConfig.getPassword(), null);
        Target nimTarget = new Target(
                nimServerConfig.getHostname(), nimServerConfig.getPort(),
                nimServerConfig.getUsername(), password
        );
        String nimName = getNimNameFromPartitionName(partition.getName());
        String command = COMMAND3.replace("{{nimName}}", nimName).replace("{{lparIp}}", ip);
        Map<String, Object> workerParam = buildWorkerParam(nimTarget, command);
        JSONObject workerContext = new JSONObject();
        workerContext.put("partition", partition);
        logger.debug("--worker--> {}", JSON.toJSONString(workerParam));
        BocloudResult bocloudResult = requestWorker.invoke(workerParam, this::cleanNimCallback, workerContext);
        logger.debug("--result--> {}", JSON.toJSONString(bocloudResult));
    }

    @Override
    public BocloudResult install(HmcNimInstallParams params, RequestContext context) {
        Long partitionId = params.getId();
        Long profileId = params.getLparProfileId();
        String ipAddr = params.getIpAddr();
        String gateway = params.getGateway();
        String netmask = params.getNetmask();

        HmcPartition partition = hmcPartitionRepository.getById(partitionId);
        if (Objects.isNull(partition)) {
            return new BocloudResult(false, "分区不存在");
        }
        HmcProfile profile = hmcProfileRepository.queryById(profileId);
        if (Objects.isNull(profile)) {
            return new BocloudResult(false, "概要文件不存在");
        }
        HmcServer server = hmcServerRepository.getServerById(partition.getServerId());
        if (Objects.isNull(server)) {
            return new BocloudResult(false, "服务器不存在");
        }
        CloudVendor vendor = cloudVendorRepository.query(server.getVendorId());
        if (Objects.isNull(vendor)) {
            return new BocloudResult(false, "云平台不存在");
        }

        List<HmcProfileNetworkAdapter> hmcProfileNetworkAdapters = hmcProfileNetworkAdapterRepository.queryByProfileId(profileId);
        if (hmcProfileNetworkAdapters.size() == 0) {
            return new BocloudResult(false, "所选概要文件尚未定义虚拟网络适配器!");
        }

        if (!checkIScsiAndFcForInstall(partition, profile)) {
            return new BocloudResult(false, "分区{" + partition.getName() + "}的概要文件{" + profile.getName() + "}" +
                    "里面定义的虚拟FC适配器或虚拟ISCSI适配器不满足系统安装要求(满足以下任意一个)：\r\n " +
                    "1. 概要文件中存在一个或多个虚拟光纤适配器，且在VIOS存在与之相匹配的光纤适配器\r\n" +
                    "2. 概要文件中存在一个或多个虚拟ISCSI适配器，且在VIOS存在与之相匹配的ISCSI适配器，且挂载了虚拟盘或物理卷"
            );
        }

        Future<BocloudResult> future = taskExecutor.submit(() -> {
            // 将安装状态及安装进度更新到数据库
            setInstallStatus(partition.getId(), 0);
            // 将安装的ip地址更新到数据库
            setIp(partition.getId(), ipAddr);
            // 将安装的ip放回到partition中
            partition.setIp(ipAddr);

            // 准备调用worker执行上下文
            JSONObject workerContext = new JSONObject();
            workerContext.put("context", context);
            workerContext.put("server", server);
            workerContext.put("partition", partition);
            workerContext.put("profile", profile);
            JSONObject authentication = JSONObject.parseObject(vendor.getAuthentication());
            try {
                workerContext.put("hmcHost", new URL(authentication.getString("address")).getHost());
            } catch (MalformedURLException e) {
                workerContext.put("hmcHost", authentication.getString("address"));
            }
            workerContext.put("hmcUser", authentication.getString("account"));
            String hmcPasd;
            try {
                hmcPasd = new DESEncrptor().decrypt(authentication.getString("password"), "BocloudCMP");
                hmcPasd = new AESEncrptor().encrypt(hmcPasd, null);
            } catch (Exception e) {
                hmcPasd = authentication.getString("password");
            }
            workerContext.put("hmcPasd", hmcPasd);
            workerContext.put("ipAddr", ipAddr);
            workerContext.put("gateway", gateway);
            workerContext.put("netmask", netmask);

            // 准备调用worker的参数
            String nimName = getNimNameFromPartitionName(partition.getName());
            String command = COMMAND1
                    .replace("{{lparName}}", partition.getName())
                    .replace("{{lparIp}}", ipAddr)
                    .replace("{{nimName}}", nimName)
                    .replace("{{nimNet}}", nimServerConfig.getNetwork())
                    .replace("{{nimMksysb}}", nimServerConfig.getMksysb())
                    .replace("{{nimSpot}}", nimServerConfig.getSpot())
                    .replace("{{nimBosinst}}", nimServerConfig.getBosinst());
            String password = new AESEncrptor().encrypt(nimServerConfig.getPassword(), null);
            Target nimServerTarget = new Target(nimServerConfig.getHostname(), nimServerConfig.getPort(), nimServerConfig.getUsername(), password);
            Map<String, Object> workerParam = buildWorkerParam(nimServerTarget, command);

            // 调用worker
            logger.info("正在调用worker下发配置任务...");
            logger.debug("--worker--> {}", JSON.toJSONString(workerParam));
            BocloudResult bocloudResult = requestWorker.invoke(workerParam, this::serverConfigCallback, workerContext);
            logger.debug("--result--< {}]", JSON.toJSONString(bocloudResult));
            if (bocloudResult.isFailed()) {
                logger.error("对nim服务器下发配置任务失败：{}", bocloudResult.getMessage());
                clearInstallStatus(partitionId, false, bocloudResult.getMessage());
                String msg = String.format("分区%s系统安装任务失败：%s", partition.getName(), bocloudResult.getMessage());
                sendMessage(false, "aix系统安装情况通知", msg, server.getVendorId(), context);
                return new BocloudResult(false, bocloudResult.getMessage());
            }

            // 调用worker成功，这里就啥也不干了，等待worker回调
            logger.info("调用worker下发配置任务成功，等待worker执行完成后回调...");
            return new BocloudResult(true, "执行成功");
        });
        return new BocloudResult(true, future, "任务已经下发，正在执行...");
    }

    @Override
    public BocloudResult process(long id, RequestContext context) {
        HmcPartition partition = hmcPartitionRepository.getById(id);
        if (Objects.isNull(partition)) {
            return new BocloudResult(false, "分区不存在");
        }
        if (!partition.getStatus().equals(HmcStatus.PARTITION.INSTALLING.getName())) {
            return new BocloudResult(false, "分区并未装机");
        }
        return new BocloudResult(true, JSON.parseObject(partition.getProps()), "执行成功");
    }

    @Override
    public BocloudResult cancel(long id, RequestContext context) {
        HmcPartition partition = hmcPartitionRepository.getById(id);
        if (Objects.isNull(partition)) {
            return new BocloudResult(false, "分区不存在");
        }
        if (!partition.getStatus().equals(HmcStatus.PARTITION.INSTALLING.getName())) {
            return new BocloudResult(false, "分区并未装机");
        }
        Future future = futureHolder.get(partition.getId());
        if (future != null) {
            future.cancel(true);
        }
        futureHolder.remove(partition.getId());
        logger.info("正在清空nim服务器...");
        clearNim(partition, partition.getIp());
        clearInstallStatus(partition.getId(), false, "主动取消安装");
        return new BocloudResult(true, "执行成功");
    }

    @PostConstruct
    public void onApplicationEvent() {
        try {
            LeaderLatchListener leaderLatchListener = new LeaderLatchListener() {
                @Override
                public void isLeader() {
                    logger.info("I'm leader!");
                    RequestContext context = new RequestContext();
                    context.setTarget(1L);
                    // 检查数据库中所有正在安装系统的逻辑分区，并进行循环检测安装进度
                    List<CloudVendor> cloudVendors = cloudVendorRepository.listByType(CloudProvider.HMC.name());
                    for (CloudVendor cloudVendor : cloudVendors) {
                        List<HmcServer> hmcServers = hmcServerRepository.listByVendorId(cloudVendor.getId());
                        for (HmcServer hmcServer : hmcServers) {
                            List<HmcPartition> hmcPartitions = hmcPartitionRepository.listByServerId(hmcServer.getId());
                            for (HmcPartition hmcPartition : hmcPartitions) {
                                if (hmcPartition.getStatus().equals(HmcStatus.PARTITION.INSTALLING.getName())) {
                                    ListenableFuture<Boolean> future = taskExecutor.submitListenable(() -> checkInstallStatus(hmcPartition, hmcServer, context));
                                    future.addCallback(new CheckInstallStatusCallBack(hmcPartition, hmcServer, context));
                                    futureHolder.put(hmcPartition.getId(), future);
                                }
                            }
                        }
                    }
                }

                @Override
                public void notLeader() {
                    logger.info("I'm not leader!");
                    for (Future future : futureHolder.values()) {
                        future.cancel(true);
                    }
                }
            };
            currentService.addListener(leaderLatchListener);
            if (currentService.isLeader()) {
                leaderLatchListener.isLeader();
            }
        } catch (Exception e) {
            logger.error("Nim监控初始化失败：{}", e.getMessage());
        }
    }
}
