diff --git a/header/src/main/java/org/zstack/header/volume/MaxDataVolumeNumberExtensionPoint.java b/header/src/main/java/org/zstack/header/volume/MaxDataVolumeNumberExtensionPoint.java index 36427bf6951..0ab65f307f2 100755 --- a/header/src/main/java/org/zstack/header/volume/MaxDataVolumeNumberExtensionPoint.java +++ b/header/src/main/java/org/zstack/header/volume/MaxDataVolumeNumberExtensionPoint.java @@ -6,4 +6,8 @@ public interface MaxDataVolumeNumberExtensionPoint { String getHypervisorTypeForMaxDataVolumeNumberExtension(); int getMaxDataVolumeNumber(); + + default int getMaxDataVolumeNumberForRunningVm() { + return getMaxDataVolumeNumber(); + } } diff --git a/plugin/kvm/src/main/java/org/zstack/kvm/KVMConstant.java b/plugin/kvm/src/main/java/org/zstack/kvm/KVMConstant.java index 1b2df9f8f2a..33ec5c90baa 100755 --- a/plugin/kvm/src/main/java/org/zstack/kvm/KVMConstant.java +++ b/plugin/kvm/src/main/java/org/zstack/kvm/KVMConstant.java @@ -160,6 +160,11 @@ public interface KVMConstant { Integer DEFAULT_MAX_NIC_QUEUE_NUMBER = 12; + // Maximum data volumes for hot-plug (running VM) due to PCI bus slot constraints. + // i440fx provides 32 PCI slots; ~12 are reserved by system devices and libvirt PCI bridge, + // leaving ~20 slots for hot-plugged virtio-blk data volumes (SharedBlock storage scenario). + int KVM_MAX_DATA_VOLUME_NUMBER_HOT_PLUG = 20; + String CONNECT_HOST_PRIMARYSTORAGE_ERROR = "psError"; String VIRTUALIZER_QEMU_KVM = "qemu-kvm"; diff --git a/plugin/kvm/src/main/java/org/zstack/kvm/KVMHostFactory.java b/plugin/kvm/src/main/java/org/zstack/kvm/KVMHostFactory.java index 0188c920a83..2050e483abf 100755 --- a/plugin/kvm/src/main/java/org/zstack/kvm/KVMHostFactory.java +++ b/plugin/kvm/src/main/java/org/zstack/kvm/KVMHostFactory.java @@ -1098,6 +1098,11 @@ public int getMaxDataVolumeNumber() { return maxDataVolumeNum; } + @Override + public int getMaxDataVolumeNumberForRunningVm() { + return Math.min(maxDataVolumeNum, KVMConstant.KVM_MAX_DATA_VOLUME_NUMBER_HOT_PLUG); + } + @Override @AsyncThread public void managementNodeReady() { diff --git a/storage/src/main/java/org/zstack/storage/volume/VolumeBase.java b/storage/src/main/java/org/zstack/storage/volume/VolumeBase.java index d75fb654a3e..2a81910337c 100755 --- a/storage/src/main/java/org/zstack/storage/volume/VolumeBase.java +++ b/storage/src/main/java/org/zstack/storage/volume/VolumeBase.java @@ -3272,7 +3272,10 @@ static void vmAttachVolumeValidator(VmInstanceInventory vmInv, String volumeUuid PluginRegistry pluginRgty = Platform.getComponentLoader().getComponent(PluginRegistry.class); MaxDataVolumeNumberExtensionPoint ext = pluginRgty.getExtensionFromMap(hypervisorType, MaxDataVolumeNumberExtensionPoint.class); - int maxDataVolumeNum = ext == null ? VolumeConstant.DEFAULT_MAX_DATA_VOLUME_NUMBER : ext.getMaxDataVolumeNumber(); + boolean isRunning = VmInstanceState.Running.toString().equals(vmInv.getState()) + || VmInstanceState.Paused.toString().equals(vmInv.getState()); + int maxDataVolumeNum = ext == null ? VolumeConstant.DEFAULT_MAX_DATA_VOLUME_NUMBER + : (isRunning ? ext.getMaxDataVolumeNumberForRunningVm() : ext.getMaxDataVolumeNumber()); long vmDataVolumeUsage = Q.New(VolumeVO.class) .eq(VolumeVO_.type, VolumeType.Data)