diff --git a/bocloud.ims.entity/src/main/java/com/bocloud/ims/entity/resource/Cluster.java b/bocloud.ims.entity/src/main/java/com/bocloud/ims/entity/resource/Cluster.java index bb39b4e..0052c0e 100644 --- a/bocloud.ims.entity/src/main/java/com/bocloud/ims/entity/resource/Cluster.java +++ b/bocloud.ims.entity/src/main/java/com/bocloud/ims/entity/resource/Cluster.java @@ -126,6 +126,15 @@ public class Cluster extends GenericEntity { private String memUsage; // fc 纳管 + @IgnoreAll + private Long cpuRemainder; + @IgnoreAll + private Long memoryRemainder; + @IgnoreAll + private Long diskRemainder; + @IgnoreAll + private List servers; + /** * @param creatorId * @param ownerId diff --git a/bocloud.ims.entity/src/main/java/com/bocloud/ims/entity/resource/DataStore.java b/bocloud.ims.entity/src/main/java/com/bocloud/ims/entity/resource/DataStore.java index ab6bdc1..df12a7e 100644 --- a/bocloud.ims.entity/src/main/java/com/bocloud/ims/entity/resource/DataStore.java +++ b/bocloud.ims.entity/src/main/java/com/bocloud/ims/entity/resource/DataStore.java @@ -164,6 +164,16 @@ public class DataStore extends GenericEntity { @IgnoreAll private String version; + @IgnoreAll + private Long SpaceRemain; + + public Long getSpaceRemain() { + return SpaceRemain; + } + + public void setSpaceRemain(Long spaceRemain) { + SpaceRemain = spaceRemain; + } /** * @return the vdcId diff --git a/bocloud.ims.entity/src/main/java/com/bocloud/ims/entity/resource/Server.java b/bocloud.ims.entity/src/main/java/com/bocloud/ims/entity/resource/Server.java index 1b6a0de..2d4f132 100644 --- a/bocloud.ims.entity/src/main/java/com/bocloud/ims/entity/resource/Server.java +++ b/bocloud.ims.entity/src/main/java/com/bocloud/ims/entity/resource/Server.java @@ -300,6 +300,10 @@ public class Server extends GenericEntity { private String cpuTotalMhz; + @IgnoreAll + private CloudVendor vendor; + + public void preSave(Long creatorId, Long menderId, Long vendorId, Long vdcId, Long clusterId) { this.setCreatorId(creatorId); diff --git a/bocloud.ims.entity/src/main/java/com/bocloud/ims/entity/resource/Vdc.java b/bocloud.ims.entity/src/main/java/com/bocloud/ims/entity/resource/Vdc.java index c2881c4..b5b0b21 100644 --- a/bocloud.ims.entity/src/main/java/com/bocloud/ims/entity/resource/Vdc.java +++ b/bocloud.ims.entity/src/main/java/com/bocloud/ims/entity/resource/Vdc.java @@ -81,6 +81,15 @@ public class Vdc extends GenericEntity { private String mgntNetworkType;//管理面ip版本,取值ipv4,ipv6 //fc新增字段 + + @IgnoreAll + private Long cpuRemainder; + @IgnoreAll + private Long memoryRemainder; + @IgnoreAll + private Long diskRemainder; + @IgnoreAll + private List servers; /** * @return the id */ diff --git a/bocloud.ims.repository/src/main/java/com/bocloud/ims/repository/resource/NetworkRepository.java b/bocloud.ims.repository/src/main/java/com/bocloud/ims/repository/resource/NetworkRepository.java index 60e3992..0eb450b 100644 --- a/bocloud.ims.repository/src/main/java/com/bocloud/ims/repository/resource/NetworkRepository.java +++ b/bocloud.ims.repository/src/main/java/com/bocloud/ims/repository/resource/NetworkRepository.java @@ -11,6 +11,7 @@ import com.bocloud.database.core.intf.impl.BasicGenericDao; import com.bocloud.database.utils.QueryBuilder; import com.bocloud.ims.entity.resource.Network; import com.bocloud.ims.entity.resource.Server; +import com.bocloud.orm.OrmGenericDaoImpl; import org.apache.commons.lang3.StringUtils; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate; @@ -30,7 +31,7 @@ import java.util.Map; * */ @Repository("networkRepository") -public class NetworkRepository extends BasicGenericDao { +public class NetworkRepository extends OrmGenericDaoImpl { public NetworkRepository(JdbcTemplate jdbcTemplate, NamedParameterJdbcTemplate npJdbcTemplate) { super(jdbcTemplate, npJdbcTemplate); diff --git a/bocloud.ims.repository/src/main/java/com/bocloud/ims/repository/resource/ServerConfigRepository.java b/bocloud.ims.repository/src/main/java/com/bocloud/ims/repository/resource/ServerConfigRepository.java index 9db61cc..f9becb0 100644 --- a/bocloud.ims.repository/src/main/java/com/bocloud/ims/repository/resource/ServerConfigRepository.java +++ b/bocloud.ims.repository/src/main/java/com/bocloud/ims/repository/resource/ServerConfigRepository.java @@ -4,6 +4,7 @@ import com.bocloud.common.utils.ListTool; import com.bocloud.common.utils.MapTools; import com.bocloud.database.core.intf.impl.BasicGenericDao; import com.bocloud.ims.entity.resource.ServerConfig; +import com.bocloud.orm.OrmGenericDaoImpl; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate; import org.springframework.stereotype.Repository; @@ -20,7 +21,7 @@ import java.util.Map; * */ @Repository("serverConfigRepository") -public class ServerConfigRepository extends BasicGenericDao { +public class ServerConfigRepository extends OrmGenericDaoImpl { public ServerConfigRepository(JdbcTemplate jdbcTemplate, NamedParameterJdbcTemplate npJdbcTemplate) { super(jdbcTemplate, npJdbcTemplate); diff --git a/bocloud.ims.service/src/main/java/com/bocloud/ims/service/builder/CloudTowerServerBuilder.java b/bocloud.ims.service/src/main/java/com/bocloud/ims/service/builder/CloudTowerServerBuilder.java new file mode 100644 index 0000000..3baf7de --- /dev/null +++ b/bocloud.ims.service/src/main/java/com/bocloud/ims/service/builder/CloudTowerServerBuilder.java @@ -0,0 +1,471 @@ +package com.bocloud.ims.service.builder; + +import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson.JSONObject; +import com.bocloud.cmp.driver.compute.ServerDriver; +import com.bocloud.cmp.driver.compute.ServerTemplateDriver; +import com.bocloud.cmp.driver.storage.VolumeDriver; +import com.bocloud.cmp.model.NetworkCardModel; +import com.bocloud.cmp.model.ServerModel; +import com.bocloud.cmp.model.VolumeModel; +import com.bocloud.cmp.vm.model.VmGenericParam; +import com.bocloud.common.model.BocloudResult; +import com.bocloud.common.model.RequestContext; +import com.bocloud.ims.entity.resource.*; +import com.bocloud.ims.repository.resource.*; +import com.bocloud.ims.service.transporter.vendor.CloudTowerTransporter; +import org.apache.commons.lang3.StringUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.util.Assert; + +import java.util.ArrayList; +import java.util.Date; +import java.util.List; +import java.util.Optional; +import java.util.stream.Collectors; + +/** + * cloudTower + */ +public class CloudTowerServerBuilder implements ServerBuilder { + private static final Logger logger = LoggerFactory.getLogger(CloudTowerServerBuilder.class); + + private final CloudVendorRepository cloudVendorRepository; + private final ServerRepository serverRepository; + private final CloudServerRepository cloudServerRepository; + private final ClusterRepository clusterRepository; + private final VolumeRepository volumeRepository; + private final VolumeTemplateRepository volumeTemplateRepository; + private final ImageRepository imageRepository; + private final SwitcherRepository switcherRepository; + private final NetworkRepository networkRepository; + private final NetworkCardRepository networkCardRepository; + private final SubnetRepository subnetRepository; + private final ServerVolumeRepository serverVolumeRepository; + private final ServerImageRepository serverImageRepository; + private final ServerDriver serverDriver; + private final VolumeDriver volumeDriver; + private final ServerTemplateDriver serverTemplateDriver; + private final CloudTowerTransporter cloudTowerTransporter; + private final RequestContext context; + + private CloudVendor vendor; + private VmGenericParam vmGenericParam; + private ServerModel serverModel; + + public CloudTowerServerBuilder(CloudVendorRepository cloudVendorRepository, ServerRepository serverRepository, + CloudServerRepository cloudServerRepository, VolumeRepository volumeRepository, + VolumeTemplateRepository volumeTemplateRepository, ImageRepository imageRepository, + SwitcherRepository switcherRepository, NetworkRepository networkRepository, + SubnetRepository subnetRepository, ServerVolumeRepository serverVolumeRepository, + ServerImageRepository serverImageRepository, ServerDriver serverDriver, + VolumeDriver volumeDriver, ServerTemplateDriver serverTemplateDriver, + CloudTowerTransporter cloudTowerTransporter, RequestContext context, + ClusterRepository clusterRepository, NetworkCardRepository networkCardRepository) { + this.cloudVendorRepository = cloudVendorRepository; + this.serverRepository = serverRepository; + this.cloudServerRepository = cloudServerRepository; + this.volumeRepository = volumeRepository; + this.volumeTemplateRepository = volumeTemplateRepository; + this.imageRepository = imageRepository; + this.switcherRepository = switcherRepository; + this.networkRepository = networkRepository; + this.subnetRepository = subnetRepository; + this.serverVolumeRepository = serverVolumeRepository; + this.serverImageRepository = serverImageRepository; + this.serverDriver = serverDriver; + this.volumeDriver = volumeDriver; + this.serverTemplateDriver = serverTemplateDriver; + this.cloudTowerTransporter = cloudTowerTransporter; + this.context = context; + this.clusterRepository = clusterRepository; + this.networkCardRepository = networkCardRepository; + } + + @Override + public BocloudResult prepare(JSONObject object) throws Exception { + try { + VmGenericParam vmGenericParam = object.toJavaObject(VmGenericParam.class); + Long sourceServerId = null; + ServerModel serverModel = new ServerModel(); + Cluster cluster = null; + if (vmGenericParam.getTemplateId() != null) { + // 从模板创建云主机 + sourceServerId = vmGenericParam.getTemplateId(); + + CloudServer template = cloudServerRepository.query(vmGenericParam.getTemplateId()); + Assert.notNull(template, "id为" + vmGenericParam.getTemplateId() + "云主机模板不存在"); + vmGenericParam.setVendorId(template.getVendorId()); + serverModel.setInstanceId(template.getInstanceId()); + cluster = clusterRepository.query(vmGenericParam.getClusterId()); + Assert.notNull(cluster, "id为" + vmGenericParam.getClusterId() + "集群不存在"); + serverModel.setClusterUuid(cluster.getUuid()); + if (vmGenericParam.getFullClone() == null) { + vmGenericParam.setFullClone(true); + } + if (vmGenericParam.getFullClone() && vmGenericParam.getServerId() == 0) { + return new BocloudResult<>(false, "完全拷贝克隆需要明确指定目标云主机所属主机"); + } + serverModel.setFullClone(vmGenericParam.getFullClone()); + + serverModel.setVmHostName(vmGenericParam.getVmHostName()); + serverModel.setPassword(vmGenericParam.getPassword()); + } + + vendor = cloudVendorRepository.query(vmGenericParam.getVendorId()); + Assert.notNull(vendor, "ID为" + vmGenericParam.getVendorId() + "云平台不存在"); + + Assert.notNull(vmGenericParam.getName(), "需要为云主机指定名称"); + CloudServer vmbyName = cloudServerRepository.getByName(vmGenericParam.getName(), vendor.getId()); + Assert.isNull(vmbyName, "名为" + vmGenericParam.getName() + "的云主机已经存在,请重新命名"); + serverModel.setName(vmGenericParam.getName()); + + if (vmGenericParam.getHa() == null) { + vmGenericParam.setHa(true); + } + serverModel.setHa(vmGenericParam.getHa()); + + Assert.notNull(vmGenericParam.getServerId(), "需要为云主机指定所属主机"); + if (vmGenericParam.getServerId() == 0) { + serverModel.setAutoSchedule(true); + } else { + Server host = serverRepository.query(vmGenericParam.getServerId()); + Assert.notNull(host, "id为" + vmGenericParam.getServerId() + "主机不存在"); + serverModel.setHostIp(host.getVmotionIp()); + serverModel.setHostUuid(host.getUuid()); + } + + Assert.notNull(vmGenericParam.getCpu(), "需要为云主机指定CPU配置"); + serverModel.setCpu(vmGenericParam.getCpu()); + + Assert.notNull(vmGenericParam.getMemory(), "需要为云主机指定内存配置"); + serverModel.setMemory(vmGenericParam.getMemory()); + + Assert.notNull(vmGenericParam.getFirmware(), "需要为云主机指定引导选项"); + serverModel.setFirmware(vmGenericParam.getFirmware()); + + Assert.notNull(vmGenericParam.getStatus(), "参数status不能为空"); + serverModel.setStatus(vmGenericParam.getStatus()); + + if (StringUtils.isNotEmpty(vmGenericParam.getRemark())) { + serverModel.setRemark(vmGenericParam.getRemark()); + } + + Assert.notEmpty(vmGenericParam.getDiskDevices(), "需要为云主机定义虚拟盘"); + List disks = new ArrayList<>(); + for (int i = 0; i < vmGenericParam.getDiskDevices().size(); i++) { + VolumeModel diskDevice = vmGenericParam.getDiskDevices().get(i); + VolumeModel disk = new VolumeModel(); + disk.setType(diskDevice.getType()); + + if ("reserveDisk".equals(diskDevice.getType())) { + Assert.notNull(diskDevice.getId(), "需要指定要保留的虚拟卷"); + Volume volume = volumeRepository.query(diskDevice.getId()); + Assert.notNull(volume, "id为" + diskDevice.getId() + "的虚拟卷不存在"); + + ServerVolume serverVolume = serverVolumeRepository + .getByServerIdAndVolumeId(sourceServerId, volume.getId()); + Assert.notNull(serverVolume, "id为" + diskDevice.getId() + "的虚拟卷并未挂载当前云主机"); + + disk.setPath(volume.getPath()); + disk.setTemplateUuid(serverVolume.getTemplateUuid()); + + if (StringUtils.isNotEmpty(diskDevice.getName())) { + disk.setName(diskDevice.getName()); + } else { + disk.setName(volume.getName()); + } + + if (StringUtils.isNotEmpty(diskDevice.getBus())) { + disk.setBus(diskDevice.getBus()); + } else { + disk.setBus(serverVolume.getBus()); + } + } else if ("removeDisk".equals(diskDevice.getType())) { + Assert.notNull(diskDevice.getId(), "需要指定要删除的虚拟卷"); + Volume volume = volumeRepository.query(diskDevice.getId()); + Assert.notNull(volume, "id为" + diskDevice.getId() + "的虚拟卷不存在"); + + ServerVolume serverVolume = serverVolumeRepository + .getByServerIdAndVolumeId(sourceServerId, volume.getId()); + Assert.notNull(serverVolume, "id为" + diskDevice.getId() + "的虚拟卷并未挂载当前云主机"); + + disk.setPath(volume.getPath()); + } else if ("reserveISO".equals(diskDevice.getType())) { + Assert.notNull(diskDevice.getDiffKey(), "需要指定要保留的ISO映像的KEY"); + List serverImages = serverImageRepository.listByServerId(sourceServerId); + ServerImage serverImage = serverImages.stream() + .filter(si -> diskDevice.getDiffKey().equals(si.getDiffKey())).findFirst().orElse(null); + Assert.notNull(serverImage, "KEY为" + diskDevice.getDiffKey() + "的ISO映像不存在"); + disk.setKey(serverImage.getDiffKey()); + disk.setBus(serverImage.getBus()); + + if (diskDevice.getId() != null) { + Image image = imageRepository.query(diskDevice.getId()); + Assert.notNull(image, "id为" + diskDevice.getId() + "的ISO映像不存在"); + disk.setPath(image.getFilePath()); + } + + if (diskDevice.getDisabled() != null) { + disk.setDisabled(diskDevice.getDisabled()); + } else { + disk.setDisabled(serverImage.getDisabled()); + } + } else if ("removeISO".equals(diskDevice.getType())) { + Assert.notNull(diskDevice.getDiffKey(), "需要指定要删除的ISO映像的KEY"); + List serverImages = serverImageRepository.listByServerId(sourceServerId); + ServerImage serverImage = serverImages.stream() + .filter(si -> diskDevice.getDiffKey().equals(si.getDiffKey())).findFirst().orElse(null); + Assert.notNull(serverImage, "KEY为" + diskDevice.getDiffKey() + "的ISO映像不存在"); + disk.setKey(serverImage.getDiffKey()); + + Image image = imageRepository.query(serverImage.getImageId()); + Assert.notNull(image, "id为" + diskDevice.getId() + "的ISO映像不存在"); + if ("HIDE".equals(image.getCatalog())) { + disk.setPath(""); + } else { + disk.setPath(image.getFilePath()); + } + } else if ("newDisk".equals(diskDevice.getType())) { + diskDevice.setName(vmGenericParam.getName() + "_disk" + i); + Assert.notNull(diskDevice.getName(), "需要为新建的虚拟卷指定名称"); + Volume volumeByName = volumeRepository.getByName(vendor.getId(), diskDevice.getName()); + Assert.isNull(volumeByName, "名为" + diskDevice.getName() + "的虚拟卷已经存在,请重新命名"); + disk.setName(diskDevice.getName()); + disk.setSize(diskDevice.getSize()); + if (disk.getSize() == null){ + disk.setSize(diskDevice.getDisk()); + } + Assert.notNull(disk.getSize(), "需要为新建的虚拟卷指定容量"); + disk.setBlockSize(1D); + disk.setBlockCount(disk.getSize() * 1024L * 1024L * 1024L); + + Assert.notNull(diskDevice.getBus(), "需要为新建的虚拟卷指定总线"); + disk.setBus(diskDevice.getBus()); + + Assert.notNull(diskDevice.getVolumeTemplateId(), "需要为新建的虚拟卷指定存储策略"); + VolumeTemplate volumeTemplate = volumeTemplateRepository.get(diskDevice.getVolumeTemplateId()); + Assert.notNull(volumeTemplate, "id为" + diskDevice.getVolumeTemplateId() + "的存储策略不存在"); + disk.setVolumeTemplateUuid(volumeTemplate.getUuid()); + } else if ("mountDisk".equals(diskDevice.getType())) { + Assert.notNull(diskDevice.getId(), "需要指定要挂载的虚拟卷"); + Volume volume = volumeRepository.query(diskDevice.getId()); + Assert.notNull(volume, "id为" + diskDevice.getId() + "的虚拟卷不存在"); + disk.setPath(volume.getPath()); + + Assert.notNull(diskDevice.getBus(), "需要为新建的虚拟卷指定总线"); + disk.setBus(diskDevice.getBus()); + } else if ("mountISO".equals(diskDevice.getType())) { + disk.setBus("ide"); + + Assert.notNull(diskDevice.getId(), "需要指定要挂载的ISO映像"); + Image image = imageRepository.query(diskDevice.getId()); + Assert.notNull(image, "id为" + diskDevice.getId() + "的ISO映像不存在"); + disk.setPath(image.getFilePath()); + + Assert.notNull(diskDevice.getDisabled(), "需要参数diskDevices.disabled"); + disk.setDisabled(diskDevice.getDisabled()); + } else { + return new BocloudResult<>(false, "未知的虚拟盘类型"); + } + disks.add(disk); + } + serverModel.setDiskDevices(disks); + + /** + * 调整工单网卡参数 + */ + // 根据集群查询网卡信息 + Network network1 = null; + if (cluster != null) { + network1 = networkRepository.queryProxy() + .col(Network::getClusterUuid).eq(cluster.getUuid()) + .col(Network::getType).eq("VM") + .col(Network::getDeleted).eq(false) + .list().get(0); + } + Assert.notEmpty(vmGenericParam.getNetworkCards(), "需要为云主机定义虚拟网卡"); + for (NetworkCardModel netcard : vmGenericParam.getNetworkCards()) { + Assert.notNull(netcard.getNetworkId(), "需要为虚拟网卡指定网络"); + Network network = networkRepository.query(netcard.getNetworkId()); + Assert.notNull(network, "未找到id为" + netcard.getNetworkId() + "的虚拟网络"); + if (network1 != null) { + logger.info("当前虚拟网络为:{}", JSONObject.toJSONString(network1)); + netcard.setNetworkUuid(network1.getNetworkUuid()); + } else { + netcard.setNetworkUuid(network.getNetworkUuid()); + } + } + serverModel.setNetworkCards(vmGenericParam.getNetworkCards()); + + this.serverModel = serverModel; + this.vmGenericParam = vmGenericParam; + + object.remove("id"); + + return new BocloudResult<>(true, serverModel, "success"); + } catch (Exception e) { + logger.error("Prepare Cloud-Tower云主机失败:{}", e.getMessage(), e); + return new BocloudResult<>(false, e.getMessage()); + } + } + + @Override + public BocloudResult builder(CloudServer server) throws Exception { + try { + String vlan = ""; + try { + List networkCards = server.getNetworkCards(); + for (NetworkCardModel networkCard : networkCards) { + vlan = networkRepository.query(networkCard.getNetworkId()).getName(); + break; + } + logger.info("获取vlan名称成功"); + } catch (Exception e) { + logger.error("获取vlan名称失败", e); + } + // 执行创建 + BocloudResult createResult; + if (vmGenericParam.getTemplateId() != null) { + logger.info("serverModel:{}", JSON.toJSONString(serverModel)); + // 从模板创建云主机 + createResult = serverDriver + .create(vendor.getUuid(), null, serverModel.getInstanceId(), serverModel); + Assert.isTrue(createResult.isSuccess(), createResult.getMessage()); + } else if (vmGenericParam.getId() != null) { + // 从云主机克隆云主机 + createResult = serverDriver.cloneAsVm(vendor.getUuid(), null, serverModel); + Assert.isTrue(createResult.isSuccess(), createResult.getMessage()); + } else { + // 创建空白云主机 + createResult = serverDriver.create(vendor.getUuid(), null, serverModel); + Assert.isTrue(createResult.isSuccess(), createResult.getMessage()); + } + // 获取云主机数据 + ServerModel serverModel = (ServerModel) createResult.getData(); + if (vmGenericParam.getTemplateId() != null) { + // 从模板创建云主机 + } + // 查询所属宿主机 + String serverIp = serverModel.getServerVal(); + Server host = serverRepository.queryBy3Ip(serverIp); + Assert.notNull(host, "未找到IP为" + serverIp + "的主机"); + // 保存云主机数据 + cloudTowerTransporter.convert(serverModel, server); + // 获取IP信息 + if(StringUtils.isBlank(serverModel.getManagerIp())) { + logger.info("开始获取tower虚拟机IP地址......"); + while (true) { + BocloudResult detail = serverDriver.detail(serverModel.getInstanceId(), vendor.getUuid(), null); + if (detail.isSuccess()) { + ServerModel model = (ServerModel) detail.getData(); + if (StringUtils.isBlank(model.getManagerIp())) { + Thread.sleep(3000L); + } else { + logger.info("获取tower虚拟机IP地址成功{}......",model.getManagerIp()); + // 保存云主机数据 + cloudTowerTransporter.convert(model, server); + List netcards = model.getNetcards(); + List networkCardList = new ArrayList<>(); + for (NetworkCardModel netcard : netcards) { + NetworkCard networkCard = new NetworkCard(); + networkCard.setCategory("VM"); + networkCard.setHostId(server.getId()); + networkCard.setName(vlan); + networkCard.setCreatorId(context.getTarget()); + networkCard.setSubnetUuid(netcard.getSubnetUuid()); + networkCard.setInstanceId(netcard.getInstanceId()); + networkCard.setVlan(netcard.getVlan()); + networkCard.setNetmask(netcard.getNetmask()); + networkCard.setIpAddress(netcard.getIpAddress()); + networkCard.setModel(netcard.getModel()); + networkCard.setUsed(netcard.getUsed()); + networkCard.setMac(netcard.getMac()); + networkCard.setNetworkcardUuid(netcard.getNetworkcardUuid()); + networkCard.setVendorId(vendor.getId()); + networkCard.setGmtCreate(new Date()); + networkCard.setDeleted(false); + networkCardList.add(networkCard); + } + for (NetworkCard networkCard : networkCardList) { + networkCardRepository.save(networkCard); + } + logger.info(server.getName() + ":创建虚拟机添加网卡成功"); + break; + } + } + } + } + server.setHostId(host.getId()); + server.setVendorId(vendor.getId()); + server.setVendorType(vendor.getType()); + cloudServerRepository.update(server); + // 保存新建的虚拟卷数据 + /* for (Map.Entry taskResourceEntry : taskResource.entrySet()) { + String taskResourceUuid = taskResourceEntry.getKey(); + if (taskResourceUuid.equals(server.getInstanceId())) { + continue; + } + BocloudResult volumeResult = volumeDriver.detail(taskResourceUuid, vendor.getUuid(), null); + Assert.isTrue(volumeResult.isSuccess(), volumeResult.getMessage()); + VolumeModel volumeModel = (VolumeModel) volumeResult.getData(); + + if (volumeModel != null) { + logger.info("查询名为[{}]cloudTower卷大小为:{}", volumeModel.getName(), volumeModel.getSize()); + Volume existsVolume = volumeRepository.getByVolumeUuid(volumeModel.getUuid()); + if (existsVolume == null) { + String volumeTemplateUuid = volumeModel.getVolumeTemplateUuid(); + VolumeTemplate volumeTemplate = volumeTemplateRepository.getByUuid(volumeTemplateUuid); + Assert.notNull(volumeTemplate, "未找到UUID为" + volumeTemplateUuid + "的存储策略"); + + Volume volume = new Volume(); + cloudTowerTransporter.convert(volumeModel, volume); + volume.setVendorId(vendor.getId()); + volume.setTemplateId(volumeTemplate.getId()); + volume.setCreatorId(context.getTarget()); + volume.setGmtCreate(new Date()); + volume.setRegionId(server.getRegionId()); + volume.setRegion(server.getRegionId()); + volumeRepository.save(volume); + } + } + }*/ + // 保存云主机网卡数据 +// cloudTowerTransporter.syncNetworkCard(server, Optional.ofNullable(serverModel.getNetworkCards()).orElse(new ArrayList<>()), context.getTarget()); + // 保存云主机关联ISO镜像 + List imageModels = Optional.ofNullable(serverModel.getDiskDevices()).orElse(new ArrayList<>()).stream() + .filter(d -> "cdrom".equalsIgnoreCase(d.getType())).collect(Collectors.toList()); + cloudTowerTransporter.syncServerImage(server, imageModels, context.getTarget()); + // 保存云主机关联虚拟盘 + List volumeModels = Optional.ofNullable(serverModel.getDiskDevices()).orElse(new ArrayList<>()).stream() + .filter(d -> "disk".equalsIgnoreCase(d.getType())).collect(Collectors.toList()); + for (VolumeModel volumeModel : volumeModels) { + String volumeUuid = volumeModel.getVolumeUuid(); + Volume volume = volumeRepository.getByVolumeUuid(volumeUuid); + Optional dbVolumeTemplateOpt = Optional.of(volumeModel) + .map(VolumeModel::getVolumeTemplateUuid).map(volumeTemplateRepository::getByName); + if (volume == null) { + volume = new Volume(); + cloudTowerTransporter.convert(volumeModel, volume); + volume.setVendorId(vendor.getId()); + volume.setServerId(server.getId()); + volume.setVendorId(server.getVendorId()); + volume.setInstanceId(server.getInstanceId()); + volume.setTemplateId(dbVolumeTemplateOpt.isPresent() ? dbVolumeTemplateOpt.get().getId() : null); + volume.setCreatorId(context.getTarget()); + volume.setGmtCreate(new Date()); + volume.setRegionId(server.getRegionId()); + volume.setRegion(server.getRegionId()); + volumeRepository.save(volume); + } + } + cloudTowerTransporter.syncServerVolume(server, volumeModels, context.getTarget()); + + return new BocloudResult<>(true, server, "success"); + } catch (Exception e) { + logger.error("Build cloudTower云主机失败:{}", e.getMessage(), e); + return new BocloudResult<>(false, server, e.getMessage()); + } + } +} diff --git a/bocloud.ims.service/src/main/java/com/bocloud/ims/service/engine/AssignResultManager.java b/bocloud.ims.service/src/main/java/com/bocloud/ims/service/engine/AssignResultManager.java new file mode 100644 index 0000000..73b51d4 --- /dev/null +++ b/bocloud.ims.service/src/main/java/com/bocloud/ims/service/engine/AssignResultManager.java @@ -0,0 +1,28 @@ +package com.bocloud.ims.service.engine; + +import com.bocloud.cmp.model.common.AssignResult; +import com.bocloud.ims.entity.resource.DataStore; +import lombok.Data; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +@Data +public class AssignResultManager { + private AssignResult assignResult; + private Map dataStoreSpaceCache = new HashMap<>(); + + public void save(List dataStoreList){ + dataStoreSpaceCache.clear(); + for (DataStore dataStore : dataStoreList) { + dataStoreSpaceCache.put(dataStore.getId(), dataStore.getSpaceRemain()); + } + } + + public void rollback(List dataStoreList){ + for (DataStore dataStore : dataStoreList) { + dataStore.setSpaceRemain(dataStoreSpaceCache.get(dataStore.getId())); + } + } +} diff --git a/bocloud.ims.service/src/main/java/com/bocloud/ims/service/engine/ResSchedulerEngine.java b/bocloud.ims.service/src/main/java/com/bocloud/ims/service/engine/ResSchedulerEngine.java index a508937..73e2d92 100644 --- a/bocloud.ims.service/src/main/java/com/bocloud/ims/service/engine/ResSchedulerEngine.java +++ b/bocloud.ims.service/src/main/java/com/bocloud/ims/service/engine/ResSchedulerEngine.java @@ -2,13 +2,20 @@ package com.bocloud.ims.service.engine; import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONObject; +import com.bocloud.cmp.driver.compute.HostDriver; import com.bocloud.cmp.driver.storage.DataStoreDriver; import com.bocloud.cmp.entity.enums.BocloudStatus; import com.bocloud.cmp.entity.enums.CloudProvider; import com.bocloud.cmp.entity.enums.PoolGroupTarget; import com.bocloud.cmp.model.DataStoreModel; +import com.bocloud.cmp.model.HostOperateModel; +import com.bocloud.cmp.model.common.AssignDataDiskParam; +import com.bocloud.cmp.model.common.AssignDataDiskResult; +import com.bocloud.cmp.model.common.AssignParam; +import com.bocloud.cmp.model.common.AssignResult; import com.bocloud.common.model.BocloudResult; import com.bocloud.common.utils.ListTool; +import com.bocloud.entity.bean.GenericEntity; import com.bocloud.ims.entity.resource.*; import com.bocloud.ims.interfaces.resource.VMExtendService; import com.bocloud.ims.repository.resource.*; @@ -19,9 +26,11 @@ import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import org.springframework.util.Assert; +import org.springframework.util.CollectionUtils; import java.math.BigDecimal; import java.util.*; +import java.util.function.Function; import java.util.stream.Collectors; /** @@ -51,6 +60,7 @@ import java.util.stream.Collectors; @Autowired private SwitcherRepository switcherRepository; @Autowired private NetworkHostRepository networkHostRepository; @Autowired private VMExtendService vmExtendService; + @Autowired private HostDriver hostDriver; /** * 分配数据存储 @@ -580,4 +590,613 @@ import java.util.stream.Collectors; return new BocloudResult<>(true, targetObjs, "success"); } + + /** + * 通用的调度方法 + * + * @param assignParamList + * @return + */ + public List assign(PoolGroup group, CloudVendor vendor, List assignParamList) throws Exception { + // 查询策略是否存在 + SchedulerPolicy schedulerPolicy = schedulerPolicyRepository.query(group.getPolicyId()); + Assert.notNull(schedulerPolicy, "策略不存在"); + + // 过滤出资源池下关联的主机和数据存储 + List relations = relationRepository.listByGroupId(group.getId()); + + + Set clusterIds = relations.stream().filter(PoolGroupRelation::getEnabled) + .filter(relation -> PoolGroupTarget.Cluster.name().equalsIgnoreCase(relation.getCategory())) + .map(PoolGroupRelation::getTarget) + .collect(Collectors.toSet()); + + //查询可以调度的宿主机 + List hostList = serverRepository.queryProxy() + .col(GenericEntity::getStatus).eq(BocloudStatus.Server.RUNNING.name()) + .col(Server::getClusterId).in(clusterIds).col(GenericEntity::getDeleted).notEq(1) + .list(); + + String sort = "Memory"; + List resultObjs = configAndAssign(sort, group.getRelationType(), SchedulerPolicy.policy.valueOf(schedulerPolicy.getPolicy()), vendor, assignParamList, hostList); + return resultObjs; + } + + + /** + * 该方法抽象出来是为了能够让获取不到资源池的场景可以调用,可以自定义调度参数 + * + * @return + */ + private List configAndAssign(String sort, String resourceRelationType, SchedulerPolicy.policy schedulerPolicy, CloudVendor vendor, List assignParamList, List hostList) { + configVendor(vendor, hostList); +// logger.info("hostList:{}", JSON.toJSONString(hostList)); + + //查询ServerConfig + configHosts(hostList); +// logger.info("hostList:{}", JSON.toJSONString(hostList)); + + //查询宿主机下的虚拟机 + associateHostVms(hostList); +// logger.info("hostList:{}", JSON.toJSONString(hostList)); + + //关联宿主机下的存储 + associateHostDataStores(hostList); +// logger.info("hostList:{}", JSON.toJSONString(hostList)); + + hostList = calculateHostsRemainingResource(vendor, hostList); + logger.info("资源变更分配宿主机列表:{}", JSON.toJSONString(hostList)); + List resultObjs = doAssign(sort, assignParamList, resourceRelationType, schedulerPolicy, hostList); + logger.info("resultObjs:{}", JSON.toJSONString(resultObjs)); + return resultObjs; + } + + + + + /** + * 默认调用此方法前已经完成了剩余资源的计算 + * 按照DataStore的大小,从大到小排列,获取最大的存储空间创建磁盘 + * + * @return + */ + private AssignResult assignSystemAndDataDiskStore(AssignParam assignParam, Server server) { + AssignResultManager assignResultManager = new AssignResultManager(); + AssignResult assignResult = new AssignResult(); + assignResult.setSuccess(false); + //获取计算过最新剩余大小的存储,且应该都是可用的 + List dataStoreList = server.getDataStores(); + assignResultManager.save(dataStoreList); + //按照剩余空间从大到小排 + dataStoreList.sort((o1, o2) -> o2.getSpaceRemain().compareTo(o1.getSpaceRemain())); + + for (DataStore dataStore : dataStoreList) { + if (dataStore.getSpaceRemain() > assignParam.getDisk()) { + dataStore.setSpaceRemain(dataStore.getSpaceRemain() - assignParam.getDisk()); + assignResult.setStoreId(dataStore.getId()); + break; + } + } + + //系统盘分配出来了才继续往下分配 + if (assignResult.getStoreId() != null) { + + List dataDiskParams = assignParam.getDataDiskParams(); + List assignDataDiskResultList = new ArrayList<>(); + if (!CollectionUtils.isEmpty(dataDiskParams)) { + for (AssignDataDiskParam dataDiskParam : dataDiskParams) { + //每次都重新排序获取最大的存储 + dataStoreList.sort((o1, o2) -> o2.getSpaceRemain().compareTo(o1.getSpaceRemain())); + + for (DataStore dataStore : dataStoreList) { + if (dataStore.getSpaceRemain() > dataDiskParam.getSize()) { + //保存storeName + dataStore.setSpaceRemain(dataStore.getSpaceRemain() - dataDiskParam.getSize()); + AssignDataDiskResult diskResult = new AssignDataDiskResult(); + diskResult.setHostId(server.getId()); + diskResult.setStoreId(dataStore.getId()); + diskResult.setSize(dataDiskParam.getSize()); + diskResult.setStoreName(dataStore.getName()); + diskResult.setStoreDeviceUuid(dataStore.getGuid()); + //设置关联的申请参数 + diskResult.setAssignDataDiskParam(dataDiskParam); + assignDataDiskResultList.add(diskResult); + break; + } + } + } + } + assignResult.setAssignDataDiskResultList(assignDataDiskResultList); + //申请数量和分配数量一致才算成功 + if (!CollectionUtils.isEmpty(dataDiskParams)) { + assignResult.setSuccess(dataDiskParams.size() == assignDataDiskResultList.size()); + } else { + assignResult.setSuccess(true); + } + + } + + //分配失败,回滚空间 + if (!assignResult.getSuccess()) { + assignResultManager.rollback(dataStoreList); + } + return assignResult; + } + + + private void sortCommon(List resource, String sort, Boolean reverse, Function getCpuRemainder, Function getMemoryRemainder, Function getDiskRemainder) { + Collections.sort(resource, (first, second) -> { + if (null == first || second == null) { + return 0; + } + Long preCpuRemainder = getCpuRemainder.apply(first); + Long preMemRemainder = getMemoryRemainder.apply(first); + Long preDiskRemainder = getDiskRemainder.apply(first); + Long nextCpuRemainder = getCpuRemainder.apply(second); + Long nextMemRemainder = getCpuRemainder.apply(second); + Long nextDiskRemainder = getCpuRemainder.apply(second); + if (reverse) { + preCpuRemainder = getCpuRemainder.apply(second); + preMemRemainder = getCpuRemainder.apply(second); + preDiskRemainder = getCpuRemainder.apply(second); + nextCpuRemainder = getCpuRemainder.apply(first); + nextMemRemainder = getMemoryRemainder.apply(first); + nextDiskRemainder = getMemoryRemainder.apply(first); + } + if ("Cpu".equalsIgnoreCase(sort)) { + return nextCpuRemainder.compareTo(preCpuRemainder); + } else if ("Memory".equalsIgnoreCase(sort)) { + return nextMemRemainder.compareTo(preMemRemainder); + } else if ("Disk".equalsIgnoreCase(sort)) { + return nextDiskRemainder.compareTo(preDiskRemainder); + } else { + return nextCpuRemainder.compareTo(preCpuRemainder); + } + }); + } + + + private void sortWithType(List hosts, String sort, Boolean reverse, String type) { + if (type != null) { + type = type.toLowerCase(); + //根据不同资源类型进行分散分配 + switch (type) { + case "cluster": + Map> clusterGroup = hosts.stream().collect(Collectors.groupingBy(Server::getClusterId)); + //宿主机按照集群分组 + List clusterList = clusterGroup.keySet().stream().map(clusterId -> { + List servers = clusterGroup.get(clusterId); + Cluster cluster = new Cluster(); + cluster.setId(clusterId); + cluster.setCpuRemainder(servers.stream().mapToLong(Server::getCpuRemainder).sum()); + cluster.setMemoryRemainder(servers.stream().mapToLong(Server::getMemoryRemainder).sum()); + cluster.setDiskRemainder(servers.stream().mapToLong(Server::getDiskRemainder).sum()); + cluster.setHostNum(servers.size()); + //排序集群下宿主机 + sort(servers, sort, reverse); + cluster.setServers(servers); + + return cluster; + }).collect(Collectors.toList()); + //先排序集群 + sortCommon(clusterList, sort, reverse, Cluster::getCpuRemainder, Cluster::getMemoryRemainder, Cluster::getDiskRemainder); + + //按照集群维度分散分配 + int maxSize = clusterList.stream().mapToInt(Cluster::getHostNum).max().orElse(0); + + + List newHosts = new ArrayList<>(); + for (int i = 0; i < maxSize; i++) { + for (Cluster cluster : clusterList) { + if (!CollectionUtils.isEmpty(cluster.getServers())) { + //每次获取剩余资源最大的宿主机 + Server server = cluster.getServers().remove(0); + newHosts.add(server); + } + } + } + //为了适配上层调用,这里暂时只能使用这种方式改变排序结果 + hosts.clear(); + hosts.addAll(newHosts); + break; + case "vdc": + Map> vdcGroup = hosts.stream().collect(Collectors.groupingBy(Server::getVdcId)); + //宿主机按照集群分组 + List vdcList = vdcGroup.keySet().stream().map(vdcId -> { + List servers = vdcGroup.get(vdcId); + Vdc vdc = new Vdc(); + vdc.setId(vdcId); + vdc.setCpuRemainder(servers.stream().mapToLong(Server::getCpuRemainder).sum()); + vdc.setMemoryRemainder(servers.stream().mapToLong(Server::getMemoryRemainder).sum()); + vdc.setDiskRemainder(servers.stream().mapToLong(Server::getDiskRemainder).sum()); + vdc.setHostNum((long) servers.size()); + vdc.setServers(servers); + return vdc; + }).collect(Collectors.toList()); + //先排序集群 + sortCommon(vdcList, sort, reverse, Vdc::getCpuRemainder, Vdc::getMemoryRemainder, Vdc::getDiskRemainder); + + //按照VDC维度分散分配 + long maxVdcHosts = vdcList.stream().mapToLong(Vdc::getHostNum).max().orElse(0); + List newVdcHost = new ArrayList<>(); + for (int i = 0; i < maxVdcHosts; i++) { + for (Vdc vdc : vdcList) { + if (i < vdc.getHostNum()) { + Server server = vdc.getServers().get(i); + newVdcHost.add(server); + } + } + } + //为了适配上层调用,这里暂时只能使用这种方式改变排序结果 + hosts.clear(); + hosts.addAll(newVdcHost); + break; + case "host": + default: + this.sort(hosts, sort, reverse); + } + } + } + + private List doAssign(String sort, List assignParamList, String resourceRelationType, SchedulerPolicy.policy policy, List hosts) { + List assignResults = new ArrayList<>(); + long cpuNeed = assignParamList.stream().mapToLong(param -> param.getCpu() * param.getInstance()).sum(); + long memoryNeed = assignParamList.stream().mapToLong(param -> param.getMemory() * param.getInstance()).sum(); + switch (policy) { + case focus: + // 对集群进行排序,如果用户选择资源密集型策略,则倒叙排列,然后取资源剩余最适合分配的那个集群,否则正序排列 + this.sort(hosts, sort, true); + // 资源集中式分配,不用考虑具体规格(CPU,内存等) + for (Server host : hosts) { + logger.info("getCpuRemainder:" + host.getCpuRemainder()); + logger.info("getMemoryRemainder:" + host.getMemoryRemainder()); + logger.info("cpuNeed:" + cpuNeed); + logger.info("memoryNeed:" + memoryNeed); + if (cpuNeed <= host.getCpuRemainder() + && memoryNeed <= host.getMemoryRemainder()) { + logger.info(""); + for (AssignParam assignParam : assignParamList) { + AssignResult assignResult = new AssignResult(); + assignResult.setHostId(host.getId()); + assignResult.setVdcId(host.getVdcId()); + assignResult.setClusterId(host.getClusterId()); + AssignResult result = assignSystemAndDataDiskStore(assignParam, host); + Assert.isTrue(result.getSuccess(), "存储资源不足分配失败"); + assignResult.setAssignId(assignParam.getUuid()); + assignResult.setAssignParam(assignParam); + assignResults.add(assignResult); + } + break; + } + } + break; + case dispersion: + //与宿主机剩余资源排序保持一致,则剩余资源最大的宿主可以匹配占用资源最大的虚拟机 + this.sortCommon(assignParamList, sort, false, AssignParam::getCpu, AssignParam::getMemory, AssignParam::getDisk); + this.sortWithType(hosts, sort, false, resourceRelationType); + for (AssignParam assignParam : assignParamList) { + assignParam.setRemain(assignParam.getInstance()); + } + int retryCount = 0; + for (int i = 0; i < hosts.size() && retryCount < hosts.size() && assignResults.size() < assignParamList.size(); i++) { + Server host = hosts.get(i); + Long cpuRemainder = host.getCpuRemainder(); + Long memoryRemainder = host.getMemoryRemainder(); + boolean assigned = false; +// if (host.getVmRemainder() > 0) { + for (AssignParam assignParam : assignParamList) { + //判断宿主机剩余资源是否足够 + if (assignParam.getRemain() > 0 + && assignParam.getCpu() <= cpuRemainder + && assignParam.getMemory() <= memoryRemainder) { + AssignResult assignResult = this.assignSystemAndDataDiskStore(assignParam, host); + if (!assignResult.getSuccess()) { + continue; + } + + assignResult.setHostId(host.getId()); + assignResult.setVdcId(host.getVdcId()); + assignResult.setAssignId(assignParam.getUuid()); + assignResult.setAssignParam(assignParam); + assignResult.setClusterId(host.getClusterId()); + assignResults.add(assignResult); + host.setCpuRemainder(cpuRemainder - assignParam.getCpu()); + host.setMemoryRemainder(memoryRemainder - assignParam.getMemory()); + + assignParam.setRemain(assignParam.getRemain() - 1); +// host.setVmRemainder(host.getVmRemainder() - 1); + assigned = true; + //分配到一个之后就中断,分配下一个宿主机 + break; + } + } +// } + if (!assigned) { + retryCount++; + } else { + retryCount = 0; + } + //重新回到第一个宿主机继续分配 + if (i == hosts.size() - 1) { + i = -1; + } + } + break; + case random: + Server host = hosts.get(new Random().nextInt(hosts.size())); + for (AssignParam assignParam : assignParamList) { + AssignResult assignResult = new AssignResult(); + assignResult.setHostId(host.getId()); + assignResult.setVdcId(host.getVdcId()); + assignResult.setClusterId(host.getClusterId()); + List dataStoreList = host.getDataStores(); + if (!CollectionUtils.isEmpty(dataStoreList)) { + DataStore dataStore = dataStoreList.get(new Random().nextInt(dataStoreList.size())); + assignResult.setStoreId(dataStore.getId()); + } + assignResult.setAssignId(assignParam.getUuid()); + assignResult.setAssignParam(assignParam); + assignResults.add(assignResult); + } + break; + default: + break; + } + return assignResults; + } + + + + + private void calculateHostRemain(CloudVendor cloudVendor, Server host, ServerConfig config, List vms) { + long cpuTotal; + long memoryTotal = 0; + //计算资源剩余统计,安超的聚合组就是集群, + //cpu超分比默认取平台 + Long cpuExcessRatio = cloudVendor.getExcessRatio(); + + //虚拟化比 +// host.setVmRemainder(cloudVendor.getVirtualizationRatio().intValue()); +// //统一计算剩余可分配虚拟机数量 +// if (vms == null) { +// vms = new ArrayList<>(); +// } +// host.setVmRemainder(host.getVmRemainder() - vms.size()); + + + // 计算每个宿主机剩余可用cpu,内存,存储容量,看是否有可分配的主机 + int cpuUsed = vms.stream().mapToInt(CloudServer::getCpu).sum(); + BigDecimal memUsed = null; + if (CloudProvider.VMWARE.name().equals(cloudVendor.getType())) { + HostOperateModel model = new HostOperateModel(); + model.setHostName(host.getName()); + try { + BocloudResult detail = hostDriver.detail(cloudVendor.getUuid(), null, model); + Assert.isTrue(detail.isSuccess(), detail.getMessage()); + JSONObject jsonObject = JSONObject.parseObject(JSONObject.toJSONString(detail.getData())); + if (jsonObject.getBigDecimal("memUsed") != null) { + memUsed = jsonObject.getBigDecimal("memUsed"); + memoryTotal = jsonObject.getLong("memTotal"); + logger.info("当前宿主机名称:" + host.getName() + ",已使用内存:" + memUsed + "G" + ",内存总量:" + memoryTotal + "G"); + } else { + Assert.isTrue(false, "获取vmware宿主机详情失败"); + } + } catch (Exception e) { + logger.error("获取vmware宿主机信息失败:", e); + Assert.isTrue(false, e.getMessage()); + } + } else { + memUsed = vms.stream().map(CloudServer::getMemory).reduce(BigDecimal::add).orElse(BigDecimal.ZERO); + //内存没有超分比 + memoryTotal = null == config.getMemoryCapacity() ? 0 : + BigDecimal.valueOf(config.getMemoryCapacity()).longValue(); + } + + cpuTotal = BigDecimal.valueOf(config.getCpuCore()).multiply(BigDecimal.valueOf(cpuExcessRatio)).longValue(); + logger.info("cpuTotal:" + cpuTotal); + logger.info("cpuUsed:" + cpuUsed); + host.setCpuRemainder(Math.max(cpuTotal - cpuUsed, 0L)); + + +// //判断当前内存使用量是否超出阈值设置 +// if (BigDecimal.valueOf(memoryTotal).multiply(BigDecimal.valueOf(cloudVendor.getMemoryRatioPre())).divide(BigDecimal.valueOf(100)).compareTo(memUsed) <= 0) { +// host.setMemoryRemainder(0L); +// } else { +// // 使用分配后的阈值计算可以用内存的量 +// memoryTotal = BigDecimal.valueOf(memoryTotal).multiply(BigDecimal.valueOf(cloudVendor.getMemoryRatioPost())).divide(BigDecimal.valueOf(100)).longValue(); +// + logger.info("memoryTotal:" + memoryTotal); + logger.info("memUsed:" + memUsed); + host.setMemoryRemainder(Math.max(memoryTotal - memUsed.longValue(), 0L)); +// } + } + + + private void calculateStoreRemain(CloudVendor vendor, DataStore store) { +// Long storageRatioPre = vendor.getStorageRatioPre(); + BigDecimal spaceUsed = BigDecimal.valueOf(store.getCapacity() - store.getFreeSpace()); + + //判断已使用空间是否小于阈值 +// if (BigDecimal.valueOf(store.getCapacity()).multiply(BigDecimal.valueOf(storageRatioPre)) +// .divide(BigDecimal.valueOf(100)).compareTo(spaceUsed) > 0) { +// +// //计算剩余可用容量 +// Long storageRatioPost = vendor.getStorageRatioPost(); + //可用容量 + BigDecimal availableSpace = BigDecimal.valueOf(store.getCapacity()); + + logger.info("availableSpace:" + availableSpace); + logger.info("spaceUsed:" + spaceUsed); + logger.info("spaceRemain" + availableSpace.subtract(spaceUsed).longValue()); + store.setSpaceRemain(availableSpace.subtract(spaceUsed).longValue()); +// } else { +// store.setSpaceRemain(0L); +// } + } + + /** + * 统一计算宿主机剩余资源,会计算宿主机剩余计算资源,宿主机可以访问到的存储资源 + * + * @return true表示有剩余资源,可以参与后续资源分配 + */ + public boolean calculateHostRemainingResource(CloudVendor vendor, Server host) { + if (!BocloudStatus.Server.RUNNING.name().equals(host.getStatus())) { + return false; + } + ServerConfig config = host.getConfig(); + if (null == config) { + return false; + } + + List vms = host.getServers(); + calculateHostRemain(vendor, host, config, vms); + logger.info("host:{}", JSON.toJSONString(host)); + List dataStoreList = host.getDataStores(); + if (host.getCpuRemainder() >= 0 && host.getMemoryRemainder() >= 0) { + long diskTotalRemain = 0; + if (CollectionUtils.isEmpty(dataStoreList)) { + return false; + } + for (DataStore store : dataStoreList) { + calculateStoreRemain(vendor, store); + if (store.getSpaceRemain() > 0) { + //统计剩余容量 + diskTotalRemain += store.getSpaceRemain(); + } + } + logger.info("dataStoreList:{}", JSON.toJSONString(dataStoreList)); + dataStoreList = dataStoreList.stream().filter(store -> store.getSpaceRemain() > 0).collect(Collectors.toList()); + host.setDiskRemainder(Math.max(diskTotalRemain, 0L)); + //新建存储和宿主机的关系 + List storeHosts = dataStoreList.stream().map(store -> { + StoreHost storeHost = new StoreHost(); + storeHost.setVhostId(host.getId()); + storeHost.setStoreId(store.getId()); + return storeHost; + }).collect(Collectors.toList()); + + //设置新的关系 + host.setStoreHosts(storeHosts); + host.setDataStores(dataStoreList); + return !dataStoreList.isEmpty(); + } + return false; + } + + + public List calculateHostsRemainingResource(CloudVendor vendor, Collection hosts) { + List newHosts = new ArrayList<>(); + for (Server host : hosts) { +// logger.info("host:{}", JSON.toJSONString(host)); + if (calculateHostRemainingResource(vendor, host)) { + newHosts.add(host); + } + } + return newHosts; + } + + + /** + * 配置云平台指标 + * + * @param cloudVendor + * @param hosts + */ + public void configVendor(CloudVendor cloudVendor, List hosts) { + JSONObject authentication = JSONObject.parseObject(cloudVendor.getAuthentication()); + cloudVendor.setExcessRatio(Optional.ofNullable(authentication.getLong("excessRatio")).orElse(100L)); +// cloudVendor.setVirtualizationRatio(Optional.ofNullable(authentication.getLong("virtualizationRatio")).orElse(100L)); +// cloudVendor.setMemoryRatioPost(Optional.ofNullable(authentication.getLong("memoryRatioPost")).orElse(85L)); +// cloudVendor.setMemoryRatioPre(Optional.ofNullable(authentication.getLong("memoryRatioPre")).orElse(80L)); +// cloudVendor.setStorageRatioPost(Optional.ofNullable(authentication.getLong("storageRatioPost")).orElse(85L)); +// cloudVendor.setStorageRatioPre(Optional.ofNullable(authentication.getLong("storageRatioPre")).orElse(80L)); + for (Server host : hosts) { + host.setVendor(cloudVendor); + } + } + + + /** + * 将宿主机的配置设置到config字段 + */ + public void configHosts(Collection hosts) { + // 统一查询宿主机 + Set hostIds = hosts.stream().map(Server::getId).collect(Collectors.toSet()); + List list = null; + if (hostIds.isEmpty()) { + list = new ArrayList<>(); + } else { + list = serverConfigRepository.queryProxy().col(ServerConfig::getServerId).in(hostIds).list(); + } + Map hostIdMapConfig = list.stream().collect(Collectors.toMap(ServerConfig::getServerId, Function.identity(), (f, s) -> f)); + for (Server host : hosts) { + host.setConfig(hostIdMapConfig.get(host.getId())); + } + } + + + public void associateHostVms(Collection hosts) { + Set hostIds = hosts.stream().map(Server::getId).collect(Collectors.toSet()); + Assert.notEmpty(hostIds, "资源池中没有合适的宿主机"); + //统一查询虚拟机 + List cloudServerList = cloudServerRepository.queryProxy() + .col(CloudServer::getHostId).in(hostIds) + .col(GenericEntity::getDeleted).notEq(1) + .col(CloudServer::getIsTemplate).notEq(1) + // 只查询活动状态下的虚拟机进行计算 + .col(CloudServer::getStatus).in("BUILDING", "RUNNING") + .list(); + logger.info("当前所有宿主机: {},下的虚拟机信息:{}", JSON.toJSONString(hostIds), JSON.toJSONString(cloudServerList)); + Map> cloudServerMap = cloudServerList.stream().collect(Collectors.groupingBy(CloudServer::getHostId)); + for (Server host : hosts) { + host.setServers(cloudServerMap.get(host.getId())); + } + } + + + /** + * 关联主机存储 + * 该方法是为了统计最新的存储状态 + * 并将宿主机的dataStores字段赋值 + * + * @return + */ + public void associateHostDataStores(Collection hosts) { + /** 处理存储关联关系,由于cloudTower超融合没有单独存储,存储挂在主机下面可以模拟创建一个dataStore对象,每台宿主机有一个存储对象 + 后续进行dataStore分配的时候需要通过存储对象来调度,vc是每个宿主机挂载多个存储,安超和cloudTower需要适配mock出宿主机和存储的关联关系 + **/ + Map> vendorMapHosts = hosts.stream().collect(Collectors.groupingBy(Server::getVendorId)); + for (Map.Entry> entry : vendorMapHosts.entrySet()) { + List hostList = entry.getValue(); + CloudVendor vendor = hostList.get(0).getVendor(); + CloudProvider provider = CloudProvider.valueOf(vendor.getType()); + + List hostIds = hostList.stream().map(Server::getId).collect(Collectors.toList()); + switch (provider) { +// case VMWARE: +// associateVmwareHostsDataStore(hosts, vendor, hostIds); +// break; + case CLOUDTOWER: + associateCloudTowerHostsDataStore(hostList); + break; +// case ARCHEROS: +// associatArcherOsHostsDataStore(hostList); +// break; + } + } + } + + + private static void associateCloudTowerHostsDataStore(List hostList) { + for (Server server : hostList) { + ServerConfig config = server.getConfig(); + DataStore dataStore = new DataStore(); + //设置一个模拟的id,负数Id不会和数据库中的冲突 + dataStore.setId(-new Random().nextLong()); + dataStore.setCapacity(config.getDiskCapacity().longValue()); + dataStore.setFreeSpace(config.getDiskCapacity().longValue() - config.getDiskUsed().longValue()); + server.setDataStores(Arrays.asList(dataStore)); + } + } + } diff --git a/bocloud.ims.service/src/main/java/com/bocloud/ims/service/resource/VMApplyServiceImpl.java b/bocloud.ims.service/src/main/java/com/bocloud/ims/service/resource/VMApplyServiceImpl.java index 63f6709..a4eda74 100644 --- a/bocloud.ims.service/src/main/java/com/bocloud/ims/service/resource/VMApplyServiceImpl.java +++ b/bocloud.ims.service/src/main/java/com/bocloud/ims/service/resource/VMApplyServiceImpl.java @@ -11,6 +11,7 @@ import com.bocloud.cmp.entity.enums.ResourceCategory; import com.bocloud.cmp.model.*; import com.bocloud.cmp.model.common.*; import com.bocloud.cmp.model.common.OrderMessage.Operation; +import com.bocloud.cmp.vm.model.CloudTowerDisk; import com.bocloud.cmp.vm.model.VmGenericParam; import com.bocloud.common.encrypt.AESEncrptor; import com.bocloud.common.encrypt.Encryptor; @@ -410,6 +411,46 @@ public class VMApplyServiceImpl implements VMApplyService { } } + +// assignMapTarget = new HashMap<>(); + List assignParamListBak = new ArrayList<>(); + //ct分配资源 + if (provider == CloudProvider.CLOUDTOWER) { + List assignParamList = new ArrayList<>(); + AssignParam assignParam = param.getAssignParam(); + //设置系统盘 + assignParam.setDisk(assignParam.getTemplateDisk()); + + //处理数据盘 + List cloudTowerDisks = Optional.ofNullable(assignParam.getOriginalConfigs()).map(c -> c.getJSONArray("volumeModels")).map(a -> a.toJavaList(CloudTowerDisk.class)).orElse(new ArrayList<>()); + + //设置数据盘申请参数 + List dataDiskParams = cloudTowerDisks.stream().map(AssignDataDiskParam::new).collect(Collectors.toList()); + assignParam.setDataDiskParams(dataDiskParams); + + assignParamList.add(assignParam); + PoolGroup poolGroup = poolGroupRepository.query(location.getPoolGroupId()); + + List assignResultList = resSchedulerEngine.assign(poolGroup, vendor, assignParamList); + + Map> assignMapTarget = assignResultList.stream().collect(Collectors.groupingBy(obj -> { + String assignId = obj.getAssignId(); + if (assignId == null) { + assignId = ""; + } + return assignId; + })); + + + Assert.notEmpty(assignMapTarget, "云主机调度失败"); + //如果有uuid关联的分配结果则一般为反亲和 + if (assignMapTarget.containsKey(assignParam.getUuid())) { + assignParamListBak = assignMapTarget.get(assignParam.getUuid()); + } else {//如果没有uuid关联的分配结果,则为随机或者亲和 + assignParamListBak = assignMapTarget.get(""); + } + } + if (CloudProvider.VMWARE == provider) { BocloudResult> bocloudResult = null; // 如果没有使用计算资源池,则默认按照随机策略分配宿主机和存储 @@ -493,6 +534,7 @@ public class VMApplyServiceImpl implements VMApplyService { } } + logger.info("分配后的IP信息:" + JSON.toJSONString(configs.getString("networkCardConfigs"))); if (provider.equals(CloudProvider.SANGFOR) && "6.8".equals(vendor.getVersion())){ //获取资源池Id @@ -519,16 +561,16 @@ public class VMApplyServiceImpl implements VMApplyService { JSONArray networkCardConfigs1 = configs.getJSONArray("networkCardConfigs"); for (Object netWork : networkCardConfigs1){ JSONObject jsonObject = (JSONObject) netWork; - JSONArray address = jsonObject.getJSONArray("address"); + JSONArray address = provider.equals(CloudProvider.CLOUDTOWER) ? jsonObject.getJSONArray("ipAddress") : jsonObject.getJSONArray("address"); Long ipPoolId = jsonObject.getLong("ipPoolId"); - if (address.isEmpty()){ + if (address.isEmpty()) { BocloudResult> listBocloudResult = ipService.achievePoolIps(ipPoolId, count.longValue(), null); List ips = listBocloudResult.getData().stream().map(Ip::getIp).collect(Collectors.toList()); - logger.info("打印分配到的ip:{}",JSON.toJSONString(ips)); + logger.info("打印分配到的ip:{}", JSON.toJSONString(ips)); address = JSONObject.parseArray(JSON.toJSONString(ips)); - jsonObject.put("address",address); + jsonObject.put("address", address); } - logger.info("打印address:{}",JSON.toJSONString(address)); + logger.info("打印address:{}", JSON.toJSONString(address)); } for (int i = 0; i < count; i++) { JSONObject newObj = JSONObject.parseObject(configs.toJSONString()); @@ -550,7 +592,7 @@ public class VMApplyServiceImpl implements VMApplyService { for (Object object : networkCardConfigs) { JSONObject jsonObject = (JSONObject) object; BocloudResult userDetail = smsUserService.detail(location.getCreatorId()); - newObj.put("name", jsonObject.getJSONArray("address").get(i) +"-"+ userDetail.getData().getName() +"-"+purpose); + newObj.put("name", (provider.equals(CloudProvider.CLOUDTOWER) ? jsonObject.getJSONArray("ipAddress") : jsonObject.getJSONArray("address")).get(i) + "-" + userDetail.getData().getName() + "-" + purpose); break; } } @@ -603,6 +645,26 @@ public class VMApplyServiceImpl implements VMApplyService { } + if (!ListTool.isEmpty(assignParamListBak)) { + + AssignResult targetObj = assignParamListBak.get(i); + //设置宿主机,系统盘存储位置 + newObj.put("storeId", targetObj.getStoreId()); + newObj.put("hostId", targetObj.getHostId()); + newObj.put("vdcId", targetObj.getVdcId()); + //冗余一个宿主机ID,cloudTower通过serverId来控制磁盘 + newObj.put("serverId", targetObj.getHostId()); + newObj.put("clusterId", targetObj.getClusterId()); + + //这个属性创建时暂时没有找到使用的地方,后续可以考虑删除 + newObj.put("dataDiskList", newObj.getString("addDiskList")); + newObj.put("diskDevices", newObj.get("volumeModels")); + newObj.put("networkCards", newObj.get("networkCardConfigs")); + // 暂时注释,解决没有数据盘问题 +// newObj.remove("addDiskList"); + + } + if (!ListTool.isEmpty(targetObjs)) { JSONObject targetObj = targetObjs.get(i); if (hostId != null && hostId.longValue() > 0L) { @@ -680,6 +742,15 @@ public class VMApplyServiceImpl implements VMApplyService { } else if (CloudProvider.SANGFOR == provider){ result = serverOperationHelper.prepareSangfor(object, param.getContext(), vendor, process, indexInSpec, size); } else { + + if (CloudProvider.CLOUDTOWER == provider) { + //ct的特殊处理 + JSONArray networkCardConfigs = object.getJSONArray("networkCardConfigs"); + for (Object networkCardConfig : networkCardConfigs) { + JSONObject jsonObject = (JSONObject) networkCardConfig; + jsonObject.put("ipAddress", jsonObject.getJSONArray("ipAddress").get(indexInSpec)); + } + } result = serverOperationHelper.prepare( JSONObject.parseObject(object.toJSONString(), VmGenericParam.class), param.getContext(), vendor, process);