package com.bocloud.ims.service.transporter.vendor;

import com.alibaba.fastjson.JSON;
import com.bocloud.cmp.driver.common.LocationDriver;
import com.bocloud.cmp.driver.common.TaskMsgDriver;
import com.bocloud.cmp.entity.enums.BocloudStatus;
import com.bocloud.cmp.entity.enums.NetworkType;
import com.bocloud.cmp.entity.enums.ServerAssetCategory;
import com.bocloud.cmp.model.*;
import com.bocloud.cmp.model.smart.SyncModel;
import com.bocloud.cmp.model.storage.StorageDiskModel;
import com.bocloud.cmp.model.storage.StorageDiskPartitionModel;
import com.bocloud.common.model.BocloudResult;
import com.bocloud.common.model.Pager;
import com.bocloud.common.utils.IDFactory;
import com.bocloud.ims.entity.resource.*;
import com.bocloud.ims.repository.resource.*;
import com.bocloud.ims.service.transporter.ResourceTransporter;
import org.apache.commons.collections.MapUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.http.util.Asserts;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.Assert;

import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

@Component
public class SmartxTransporter implements ResourceTransporter {

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

    @Autowired
    private TaskMsgDriver taskMsgDriver;
    @Autowired
    private ServerRepository serverRepository;
    @Autowired
    private ServerConfigRepository serverConfigRepository;
    @Autowired
    private CloudServerRepository cloudServerRepository;
    @Autowired
    private VolumeRepository volumeRepository;
    @Autowired
    private DiskRepository diskRepository;
    @Autowired
    private DiskPartitionRepository diskPartitionRepository;
    @Autowired
    private SwitcherRepository switcherRepository;
    @Autowired
    private NetworkRepository networkRepository;
    @Autowired
    private SubnetRepository subnetRepository;
    @Autowired
    private PortGroupRepository portGroupRepository;
    @Autowired
    private NetworkCardRepository networkCardRepository;
    @Autowired
    private ServerVolumeRepository serverVolumeRepository;
    @Autowired
    private SnapshotRepository snapshotRepository;
    @Autowired
    private VolumeTemplateRepository volumeTemplateRepository;
    @Autowired
    private ImageRepository imageRepository;
    @Autowired
    private ServerImageRepository serverImageRepository;
    @Autowired
    private TaskMsgRepository taskMsgRepository;
    @Autowired
    private LocationDriver locationDriver;

    @Transactional
    @Override
    public BocloudResult transport(CloudVendor vendor, Long userId) {
        BocloudResult bocloudResult = this.locationDriver.sync(vendor.getUuid());
        if (bocloudResult.isFailed()) {
            return bocloudResult;
        }
        Assert.notNull(bocloudResult.getData(), "数据同步异常！");
        SyncModel syncModel = (SyncModel) bocloudResult.getData();

        logger.info("开始保存Smart-X[{}]的数据...", vendor.getName());
        logger.info("正在保存存储策略数据...");
        this.syncVolumeTemplate(vendor, syncModel.getVolumeTemplateModels(), userId);
        logger.info("正在保存ISO镜像数据...");
        this.syncImage(vendor, syncModel.getImageModels(), userId);
        logger.info("正在保存虚拟卷数据...");
        this.syncVolume(vendor, syncModel.getVolumeModels(), userId);
        logger.info("正在保存主机数据...");
        this.syncHost(vendor, syncModel.getHostModels(), userId);
        logger.info("正在保存虚拟交换机和虚拟网络数据...");
        this.syncSwitcher(vendor, syncModel.getSwitcherModels(), userId);
        logger.info("正在保存云主机和云主机模板数据...");
        this.syncServer(vendor, syncModel.getServerAndServerTemplateModels(), userId);
        logger.info("正在保存模板镜像数据...");
        this.syncServerTemplate(vendor, syncModel.getServerTemplateModels(), userId);
        logger.info("正在保存云主机快照和虚拟卷快照数据...");
        this.syncSnapshot(vendor, syncModel.getServerAndVolumeSnapshotModels(), userId);
        logger.info("正在保存任务数据...");
        this.syncTask(vendor, syncModel.getTaskMsgModels(), userId);
        logger.info("成功保存Smart-X[{}]的数据...", vendor.getName());
        return new BocloudResult(true, "云平台【 " + vendor.getName() + " 】数据同步成功");
    }

    public BocloudResult remove(CloudVendor vendor, Long userId) {
        List<Server> servers = serverRepository.listByVendor(vendor.getId());
        servers.stream().map(Server::getId).forEach(serverConfigRepository::removeBySid);
        serverRepository.removeByVendor(vendor.getId(), userId);

        List<CloudServer> cloudServers = cloudServerRepository.listByVendorId(vendor.getId());
        cloudServers.stream().map(CloudServer::getId).forEach(serverVolumeRepository::removeBySid);
        cloudServers.stream().map(CloudServer::getId).forEach(serverImageRepository::removeBySid);
        cloudServerRepository.removeByVid(vendor.getId(), userId);

        List<Volume> volumes = volumeRepository.listByVendorId(vendor.getId());
        volumes.stream().map(Volume::getId).forEach(serverVolumeRepository::removeByVid);
        volumeRepository.removeByVid(vendor.getId(), userId);

        List<Image> images = imageRepository.listByVendorId(vendor.getId());
        images.stream().map(Image::getId).forEach(serverImageRepository::removeByIid);
        imageRepository.removeByVid(vendor.getId(), userId);

        List<Disk> disks = diskRepository.listByVendor(vendor.getId());
        disks.stream().map(Disk::getId).forEach(diskPartitionRepository::removeByDisk);
        diskRepository.removeByVendorId(vendor.getId());

        switcherRepository.removeByVid(vendor.getId(), userId);
        networkRepository.removeByVid(vendor.getId(), userId);
        subnetRepository.removeByVid(vendor.getId(), userId);
        portGroupRepository.removeByVid(vendor.getId(), userId);
        networkCardRepository.removeByVid(vendor.getId(), userId);
        snapshotRepository.removeByVid(vendor.getId(), userId);
        volumeTemplateRepository.removeByVendor(vendor.getId());
        taskMsgRepository.removeByVendor(vendor.getId());

        return new BocloudResult(true, "success");
    }

    private VendorTaskMsg saveTask(CloudVendor vendor, TaskMsgModel taskMsgModel, Long userId) {
        VendorTaskMsg taskMsg = taskMsgRepository.getByKey(taskMsgModel.getUuid());
        if (taskMsg == null) {
            taskMsg = new VendorTaskMsg();
            this.convert(taskMsgModel, taskMsg);
            taskMsg.setVendorId(vendor.getId());
            taskMsg.setParentId(0L);
            taskMsgRepository.save(taskMsg);
        } else {
            this.convert(taskMsgModel, taskMsg);
            taskMsgRepository.update(taskMsg);
        }

        if ("VM".equals(taskMsg.getTargetType())) {
            CloudServer server = cloudServerRepository.getByInstanceId(taskMsg.getTargetUuid());
            if (server != null) {
                taskMsg.setTargetId(server.getId());
            }
        } else if ("VM_SNAPSHOT".equals(taskMsg.getTargetType())) {
            Snapshot snapshot = snapshotRepository.getBySnapUuid(taskMsg.getTargetUuid());
            if (snapshot != null) {
                taskMsg.setTargetId(snapshot.getId());
            }
        } else if ("VM_TEMPLATE".equals(taskMsg.getTargetType())) {
            CloudServer server = cloudServerRepository.getByInstanceId(taskMsg.getTargetUuid());
            if (server != null) {
                taskMsg.setTargetId(server.getId());
            }
        } else if ("VOLUME".equals(taskMsg.getTargetType())) {
            Volume volume = volumeRepository.getByVolumeUuid(taskMsg.getTargetUuid());
            if (volume != null) {
                taskMsg.setTargetId(volume.getId());
            }
        } else if ("VOLUME_SNAPSHOT".equals(taskMsg.getTargetType())) {
            Snapshot snapshot = snapshotRepository.getBySnapUuid(taskMsg.getTargetUuid());
            if (snapshot != null) {
                taskMsg.setTargetId(snapshot.getId());
            }
        } else if ("HOST".equals(taskMsg.getTargetType())) {
            Server server = serverRepository.queryBy3Ip(taskMsg.getTargetUuid());
            if (server != null) {
                taskMsg.setTargetId(server.getId());
            }
        } else if ("SWITCHER".equals(taskMsg.getTargetType())) {
            Switcher switcher = switcherRepository.getByUuid(taskMsg.getTargetUuid());
            if (switcher != null) {
                taskMsg.setTargetId(switcher.getId());
            }
        }

        List<VendorTaskMsg> subTaskMsgs = this.syncTask(taskMsg, taskMsgModel.getSubTasks(), userId);
        taskMsg.setSubTasks(subTaskMsgs);
        return taskMsg;
    }

    public VendorTaskMsg getTask(CloudVendor vendor, String taskId, Long userId) {
        return this.getTask(vendor, taskId, VendorTaskMsg::getSmartCompleted, userId);
    }

    public VendorTaskMsg getTask(CloudVendor vendor, String taskUuid, Function<VendorTaskMsg, Boolean> breakOut, Long userId) {
        final int interval = 1;
        VendorTaskMsg taskMsg;
        while (true) {
            BocloudResult<TaskMsgModel> bocloudResult = taskMsgDriver.get(taskUuid, vendor.getUuid());
            Assert.isTrue(bocloudResult.isSuccess(), bocloudResult.getMessage());
            TaskMsgModel taskMsgModel = bocloudResult.getData();
            taskMsg = this.saveTask(vendor, taskMsgModel, userId);
            logger.info("任务{}执行状态: {}", taskMsg.getKey(), taskMsg.getStatus());
            if (breakOut.apply(taskMsg)) {
                break;
            }
            try {
                Thread.sleep(interval * 1000L);
            } catch (InterruptedException e) {
                logger.warn("任务{}检测过程被打断", taskMsgModel.getKey());
                Thread.currentThread().interrupt();
                break;
            }
        }

        return taskMsg;
    }

    public List<VolumeTemplate> syncVolumeTemplate(CloudVendor vendor, List<VolumeTemplateModel> driverVolumeTemplates, Long userId) {
        List<VolumeTemplate> dbVolumeTemplates = volumeTemplateRepository.listByVendor(vendor.getId());
        List<VolumeTemplate> finalVolumeTemplates = new ArrayList<>();
        for (VolumeTemplateModel driverVolumeTemplate : driverVolumeTemplates) {
            Optional<VolumeTemplate> dbVolumeTemplateOpt = dbVolumeTemplates.stream()
                    .filter(dbVolumeTemplate -> Objects.equals(driverVolumeTemplate.getUuid(), dbVolumeTemplate.getUuid()))
                    .findFirst();
            VolumeTemplate dbVolumeTemplate;
            if (dbVolumeTemplateOpt.isPresent()) {
                dbVolumeTemplate = dbVolumeTemplateOpt.get();
                this.convert(driverVolumeTemplate, dbVolumeTemplate);
                dbVolumeTemplate.setMenderId(userId);
                dbVolumeTemplate.setGmtModify(new Date());
                volumeTemplateRepository.update(dbVolumeTemplate);
            } else {
                dbVolumeTemplate = new VolumeTemplate();
                this.convert(driverVolumeTemplate, dbVolumeTemplate);
                dbVolumeTemplate.setVendorId(vendor.getId());
                dbVolumeTemplate.setCreatorId(userId);
                dbVolumeTemplate.setGmtCreate(new Date());
                volumeTemplateRepository.save(dbVolumeTemplate);
            }
            finalVolumeTemplates.add(dbVolumeTemplate);
        }
        dbVolumeTemplates.removeAll(finalVolumeTemplates);
        for (VolumeTemplate dbVolumeTemplate : dbVolumeTemplates) {
            volumeTemplateRepository.remove(dbVolumeTemplate.getId());
        }
        return finalVolumeTemplates;
    }

    public List<Image> syncServerTemplate(CloudVendor vendor, List<ServerModel> driverServerTemplates, Long userId) {
        List<Image> dbImages = imageRepository.listByVendorId(vendor.getId());
        dbImages = dbImages.stream().filter(dbImage -> "VM".equals(dbImage.getCatalog())).collect(Collectors.toList());
        List<Image> finalImages = new ArrayList<>();
        for (ServerModel driverServerTemplate : driverServerTemplates) {
            Optional<Image> dbImageOpt = dbImages.stream()
                    .filter(dbImage -> Objects.equals(driverServerTemplate.getTempInstId(), dbImage.getImageUuid()))
                    .findFirst();
            Optional<CloudServer> dbServerOpt = Optional.of(driverServerTemplate).map(ServerModel::getTempInstId).map(cloudServerRepository::getByInstanceId);
            Image dbImage;
            if (dbImageOpt.isPresent()) {
                dbImage = dbImageOpt.get();
                this.convert(driverServerTemplate, dbImage);
                dbServerOpt.map(CloudServer::getId).ifPresent(dbImage::setTemplateId);
                dbImage.setMenderId(userId);
                dbImage.setGmtModify(new Date());
                imageRepository.update(dbImage);
            } else {
                dbImage = new Image();
                this.convert(driverServerTemplate, dbImage);
                dbServerOpt.map(CloudServer::getId).ifPresent(dbImage::setTemplateId);
                dbImage.setVendorId(vendor.getId());
                dbImage.setCreatorId(userId);
                dbImage.setGmtCreate(new Date());
                imageRepository.save(dbImage);
            }
            finalImages.add(dbImage);
        }
        dbImages.removeAll(finalImages);
        for (Image dbImage : dbImages) {
            imageRepository.remove(dbImage.getId(), userId);
        }
        return finalImages;
    }

    public List<Image> syncImage(CloudVendor vendor, List<ImageModel> driverImages, Long userId) {
        List<Image> dbImages = imageRepository.listByVendorId(vendor.getId());
        dbImages = dbImages.stream().filter(dbImage -> "ISO".equals(dbImage.getCatalog())).collect(Collectors.toList());
        List<Image> finalImages = new ArrayList<>();
        for (ImageModel driverImage : driverImages) {
            Optional<Image> dbImageOpt = dbImages.stream()
                    .filter(dbImage -> Objects.equals(driverImage.getImageUuid(), dbImage.getImageUuid()))
                    .findFirst();
            Image dbImage;
            if (dbImageOpt.isPresent()) {
                dbImage = dbImageOpt.get();
                this.convert(driverImage, dbImage);
                dbImage.setMenderId(userId);
                dbImage.setGmtModify(new Date());
                imageRepository.update(dbImage);
            } else {
                dbImage = new Image();
                this.convert(driverImage, dbImage);
                dbImage.setVendorId(vendor.getId());
                dbImage.setCreatorId(userId);
                dbImage.setGmtCreate(new Date());
                imageRepository.save(dbImage);
            }
            finalImages.add(dbImage);
        }
        dbImages.removeAll(finalImages);
        for (Image dbImage : dbImages) {
            imageRepository.remove(dbImage.getId(), userId);
            serverImageRepository.removeByIid(dbImage.getId());
        }
        return finalImages;
    }

    public List<Server> syncHost(CloudVendor vendor, List<HostModel> driverHosts, Long userId) {
        List<Server> dbHosts = serverRepository.listByVendor(vendor.getId());
        List<ServerConfig> dbHostConfigs = new ArrayList<>();
        if (!dbHosts.isEmpty()) {
            List<Long> dbHostIds = dbHosts.stream().map(Server::getId).collect(Collectors.toList());
            dbHostConfigs = serverConfigRepository.listByHostIds(dbHostIds);
        }
        List<Server> finalHosts = new ArrayList<>();
        for (HostModel driverHost : driverHosts) {
            Optional<Server> dbHostOpt = dbHosts.stream()
                    .filter(dbHost -> Objects.equals(driverHost.getUuid(), dbHost.getUuid())).findFirst();
            Server dbHost;
            if (dbHostOpt.isPresent()) {
                dbHost = dbHostOpt.get();
                ServerConfig dbConfig = dbHostConfigs.stream()
                        .filter(config -> dbHost.getId().equals(config.getServerId()))
                        .findFirst().orElseThrow(NullPointerException::new);
                this.convert(driverHost, dbHost, dbConfig);
                dbHost.setMenderId(userId);
                dbHost.setGmtModify(new Date());
                serverRepository.update(dbHost);
                serverConfigRepository.update(dbConfig);
            } else {
                dbHost = new Server();
                ServerConfig dbConfig = new ServerConfig();
                this.convert(driverHost, dbHost, dbConfig);
                dbHost.setVendorId(vendor.getId());
                dbHost.setCreatorId(userId);
                dbHost.setGmtCreate(new Date());
                serverRepository.save(dbHost);
                dbConfig.setServerId(dbHost.getId());
                serverConfigRepository.save(dbConfig);
            }

            finalHosts.add(dbHost);

            this.syncDisk(dbHost, driverHost.getDisks(), userId);
            this.syncNetworkCard(dbHost, driverHost.getNetcards(), userId);
        }
        dbHosts.removeAll(finalHosts);
        for (Server dbHost : dbHosts) {
            serverRepository.remove(dbHost.getId(), userId);
            serverConfigRepository.removeBySid(dbHost.getId());
            diskRepository.removeByServerId(dbHost.getId());
            networkCardRepository.removeByHost(dbHost.getId(), "PM", userId);
        }
        return finalHosts;
    }

    public List<Disk> syncDisk(Server host, List<StorageDiskModel> driverDisks, Long userId) {
        List<Disk> dbDisks = diskRepository.listByServer(host.getId());
        List<Disk> finalDisks = new ArrayList<>();
        for (StorageDiskModel driverDisk : driverDisks) {
            Optional<Disk> dbDiskOpt = dbDisks.stream()
                    .filter(dbDisk -> Objects.equals(driverDisk.getDiskUuid(), dbDisk.getDiskUuid()))
                    .findFirst();
            Disk dbDisk;
            if (dbDiskOpt.isPresent()) {
                dbDisk = dbDiskOpt.get();
                this.convert(driverDisk, dbDisk);
                dbDisk.setMenderId(userId);
                dbDisk.setGmtModify(new Date());
                diskRepository.update(dbDisk);
            } else {
                dbDisk = new Disk();
                this.convert(driverDisk, dbDisk);
                dbDisk.setServerId(host.getId());
                dbDisk.setVendorId(host.getVendorId());
                dbDisk.setCreatorId(userId);
                dbDisk.setGmtCreate(new Date());
                diskRepository.save(dbDisk);
            }
            finalDisks.add(dbDisk);

            this.syncPartition(dbDisk, driverDisk.getPartitions(), userId);
        }
        dbDisks.removeAll(finalDisks);
        for (Disk dbDisk : dbDisks) {
            diskRepository.remove(dbDisk.getId());
        }
        return finalDisks;
    }

    public List<DiskPartition> syncPartition(Disk disk, List<StorageDiskPartitionModel> driverPartitions, Long userId) {
        List<DiskPartition> dbPartitions = diskPartitionRepository.listByDisk(disk.getId());
        List<DiskPartition> finalPartitions = new ArrayList<>();
        for (StorageDiskPartitionModel driverPartition : driverPartitions) {
            Optional<DiskPartition> dbPartitionOpt = dbPartitions.stream()
                    .filter(dbPartition -> Objects.equals(driverPartition.getMountPath(), dbPartition.getMountPath()))
                    .findFirst();
            if (dbPartitionOpt.isPresent()) {
                DiskPartition dbPartition = dbPartitionOpt.get();
                this.convert(driverPartition, dbPartition);
                dbPartition.setMenderId(userId);
                dbPartition.setGmtModify(new Date());
                diskPartitionRepository.update(dbPartition);
                finalPartitions.add(dbPartition);
            } else {
                DiskPartition dbDisk = new DiskPartition();
                this.convert(driverPartition, dbDisk);
                dbDisk.setDiskId(disk.getId());
                dbDisk.setCreatorId(userId);
                dbDisk.setGmtCreate(new Date());
                diskPartitionRepository.save(dbDisk);
                finalPartitions.add(dbDisk);
            }
        }
        dbPartitions.removeAll(finalPartitions);
        for (DiskPartition dbPartition : dbPartitions) {
            diskPartitionRepository.remove(dbPartition.getId());
        }
        return finalPartitions;
    }

    public List<NetworkCard> syncNetworkCard(Server host, List<NetworkCardModel> driverNetworkCards, Long userId) {
        List<NetworkCard> dbNetworkCards = networkCardRepository.listByHost(host.getId(), "PM");
        List<NetworkCard> finalNetworkCards = new ArrayList<>();
        for (NetworkCardModel driverNetworkCard : driverNetworkCards) {
            Optional<NetworkCard> dbNetworkCardOpt = dbNetworkCards.stream()
                    .filter(dbNetworkCard -> Objects.equals(driverNetworkCard.getMac(), dbNetworkCard.getMac())).findFirst();
            if (dbNetworkCardOpt.isPresent()) {
                NetworkCard dbNetworkCard = dbNetworkCardOpt.get();
                this.convert(driverNetworkCard, dbNetworkCard);
                dbNetworkCard.setMenderId(userId);
                dbNetworkCard.setGmtModify(new Date());
                networkCardRepository.update(dbNetworkCard);
                finalNetworkCards.add(dbNetworkCard);
            } else {
                NetworkCard dbNetworkCard = new NetworkCard();
                this.convert(driverNetworkCard, dbNetworkCard);
                dbNetworkCard.setCategory("PM");
                dbNetworkCard.setHostId(host.getId());
                dbNetworkCard.setVendorId(host.getVendorId());
                dbNetworkCard.setCreatorId(userId);
                dbNetworkCard.setGmtCreate(new Date());
                networkCardRepository.save(dbNetworkCard);
                finalNetworkCards.add(dbNetworkCard);
            }
        }
        dbNetworkCards.removeAll(finalNetworkCards);
        for (NetworkCard dbNetworkCard : dbNetworkCards) {
            networkCardRepository.remove(dbNetworkCard.getId(), userId);
        }
        return finalNetworkCards;
    }

    public List<NetworkCard> syncNetworkCard(CloudServer server, List<NetworkCardModel> driverNetworkCards, Long userId) {
        List<NetworkCard> dbNetworkCards = networkCardRepository.listByHost(server.getId(), "VM");
        List<NetworkCard> finalNetworkCards = new ArrayList<>();
        for (NetworkCardModel driverNetworkCard : driverNetworkCards) {
            Optional<NetworkCard> dbNetworkCardOpt = dbNetworkCards.stream()
                    .filter(dbNetworkCard -> Objects.equals(driverNetworkCard.getMac(), dbNetworkCard.getMac())).findFirst();
            Optional<Network> networkOpt = Optional.of(driverNetworkCard)
                    .map(NetworkCardModel::getNetworkUuid).map(networkRepository::getByNetId);
            if (dbNetworkCardOpt.isPresent()) {
                NetworkCard dbNetworkCard = dbNetworkCardOpt.get();
                this.convert(driverNetworkCard, dbNetworkCard);
                networkOpt.map(Network::getNetworkUuid).ifPresent(dbNetworkCard::setSubnetUuid);
                networkOpt.map(Network::getName).ifPresent(dbNetworkCard::setName);
                dbNetworkCard.setMenderId(userId);
                dbNetworkCard.setGmtModify(new Date());
                networkCardRepository.update(dbNetworkCard);
                finalNetworkCards.add(dbNetworkCard);
            } else {
                NetworkCard dbNetworkCard = new NetworkCard();
                this.convert(driverNetworkCard, dbNetworkCard);
                networkOpt.map(Network::getNetworkUuid).ifPresent(dbNetworkCard::setSubnetUuid);
                networkOpt.map(Network::getName).ifPresent(dbNetworkCard::setName);
                dbNetworkCard.setCategory("VM");
                dbNetworkCard.setHostId(server.getId());
                dbNetworkCard.setVendorId(server.getVendorId());
                dbNetworkCard.setCreatorId(userId);
                dbNetworkCard.setGmtCreate(new Date());
                networkCardRepository.save(dbNetworkCard);
                finalNetworkCards.add(dbNetworkCard);
            }
        }
        dbNetworkCards.removeAll(finalNetworkCards);
        for (NetworkCard dbNetworkCard : dbNetworkCards) {
            networkCardRepository.remove(dbNetworkCard.getId(), userId);
        }
        return finalNetworkCards;
    }

    public List<Switcher> syncSwitcher(CloudVendor vendor, List<SwitcherModel> driverSwitchers, Long userId) {
        List<Switcher> dbSwitchers = switcherRepository.listByVid(vendor.getId());
        List<Switcher> finalSwitchers = new ArrayList<>();
        for (SwitcherModel driverSwitcher : driverSwitchers) {
            Optional<Switcher> dbSwitcherOpt = dbSwitchers.stream()
                    .filter(dbSwitcher -> Objects.equals(driverSwitcher.getUuid(), dbSwitcher.getUuid())).findFirst();
            Switcher dbSwitcher;
            if (dbSwitcherOpt.isPresent()) {
                dbSwitcher = dbSwitcherOpt.get();
                this.convert(driverSwitcher, dbSwitcher);
                dbSwitcher.setMenderId(userId);
                dbSwitcher.setGmtModify(new Date());
                switcherRepository.update(dbSwitcher);
            } else {
                dbSwitcher = new Switcher();
                this.convert(driverSwitcher, dbSwitcher);
                dbSwitcher.setVendorId(vendor.getId());
                dbSwitcher.setCreatorId(userId);
                dbSwitcher.setGmtCreate(new Date());
                switcherRepository.save(dbSwitcher);
            }
            finalSwitchers.add(dbSwitcher);

            this.syncSwitcherUsedNetworkCard(dbSwitcher, driverSwitcher.getHostNetworkCards());
            this.syncNetwork(dbSwitcher, driverSwitcher.getNetworks(), userId);
        }
        dbSwitchers.removeAll(finalSwitchers);
        for (Switcher dbSwitcher : dbSwitchers) {
            switcherRepository.remove(dbSwitcher.getId(), userId);
            List<NetworkCard> networkCards = networkCardRepository.listBySwitcher(dbSwitcher.getId(), "PM");
            for (NetworkCard networkCard : networkCards) {
                networkCard.setSwitcherId(null);
                networkCardRepository.update(networkCard);
            }
            networkRepository.removeBySwitchId(dbSwitcher.getId(), userId);
            portGroupRepository.removeBySwitcher(dbSwitcher.getId(), userId);
        }
        return finalSwitchers;
    }

    public void syncSwitcherUsedNetworkCard(Switcher switcher, Map<String, List<String>> hostNetworkCards) {
        if (MapUtils.isEmpty(hostNetworkCards)) {
            return;
        }
        for (Map.Entry<String, List<String>> hostNetworkCardEntry : hostNetworkCards.entrySet()) {
            String hostUuid = hostNetworkCardEntry.getKey();
            List<String> networkCardNames = hostNetworkCardEntry.getValue();

            Server host = serverRepository.getByUuid(hostUuid);
            Asserts.notNull(host, "未找到uuid为" + hostUuid + "的主机");
            List<NetworkCard> networkCards = networkCardRepository.listByHost(host.getId(), "PM");
            networkCards = networkCards.stream()
                    .filter(n -> networkCardNames.contains(n.getName())).collect(Collectors.toList());
            for (NetworkCard networkCard : networkCards) {
                networkCard.setSwitcherId(switcher.getId());
                networkCardRepository.update(networkCard);
            }
        }
    }

    public List<Network> syncNetwork(Switcher switcher, List<NetworkModel> driverNetworks, Long userId) {
        List<Network> dbNetworks = networkRepository.listBySwitcher(switcher.getId());
        List<PortGroup> dbPortGroups = portGroupRepository.listBySwitcher(switcher.getId());
        List<Network> finalNetworks = new ArrayList<>();
        List<PortGroup> finalPortGroups = new ArrayList<>();
        for (NetworkModel driverNetwork : driverNetworks) {
            Optional<Network> dbNetworkOpt = dbNetworks.stream()
                    .filter(dbNetwork -> Objects.equals(driverNetwork.getNetworkUuid(), dbNetwork.getNetworkUuid()))
                    .findFirst();
            Optional<PortGroup> dbPortGroupOpt = dbPortGroups.stream()
                    .filter(dbPortGroup -> Objects.equals(driverNetwork.getNetworkUuid(), dbPortGroup.getGroupVal()))
                    .findFirst();
            Network dbNetwork;
            Subnet dbSubnet;
            PortGroup dbPortGroup = new PortGroup();
            if (dbNetworkOpt.isPresent()) {
                dbNetwork = dbNetworkOpt.get();
                dbSubnet = subnetRepository.listByNetwork(dbNetwork.getId()).get(0);
                if (dbPortGroupOpt.isPresent()) {
                    dbPortGroup = dbPortGroupOpt.get();
                }
                this.convert(driverNetwork, dbNetwork, dbSubnet, dbPortGroup);
                dbNetwork.setMenderId(userId);
                dbNetwork.setGmtModify(new Date());
                networkRepository.update(dbNetwork);
                dbSubnet.setMenderId(userId);
                dbSubnet.setGmtModify(new Date());
                subnetRepository.update(dbSubnet);
                dbPortGroup.setMenderId(userId);
                dbPortGroup.setGmtModify(new Date());
                portGroupRepository.update(dbPortGroup);
            } else {
                dbNetwork = new Network();
                dbSubnet = new Subnet();
                dbPortGroup = new PortGroup();
                this.convert(driverNetwork, dbNetwork, dbSubnet, dbPortGroup);
                dbNetwork.setVendorId(switcher.getVendorId());
                dbNetwork.setSwitcherId(switcher.getId());
                dbNetwork.setCreatorId(userId);
                dbNetwork.setGmtCreate(new Date());
                networkRepository.save(dbNetwork);
                dbSubnet.setVendorId(switcher.getVendorId());
                dbSubnet.setNetworkId(dbNetwork.getId());
                dbSubnet.setCreatorId(userId);
                dbSubnet.setGmtCreate(new Date());
                subnetRepository.save(dbSubnet);
                dbPortGroup.setVendorId(switcher.getVendorId());
                dbPortGroup.setSwitcherId(switcher.getId());
                dbPortGroup.setCreatorId(userId);
                dbPortGroup.setGmtCreate(new Date());
                portGroupRepository.save(dbPortGroup);
            }
            finalNetworks.add(dbNetwork);
            finalPortGroups.add(dbPortGroup);
        }
        dbNetworks.removeAll(finalNetworks);
        dbPortGroups.removeAll(finalPortGroups);
        for (Network dbNetwork : dbNetworks) {
            networkRepository.remove(dbNetwork.getId(), userId);
            subnetRepository.removeByNetworkId(dbNetwork.getId(), userId);
        }
        for (PortGroup dbPortGroup : dbPortGroups) {
            portGroupRepository.remove(dbPortGroup.getId(), userId);
        }
        return finalNetworks;
    }

    public List<CloudServer> syncServer(CloudVendor vendor, List<ServerModel> driverServers, Long userId) {
        driverServers.sort(Comparator.comparing(ServerModel::getIsTemplate));
        List<CloudServer> dbServers = cloudServerRepository.listByVidAndIsTemplate(vendor.getId(), null);
        List<CloudServer> finalServers = new ArrayList<>();
        for (ServerModel driverServer : driverServers) {
            Optional<CloudServer> dbServerOpt;
            if (driverServer.getIsTemplate()) {
                dbServerOpt = dbServers.stream().filter(dbServer -> Objects.equals(driverServer.getTempInstId(), dbServer.getInstanceId())).findFirst();
            } else {
                dbServerOpt = dbServers.stream().filter(dbServer -> Objects.equals(driverServer.getInstanceId(), dbServer.getInstanceId())).findFirst();
            }
            Optional<Server> dbHostOpt = Optional.of(driverServer).map(ServerModel::getServerVal).map(serverRepository::queryBy3Ip);
            CloudServer dbServer;
            if (dbServerOpt.isPresent()) {
                dbServer = dbServerOpt.get();
                this.convert(driverServer, dbServer);
                dbHostOpt.map(Server::getId).ifPresent(dbServer::setHostId);
                dbServer.setMenderId(userId);
                dbServer.setGmtModify(new Date());
                cloudServerRepository.update(dbServer);
            } else {
                dbServer = new CloudServer();
                this.convert(driverServer, dbServer);
                dbHostOpt.map(Server::getId).ifPresent(dbServer::setHostId);
                dbServer.setVendorId(vendor.getId());
                dbServer.setVendorType(vendor.getType());
                dbServer.setCreatorId(userId);
                dbServer.setGmtCreate(new Date());
                cloudServerRepository.save(dbServer);
            }
            finalServers.add(dbServer);

            this.syncNetworkCard(dbServer, driverServer.getNetworkCards(), userId);

            List<VolumeModel> imageModels = driverServer.getDiskDevices().stream()
                    .filter(d -> "cdrom".equals(d.getType())).collect(Collectors.toList());
            this.syncServerImage(dbServer, imageModels, userId);

            List<VolumeModel> volumeModels = driverServer.getDiskDevices().stream()
                    .filter(d -> "disk".equals(d.getType())).collect(Collectors.toList());
            this.syncServerVolume(dbServer, volumeModels, userId);
        }
        dbServers.removeAll(finalServers);
        Pager pager = new Pager(1, Integer.MAX_VALUE, Collections.emptyList(), Collections.emptyMap());
        for (CloudServer dbServer : dbServers) {
            cloudServerRepository.remove(dbServer.getId(), userId);
            networkCardRepository.removeByHost(dbServer.getId(), "VM", userId);
            List<Volume> volumes = volumeRepository.listVolumeOfCloudServerAttached(dbServer.getId(), pager);
            volumes.stream().filter(volume -> volume.getMultiattach() == false || !volume.getMultiattach())
                    .forEach(volume -> volumeRepository.remove(volume.getId(), userId));
            serverVolumeRepository.removeBySid(dbServer.getId());
            List<Image> images = imageRepository.listImageOfCloudServerAttached(dbServer.getId(), pager);
            images.stream().filter(image -> "HIDE".equals(image.getCatalog()))
                    .forEach(image -> imageRepository.remove(image.getId(), userId));
            serverImageRepository.removeBySid(dbServer.getId());
        }
        return finalServers;
    }

    public List<Volume> syncVolume(CloudVendor vendor, List<VolumeModel> driverVolumes, Long userId) {
        List<Volume> dbVolumes = volumeRepository.listByVendorId(vendor.getId());
        List<Volume> finalVolumes = new ArrayList<>();
        for (VolumeModel driverVolume : driverVolumes) {
            Optional<Volume> dbVolumeOpt = dbVolumes.stream()
                    .filter(dbVolume -> Objects.equals(driverVolume.getVolumeUuid(), dbVolume.getVolumeUuid())).findFirst();
            Optional<VolumeTemplate> dbVolumeTemplateOpt = Optional.of(driverVolume)
                    .map(VolumeModel::getVolumeTemplateUuid).map(volumeTemplateRepository::getByUuid);
            Volume dbVolume;
            if (dbVolumeOpt.isPresent()) {
                dbVolume = dbVolumeOpt.get();
                this.convert(driverVolume, dbVolume);
                dbVolume.setMenderId(userId);
                dbVolume.setGmtModify(new Date());
                dbVolumeTemplateOpt.map(VolumeTemplate::getId).ifPresent(dbVolume::setTemplateId);
                volumeRepository.update(dbVolume);
            } else {
                dbVolume = new Volume();
                this.convert(driverVolume, dbVolume);
                dbVolume.setVendorId(vendor.getId());
                dbVolume.setCreatorId(userId);
                dbVolume.setGmtCreate(new Date());
                dbVolumeTemplateOpt.map(VolumeTemplate::getId).ifPresent(dbVolume::setTemplateId);
                volumeRepository.save(dbVolume);
            }
            finalVolumes.add(dbVolume);
        }
        dbVolumes.removeAll(finalVolumes);
        for (Volume dbVolume : dbVolumes) {
            volumeRepository.remove(dbVolume.getId(), userId);
            serverVolumeRepository.removeByVid(dbVolume.getId());
        }
        return finalVolumes;
    }

    public List<VendorTaskMsg> syncTask(CloudVendor vendor, List<TaskMsgModel> driverTaskMsgs, Long userId) {
        List<VendorTaskMsg> dbTaskMsgs = taskMsgRepository.listRootByVendorId(vendor.getId());
        List<VendorTaskMsg> finalTaskMsgs = new ArrayList<>();
        for (TaskMsgModel driverTaskMsg : driverTaskMsgs) {
            VendorTaskMsg dbTaskMsg = this.saveTask(vendor, driverTaskMsg, userId);
            finalTaskMsgs.add(dbTaskMsg);
        }
        List<String> finalTaskMsgUuids = finalTaskMsgs.stream().map(VendorTaskMsg::getKey).collect(Collectors.toList());
        dbTaskMsgs.removeIf(t -> finalTaskMsgUuids.contains(t.getKey()));
        for (VendorTaskMsg dbTaskMsg : dbTaskMsgs) {
            cloudServerRepository.remove(dbTaskMsg.getId(), userId);
        }
        return finalTaskMsgs;
    }

    public List<VendorTaskMsg> syncTask(VendorTaskMsg taskMsg, List<TaskMsgModel> driverTasks, Long userId) {
        List<VendorTaskMsg> dbTaskMsgs = taskMsgRepository.listByParent(taskMsg.getId());
        List<VendorTaskMsg> finalTaskMsgs = new ArrayList<>();
        for (TaskMsgModel driverTaskMsg : driverTasks) {
            Optional<VendorTaskMsg> dbTaskMsgOpt = dbTaskMsgs.stream()
                    .filter(dbTaskMsg -> Objects.equals(driverTaskMsg.getUuid(), dbTaskMsg.getKey())).findFirst();
            VendorTaskMsg dbTaskMsg;
            if (dbTaskMsgOpt.isPresent()) {
                dbTaskMsg = dbTaskMsgOpt.get();
                this.convert(driverTaskMsg, dbTaskMsg);
                taskMsgRepository.update(dbTaskMsg);
            } else {
                dbTaskMsg = new VendorTaskMsg();
                this.convert(driverTaskMsg, dbTaskMsg);
                dbTaskMsg.setVendorId(taskMsg.getVendorId());
                dbTaskMsg.setParentId(taskMsg.getId());
                taskMsgRepository.save(dbTaskMsg);
            }
            finalTaskMsgs.add(dbTaskMsg);
        }
        dbTaskMsgs.removeAll(finalTaskMsgs);
        for (VendorTaskMsg dbTaskMsg : dbTaskMsgs) {
            taskMsgRepository.remove(dbTaskMsg.getId());
        }
        return finalTaskMsgs;
    }

    public void syncServerVolume(CloudServer server, List<VolumeModel> driverVolumes, Long userId) {
        List<Volume> dbVolumes = volumeRepository.listVolumeByServerId(server.getId());
        List<Volume> finalVolumes = new ArrayList<>();
        long serverDiskCapacity = 0;
        for (VolumeModel driverVolume : driverVolumes) {
            Optional<Volume> dbVolumeOpt = dbVolumes.stream()
                    .filter(dbVolume -> Objects.equals(driverVolume.getPath(), dbVolume.getPath()))
                    .findFirst();
            Volume dbVolume;
            if (dbVolumeOpt.isPresent()) {
                dbVolume = dbVolumeOpt.get();
                ServerVolume dbServerVolume = serverVolumeRepository.getByServerIdAndVolumeId(server.getId(), dbVolume.getId());
                dbServerVolume.setBootIndex(driverVolume.getBootIndex());
                dbServerVolume.setBus(driverVolume.getBus());
                dbServerVolume.setDisabled(driverVolume.getDisabled());
                dbServerVolume.setDiffKey(driverVolume.getKey());
                dbServerVolume.setTemplateUuid(driverVolume.getTemplateUuid());
                dbServerVolume.setVolumeSize(driverVolume.getBlockCount());
                serverVolumeRepository.update(dbServerVolume);
                finalVolumes.add(dbVolume);
                serverDiskCapacity += dbVolume.getBlockCount() * dbVolume.getBlockSize();
            } else {
                dbVolume = volumeRepository.getByPath(server.getVendorId(), driverVolume.getPath());
                if (dbVolume == null) {
                    dbVolume = new Volume();
                    dbVolume.setVendorId(server.getVendorId());
                    dbVolume.setName("未定义虚拟卷");
                    dbVolume.setMultiattach(false);
                    dbVolume.setVolumeUuid(IDFactory.instance().uuid());
                    dbVolume.setType("KVM_VOLUME_HIDE");
                    dbVolume.setPath(driverVolume.getPath());
                    dbVolume.setDevice(driverVolume.getPath());
                    dbVolume.setSize(driverVolume.getSize());
                    dbVolume.setBlockSize(driverVolume.getBlockSize());
                    dbVolume.setBlockCount(driverVolume.getBlockCount());
                    Optional.of(driverVolume).map(VolumeModel::getVolumeTemplateUuid)
                            .map(volumeTemplateRepository::getByUuid).map(VolumeTemplate::getId)
                            .ifPresent(dbVolume::setTemplateId);
                    dbVolume.setGmtCreate(new Date());
                    dbVolume.setCreatorId(userId);
                    volumeRepository.save(dbVolume);
                }

                ServerVolume dbServerVolume = new ServerVolume();
                dbServerVolume.setServerId(server.getId());
                dbServerVolume.setVolumeId(dbVolume.getId());
                dbServerVolume.setBootIndex(driverVolume.getBootIndex());
                dbServerVolume.setBus(driverVolume.getBus());
                dbServerVolume.setDisabled(driverVolume.getDisabled());
                dbServerVolume.setDiffKey(driverVolume.getKey());
                dbServerVolume.setTemplateUuid(driverVolume.getTemplateUuid());
                dbServerVolume.setVolumeSize(driverVolume.getBlockCount());
                serverVolumeRepository.save(dbServerVolume);

                dbVolume.setStatus("IN-USE");
                volumeRepository.update(dbVolume);

                finalVolumes.add(dbVolume);
                serverDiskCapacity += dbVolume.getBlockCount() * dbVolume.getBlockSize();
            }
        }
        server.setDisk((int) (serverDiskCapacity / 1024L / 1024L / 1024L));
        cloudServerRepository.update(server);
        dbVolumes.removeAll(finalVolumes);
        for (Volume dbVolume : dbVolumes) {
            serverVolumeRepository.remove(dbVolume.getId(), server.getId());
        }
    }

    public void syncServerImage(CloudServer server, List<VolumeModel> driverImages, Long userId) {
        List<Image> dbImages = imageRepository.listByServerId(server.getId());
        List<Image> finalImages = new ArrayList<>();
        int hideIndex = 0;
        for (VolumeModel driverImage : driverImages) {
            if (StringUtils.isEmpty(driverImage.getPath())) {
                hideIndex++;
                driverImage.setPath("/com/bocloud/images/" + server.getName() + "_anonymous_" + hideIndex + ".iso");
            }
            Optional<Image> dbImageOpt = dbImages.stream()
                    .filter(dbImage -> Objects.equals(driverImage.getPath(), dbImage.getFilePath())).findFirst();
            Image dbImage;
            if (dbImageOpt.isPresent()) {
                dbImage = dbImageOpt.get();
                ServerImage dbServerImage = serverImageRepository.getByServerIdAndImageId(server.getId(), dbImage.getId());
                dbServerImage.setBootIndex(driverImage.getBootIndex());
                dbServerImage.setBus(driverImage.getBus());
                dbServerImage.setDisabled(driverImage.getDisabled());
                dbServerImage.setDiffKey(driverImage.getKey());
                serverImageRepository.update(dbServerImage);
                finalImages.add(dbImage);
            } else {
                dbImage = imageRepository.getByPath(driverImage.getPath(), server.getVendorId());
                if (dbImage == null) {
                    dbImage = new Image();
                    if (StringUtils.isEmpty(driverImage.getName())) {
                        dbImage.setName("未挂载ISO映像");
                    } else {
                        dbImage.setName(driverImage.getName());
                    }
                    dbImage.setImageType("ANONYMOUS_IMAGE");
                    dbImage.setCatalog("HIDE");
                    dbImage.setFilePath(driverImage.getPath());
                    dbImage.setVendorId(server.getVendorId());
                    dbImage.setGmtCreate(new Date());
                    dbImage.setCreatorId(userId);
                    imageRepository.save(dbImage);
                }

                ServerImage dbServerImage = new ServerImage();
                dbServerImage.setServerId(server.getId());
                dbServerImage.setImageId(dbImage.getId());
                dbServerImage.setBootIndex(driverImage.getBootIndex());
                dbServerImage.setBus(driverImage.getBus());
                dbServerImage.setDisabled(driverImage.getDisabled());
                dbServerImage.setDiffKey(driverImage.getKey());
                serverImageRepository.save(dbServerImage);
                finalImages.add(dbImage);
            }
        }
        dbImages.removeAll(finalImages);
        for (Image dbImage : dbImages) {
            serverImageRepository.remove(dbImage.getId(), server.getId());
        }
    }

    public List<Snapshot> syncSnapshot(CloudVendor vendor, List<SnapshotModel> driverSnapshots, Long userId) {
        List<Snapshot> dbSnapshots = snapshotRepository.listByVid(vendor.getId());
        List<Snapshot> finalSnaoshots = new ArrayList<>();
        for (SnapshotModel driverSnapshot : driverSnapshots) {
            Optional<Snapshot> dbSnapshotOpt = dbSnapshots.stream()
                    .filter(dbSnapshot -> Objects.equals(driverSnapshot.getSnapshotUuid(), dbSnapshot.getSnapshotUuid()))
                    .findFirst();
            Snapshot dbSnapshot;
            if (dbSnapshotOpt.isPresent()) {
                dbSnapshot = dbSnapshotOpt.get();
                this.convert(driverSnapshot, dbSnapshot);
                dbSnapshot.setMenderId(userId);
                dbSnapshot.setGmtModify(new Date());
                snapshotRepository.update(dbSnapshot);
            } else {
                dbSnapshot = new Snapshot();
                this.convert(driverSnapshot, dbSnapshot);
                dbSnapshot.setVendorId(vendor.getId());
                dbSnapshot.setCreatorId(userId);
                dbSnapshot.setGmtCreate(new Date());
                snapshotRepository.save(dbSnapshot);
            }
            if (StringUtils.isNotEmpty(driverSnapshot.getInstanceId())) {
                CloudServer server = cloudServerRepository.getByInstanceId(driverSnapshot.getInstanceId());
                // 存在云主机删除了，但是快照还存在的情况
                if (server != null) {
                    dbSnapshot.setVmId(server.getId());
                    dbSnapshot.setVmValue(driverSnapshot.getVmValue());
                }
            }
            if (StringUtils.isNotEmpty(driverSnapshot.getVolumeUuid())) {
                Volume volume = volumeRepository.getByVolumeUuid(driverSnapshot.getVolumeUuid());
                // 存在卷删除了，但是快照还存在的情况
                if (volume != null) {
                    dbSnapshot.setVolumeId(volume.getId());
                }
            }
            snapshotRepository.update(dbSnapshot);
            finalSnaoshots.add(dbSnapshot);
        }
        dbSnapshots.removeAll(finalSnaoshots);
        for (Snapshot dbSnapshot : dbSnapshots) {
            snapshotRepository.remove(dbSnapshot.getId(), userId);
        }
        return finalSnaoshots;
    }

    public void convert(VolumeTemplateModel model, VolumeTemplate entity) {
        entity.setUuid(model.getUuid());
        entity.setName(model.getName());
        entity.setStatus(model.getStatus());
        entity.setDescription(model.getDescription());
        entity.setRemark(model.getDescription());

        entity.setReadonly(model.getReadonly());
        entity.setThin(model.getThin());

        entity.setReplicaNum(model.getReplicaNum());
        entity.setStripeNum(model.getStripeNum());
        entity.setStripeSize(model.getStripeSize());

        entity.setWhitelist(model.getWhitelist());

        Optional.of(model).map(VolumeTemplateModel::getCreatedTime).map(t -> t * 1000).map(Date::new).ifPresent(entity::setCreatedTime);
        Optional.of(model).map(VolumeTemplateModel::getModifiedTime).map(t -> t * 1000).map(Date::new).ifPresent(entity::setModifiedTime);
    }

    public void convert(HostModel host, Server server, ServerConfig config) {
        server.setUuid(host.getUuid());
        server.setName(host.getName());
        server.setManageIp(host.getHostIp());
        server.setServiceIp(host.getServiceIp());
        server.setVmotionIp(host.getVmotionIp());
        server.setHostname(host.getHostName());
        server.setModelName(host.getModelName());
        server.setCpuModel(host.getCpuModel());
        server.setAssetCategory(ServerAssetCategory.HYPERVISOR.name());
        if ("2".equals(host.getStatus())) {
            server.setStatus(BocloudStatus.Server.RUNNING.name());
            server.setPowerState(BocloudStatus.Server.RUNNING.name());
        } else {
            server.setStatus(BocloudStatus.Server.UNKNOWN.name());
            server.setPowerState(BocloudStatus.Server.UNKNOWN.name());
        }
        config.setCpuCore(host.getCpuCore());
        config.setCpuHz(host.getCpuHz());
        config.setMemoryCapacity(host.getMemTotal());
        config.setDiskCapacity(host.getDiskTotal());

        Optional.of(host).map(HostModel::getDiskCacheSizeByte).map(d -> d / 1024D / 1024D / 1024D * 1000D)
                .map(Math::round).map(d -> d / 1000D).ifPresent(config::setCacheCapacity);
        Optional.of(host).map(HostModel::getDiskTotalSizeByte).map(d -> d / 1024D / 1024D / 1024D * 1000D)
                .map(Math::round).map(d -> d / 1000D).ifPresent(config::setDiskCapacity);
        Optional.of(host).map(HostModel::getDiskUsedSizeByte).map(d -> d / 1024D / 1024D / 1024D * 1000D)
                .map(Math::round).map(d -> d / 1000D).ifPresent(config::setDiskUsed);
        config.setSsdCount(host.getSsdCount());
        config.setHddCount(host.getHddCount());
        Optional.of(host).map(HostModel::getSsdTotalSizeByte).map(d -> d / 1024D / 1024D / 1024D * 1000D)
                .map(Math::round).map(d -> d / 1000D).ifPresent(config::setSsdCapacity);
        Optional.of(host).map(HostModel::getHddTotalSizeByte).map(d -> d / 1024D / 1024D / 1024D * 1000D)
                .map(Math::round).map(d -> d / 1000D).ifPresent(config::setHddCapacity);
        config.setIpmiAddress(host.getIpmiAddress());
    }

    public void convert(StorageDiskModel model, Disk entity) {
        entity.setDiskUuid(model.getDiskUuid());
        entity.setName(model.getName());
        entity.setStatus(model.getStatus());
        entity.setType(model.getType());
        entity.setPath(model.getPath());
        entity.setCapacity(model.getCapacity());
        entity.setProductId(model.getProductId());
        entity.setSize(Math.round(model.getCapacity() / 1024D / 1024D / 1024D * 1000D) / 1000D);
    }

    public void convert(NetworkCardModel model, NetworkCard entity) {
        entity.setNetworkcardUuid(model.getNetworkcardUuid());
        entity.setName(model.getName());
        entity.setMac(model.getMac());
        entity.setUsed(model.getUsed());
        entity.setIpAddress(model.getIpAddress());
        entity.setNetmask(model.getNetmask());
        entity.setGateway(model.getGateway());
        entity.setVlan(model.getVlan());
        entity.setUsed(model.getUsed());
        entity.setLabel(model.getLabel());
        entity.setSwitcherAvailable(model.getSwitcherAvailable());
        entity.setModel(model.getModel());
        entity.setMtu(model.getMtu());
        entity.setSpeed(model.getSpeed());
        entity.setIsPrimary(model.getIsPrimary());
    }

    public void convert(SwitcherModel model, Switcher entity) {
        entity.setUuid(model.getUuid());
        entity.setName(model.getName());
        entity.setBondMode(model.getBondMode());
        entity.setBondName(model.getBondName());
        entity.setOvsbrName(model.getOvsbrName());
    }

    public void convert(NetworkModel networkModel, Network network, Subnet subnet, PortGroup portGroup) {
        SubnetModel subnetModel = networkModel.getSubnets().get(0);

        network.setNetworkUuid(networkModel.getNetworkUuid());
        network.setName(networkModel.getName());

        subnet.setNetworkUuid(subnetModel.getNetworkUuid());
        subnet.setSubnetUuid(subnetModel.getSubnetUuid());
        subnet.setName(subnetModel.getName());
        subnet.setCidr(subnetModel.getCidr());
        subnet.setIpPools(subnetModel.getIpPools());
        subnet.setGateway(subnetModel.getGateway());
        subnet.setNetmask(subnetModel.getNetmask());
        subnet.setDnsNames(subnetModel.getDnsNames());
        subnet.setVlan(subnetModel.getVlan());
        subnet.setDhcpEnabled(subnetModel.getDhcpEnabled());
        subnet.setDhcpServerIp(subnetModel.getDhcpServerIp());

        portGroup.setGroupVal(networkModel.getNetworkUuid());
        portGroup.setName(networkModel.getName());
        portGroup.setVlanId(subnetModel.getVlan());
        portGroup.setType(NetworkType.PortGroupType.DVPORT.name());
    }

    public BocloudStatus.Server convertVmStatus(String status) {
        if (status == null) {
            return null;
        }
        if ("running".equalsIgnoreCase(status)) {
            return BocloudStatus.Server.RUNNING;
        } else if ("stopped".equalsIgnoreCase(status)) {
            return BocloudStatus.Server.STOPPED;
        } else if ("created".equalsIgnoreCase(status)) {
            return BocloudStatus.Server.CREATED;
        } else if ("suspended".equalsIgnoreCase(status)) {
            return BocloudStatus.Server.PAUSED;
        } else {
            try {
                return BocloudStatus.Server.valueOf(status.toUpperCase());
            } catch (IllegalArgumentException e) {
                logger.warn("Unknown Vm status: {}, {}", status, e.getMessage());
                return BocloudStatus.Server.UNKNOWN;
            }
        }
    }

    public void convert(ServerModel model, CloudServer entity) {
        entity.setIsTemplate(model.getIsTemplate());
        if (model.getIsTemplate()) {
            entity.setInstanceId(model.getTempInstId());
            entity.setName(model.getTemplate());
        } else {
            entity.setInstanceId(model.getInstanceId());
            entity.setName(model.getName());
        }
        entity.setRemark(model.getRemark());
        entity.setIsHA(model.getHa());

        entity.setCpu(model.getCpu());
        entity.setMemory(model.getMemory());

        entity.setGmtVmCreate(model.getGmtVmCreate());
        entity.setGmtVmModify(model.getGmtVmModify());

        entity.setPrivateIps(JSON.toJSONString(model.getIps()));
        entity.setManagerIp(model.getManagerIp());

        entity.setHostName(model.getHostName());
        entity.setOsVersion(model.getOsVersion());

        entity.setTemplateSize(model.getTemplateSize());
        entity.setStatus(this.convertVmStatus(model.getStatus()).name());

        entity.setCloudInitSupported(model.getCloudInitSupported());
    }

    public void convert(VolumeModel model, Volume entity) {
        entity.setName(model.getName());
        entity.setType(model.getType());
        entity.setPath(model.getPath());
        entity.setDevice(model.getDevice());
        entity.setStatus(model.getStatus());
        entity.setRemark(model.getRemark());
        entity.setVolumeUuid(model.getVolumeUuid());
        entity.setBlockSize(model.getBlockSize());
        entity.setBlockCount(model.getBlockCount());
        entity.setSize(model.getSize());
        entity.setMultiattach(model.getMultiattach());
        entity.setCreateTime(model.getCreateTime());
    }

    public void convert(SnapshotModel model, Snapshot entity) {
        entity.setSnapshotUuid(model.getSnapshotUuid());
        entity.setName(model.getName());
        entity.setSnapshotType(model.getSnapshotType());
        entity.setStatus(model.getStatus());
        entity.setSizeByte(model.getSizeByte());
        entity.setSize(model.getSize());
        entity.setRemark(model.getRemark());
        entity.setCreateTime(model.getCreateTime());
    }

    public void convert(StorageDiskPartitionModel model, DiskPartition entity) {
        entity.setName(model.getName());
        entity.setMountPath(model.getMountPath());
        entity.setUsages(model.getUsages());
        entity.setSizeByte(model.getSizeByte());
        entity.setUsedSizeByte(model.getUsedSizeByte());
    }

    public void convert(ImageModel model, Image entity) {
        entity.setImageUuid(model.getImageUuid());
        entity.setName(model.getName());
        entity.setImageType(model.getImageType());
        entity.setFilePath(model.getFilePath());
        entity.setSize(model.getSize());
        entity.setCreateTime(model.getGmtCreate());
        entity.setOsVersion(model.getOsVersion());
        entity.setRemark(model.getRemark());
        entity.setCatalog("ISO");
        entity.setStatus("ACTIVE");
    }

    public void convert(ServerModel model, Image entity) {
        entity.setImageUuid(model.getTempInstId());
        entity.setName(model.getTemplate());
        entity.setImageType("TEMPLATE_IMAGE");
        entity.setRemark(model.getRemark());
        entity.setCatalog("VM");
        entity.setStatus("ACTIVE");
    }

    public void convert(TaskMsgModel model, VendorTaskMsg entity) {
        entity.setKey(model.getUuid());
        entity.setName(model.getName());
        entity.setStatus(model.getState());
        entity.setTargetType(model.getTargetType());
        entity.setTargetUuid(model.getTargetUuid());
        entity.setTargetName(model.getTargetName());
        entity.setStartTime(model.getStartTime());
        entity.setCompleteTime(model.getCompleteTime());
        entity.setMessageEnUS(model.getMessageEnUS());
        entity.setMessageZhCN(model.getMessageZhCN());
        entity.setDetailEnUS(model.getDetailEnUS());
        entity.setDetailZhCN(model.getDetailZhCN());
        entity.setResources(model.getResources());
        entity.setResourceGroup(model.getResourceGroup());
        entity.setErrorCode(model.getErrorCode());
        entity.setErrorMessage(model.getErrorMessage());
    }
}
