package org.zstack.compute.vm; import org.springframework.beans.factory.annotation.Autowire; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Configurable; import org.zstack.core.componentloader.PluginRegistry; import org.zstack.core.db.DatabaseFacade; import org.zstack.core.db.SimpleQuery; import org.zstack.core.db.SimpleQuery.Od; import org.zstack.core.db.SimpleQuery.Op; import org.zstack.core.errorcode.ErrorFacade; import org.zstack.header.core.workflow.Flow; import org.zstack.header.core.workflow.FlowRollback; import org.zstack.header.core.workflow.FlowTrigger; import org.zstack.header.errorcode.OperationFailureException; import org.zstack.header.errorcode.SysErrors; import org.zstack.header.vm.VmInstanceConstant; import org.zstack.header.vm.VmInstanceSpec; import org.zstack.header.volume.VolumeInventory; import org.zstack.header.volume.VolumeVO; import org.zstack.header.volume.VolumeVO_; import org.zstack.utils.Utils; import org.zstack.utils.logging.CLogger; import static org.zstack.core.Platform.err; import java.util.BitSet; import java.util.List; import java.util.Map; @Configurable(preConstruction = true, autowire = Autowire.BY_TYPE) public class VmAssignDeviceIdToAttachingVolumeFlow implements Flow { CLogger logger = Utils.getLogger(VmAssignDeviceIdToAttachingVolumeFlow.class); @Autowired private DatabaseFacade dbf; @Autowired private PluginRegistry pluginRegistry; @Autowired ErrorFacade errf; private int getNextVolumeDeviceId(String vmUuid) { SimpleQuery<VolumeVO> q = dbf.createQuery(VolumeVO.class); q.select(VolumeVO_.deviceId); q.add(VolumeVO_.vmInstanceUuid, Op.EQ, vmUuid); q.add(VolumeVO_.deviceId, Op.NOT_NULL); q.orderBy(VolumeVO_.deviceId, Od.ASC); List<Integer> devIds = q.listValue(); BitSet full = new BitSet(devIds.size() + 1); devIds.forEach(full::set); return full.nextClearBit(0); } @Override public void run(FlowTrigger chain, Map ctx) { final VolumeInventory volume = (VolumeInventory) ctx.get(VmInstanceConstant.Params.AttachingVolumeInventory.toString()); final VmInstanceSpec spec = (VmInstanceSpec) ctx.get(VmInstanceConstant.Params.VmInstanceSpec.toString()); VolumeVO dvol = dbf.findByUuid(volume.getUuid(), VolumeVO.class); List<GetNextVolumeDeviceIdExtensionPoint> exts = pluginRegistry.getExtensionList( GetNextVolumeDeviceIdExtensionPoint.class); if (exts == null || exts.isEmpty()) { dvol.setDeviceId(getNextVolumeDeviceId(spec.getVmInventory().getUuid())); } else if (exts.size() == 1) { dvol.setDeviceId(exts.get(0).getNextVolumeDeviceId(spec.getVmInventory().getUuid())); } else { throw new OperationFailureException(err(SysErrors.INTERNAL, "should not be more than one GetNextVolumeDeviceIdExtensionPoint implementation")); } dvol = dbf.updateAndRefresh(dvol); ctx.put(VmInstanceConstant.Params.AttachingVolumeInventory.toString(), VolumeInventory.valueOf(dvol)); chain.next(); } @Override public void rollback(FlowRollback chain, Map data) { final VolumeInventory volume = (VolumeInventory) data.get(VmInstanceConstant.Params.AttachingVolumeInventory.toString()); VolumeVO dvol = dbf.findByUuid(volume.getUuid(), VolumeVO.class); dvol.setDeviceId(null); dbf.update(dvol); chain.rollback(); } }