diff --git a/controller/src/main/java/com/linbit/linstor/core/apicallhandler/controller/CtrlRscToggleDiskApiCallHandler.java b/controller/src/main/java/com/linbit/linstor/core/apicallhandler/controller/CtrlRscToggleDiskApiCallHandler.java index 614d460fa..aa6b15dc7 100644 --- a/controller/src/main/java/com/linbit/linstor/core/apicallhandler/controller/CtrlRscToggleDiskApiCallHandler.java +++ b/controller/src/main/java/com/linbit/linstor/core/apicallhandler/controller/CtrlRscToggleDiskApiCallHandler.java @@ -54,6 +54,7 @@ import com.linbit.linstor.storage.StorageException; import com.linbit.linstor.storage.data.adapter.drbd.DrbdRscData; import com.linbit.linstor.storage.interfaces.categories.resource.AbsRscLayerObject; +import com.linbit.linstor.core.types.TcpPortNumber; import com.linbit.linstor.storage.kinds.DeviceLayerKind; import com.linbit.linstor.storage.utils.LayerUtils; import com.linbit.linstor.tasks.AutoDiskfulTask; @@ -81,6 +82,7 @@ import java.util.Map; import java.util.Map.Entry; import java.util.Set; +import java.util.TreeSet; import org.reactivestreams.Publisher; import reactor.core.publisher.Flux; @@ -855,10 +857,16 @@ private Flux prepareDiskAddition( rsc.getNode().getName().displayValue, rscDfn.getName().displayValue ); + + /* + * We also have to remove the currently diskless DrbdRscData and free up the node-id as now we must + * use the shared resource's node-id. We still need to preserve TCP ports though. + */ + copyDrbdTcpPortsIfExists(rsc, payload); } else { - copyDrbdNodeIdIfExists(rsc, payload); + copyDrbdSettings(rsc, payload); } removeLayerData(rsc); @@ -1038,10 +1046,20 @@ private void ensureAllPeersHavePeerSlotLeft(ResourceDefinition rscDfnRef) } /** - * Although we need to rebuild the layerData as the layerList might have changed, if we do not - * deactivate (i.e. down) the current resource, we need to make sure that deleting DrbdRscData - * and recreating a new DrbdRscData ends up with the same node-id as before. + * Copies DRBD settings (node-id and TCP ports) from the existing DrbdRscData into the payload + * before removeLayerData() deletes them. This ensures that recreated DrbdRscData ends up with + * the same node-id and TCP ports as before. + * + * TCP ports must be preserved because if the satellite misses the update (e.g. due to controller + * restart or connectivity issues), it will keep the old ports while peers receive the new ones, + * causing DRBD connections to fail with StandAlone state. */ + private void copyDrbdSettings(Resource rsc, LayerPayload payload) throws ImplementationError + { + copyDrbdNodeIdIfExists(rsc, payload); + copyDrbdTcpPortsIfExists(rsc, payload); + } + private void copyDrbdNodeIdIfExists(Resource rsc, LayerPayload payload) throws ImplementationError { Set> drbdRscDataSet = LayerRscUtils.getRscDataByLayer( @@ -1060,6 +1078,28 @@ private void copyDrbdNodeIdIfExists(Resource rsc, LayerPayload payload) throws I } } + private void copyDrbdTcpPortsIfExists(Resource rsc, LayerPayload payload) throws ImplementationError + { + Set> drbdRscDataSet = LayerRscUtils.getRscDataByLayer( + getLayerData(apiCtx, rsc), + DeviceLayerKind.DRBD + ); + if (!drbdRscDataSet.isEmpty()) + { + DrbdRscData drbdRscData = (DrbdRscData) drbdRscDataSet.iterator().next(); + Collection tcpPorts = drbdRscData.getTcpPortList(); + if (tcpPorts != null && !tcpPorts.isEmpty()) + { + Set portInts = new TreeSet<>(); + for (TcpPortNumber port : tcpPorts) + { + portInts.add(port.value); + } + payload.drbdRsc.tcpPorts = portInts; + } + } + } + private List removeLayerData(Resource rscRef) { List layerList; @@ -1314,15 +1354,17 @@ private Flux finishOperationInTransaction( /* * We also have to remove the possible meta-children of previous StorageRscData. * LayerData will be recreated with ensureStackDataExists. - * However, we still need to remember our node-id if we had / have DRBD in the list + * However, we still need to remember our DRBD settings if we had / have DRBD in the list */ - copyDrbdNodeIdIfExists(rsc, payload); + copyDrbdSettings(rsc, payload); layerList = removeLayerData(rsc); } else { markDiskAdded(rsc); - ctrlLayerStackHelper.resetStoragePools(rsc); + // Pass false to skip redundant ensureStackDataExists call within resetStoragePools, + // since we call it explicitly below with the correct payload + ctrlLayerStackHelper.resetStoragePools(rsc, false); } ctrlLayerStackHelper.ensureStackDataExists(rsc, layerList, payload); diff --git a/controller/src/main/java/com/linbit/linstor/layer/resource/CtrlRscLayerDataFactory.java b/controller/src/main/java/com/linbit/linstor/layer/resource/CtrlRscLayerDataFactory.java index 5abafef18..fc1ae658a 100644 --- a/controller/src/main/java/com/linbit/linstor/layer/resource/CtrlRscLayerDataFactory.java +++ b/controller/src/main/java/com/linbit/linstor/layer/resource/CtrlRscLayerDataFactory.java @@ -264,6 +264,11 @@ rscRef, payload, layerList, new ChildResourceData(""), null } public void resetStoragePools(Resource rscRef) + { + resetStoragePools(rscRef, true); + } + + public void resetStoragePools(Resource rscRef, boolean callEnsureStackDataExistsRef) { try { @@ -278,7 +283,10 @@ public void resetStoragePools(Resource rscRef) rscDataToProcess.addAll(rscData.getChildren()); } - ensureStackDataExists(rscRef, null, new LayerPayload()); + if (callEnsureStackDataExistsRef) + { + ensureStackDataExists(rscRef, null, new LayerPayload()); + } } catch (AccessDeniedException exc) {