3535import org .libvirt .LibvirtException ;
3636
3737import com .linbit .linstor .api .ApiClient ;
38+ import com .linbit .linstor .api .ApiConsts ;
3839import com .linbit .linstor .api .ApiException ;
3940import com .linbit .linstor .api .Configuration ;
4041import com .linbit .linstor .api .DevelopersApi ;
4142import com .linbit .linstor .api .model .ApiCallRc ;
4243import com .linbit .linstor .api .model .ApiCallRcList ;
4344import com .linbit .linstor .api .model .Properties ;
4445import com .linbit .linstor .api .model .ProviderKind ;
46+ import com .linbit .linstor .api .model .Resource ;
4547import com .linbit .linstor .api .model .ResourceDefinition ;
4648import com .linbit .linstor .api .model .ResourceDefinitionModify ;
4749import com .linbit .linstor .api .model .ResourceGroupSpawn ;
@@ -80,6 +82,10 @@ private void logLinstorAnswer(@Nonnull ApiCallRc answer) {
8082 }
8183 }
8284
85+ private void logLinstorAnswers (@ Nonnull ApiCallRcList answers ) {
86+ answers .forEach (this ::logLinstorAnswer );
87+ }
88+
8389 private void checkLinstorAnswersThrow (@ Nonnull ApiCallRcList answers ) {
8490 answers .forEach (this ::logLinstorAnswer );
8591 if (answers .hasError ())
@@ -239,6 +245,28 @@ public KVMPhysicalDisk createPhysicalDisk(String name, KVMStoragePool pool, Qemu
239245 }
240246 }
241247
248+ /**
249+ * Checks if the given resource is in use by drbd on any host and
250+ * if so set the drbd option allow-two-primaries
251+ * @param api linstor api object
252+ * @param rscName resource name to set allow-two-primaries if in use
253+ * @throws ApiException if any problem connecting to the Linstor controller
254+ */
255+ private void allow2PrimariesIfInUse (DevelopersApi api , String rscName ) throws ApiException {
256+ if (LinstorUtil .isResourceInUse (api , rscName )) {
257+ // allow 2 primaries for live migration, should be removed by disconnect on the other end
258+ ResourceDefinitionModify rdm = new ResourceDefinitionModify ();
259+ Properties props = new Properties ();
260+ props .put ("DrbdOptions/Net/allow-two-primaries" , "yes" );
261+ rdm .setOverrideProps (props );
262+ ApiCallRcList answers = api .resourceDefinitionModify (rscName , rdm );
263+ if (answers .hasError ()) {
264+ logger .error ("Unable to set 'allow-two-primaries' on {} " , rscName );
265+ // do not fail here as adding allow-two-primaries property is only a problem while live migrating
266+ }
267+ }
268+ }
269+
242270 @ Override
243271 public boolean connectPhysicalDisk (String volumePath , KVMStoragePool pool , Map <String , String > details )
244272 {
@@ -265,40 +293,98 @@ public boolean connectPhysicalDisk(String volumePath, KVMStoragePool pool, Map<S
265293
266294 try
267295 {
268- // allow 2 primaries for live migration, should be removed by disconnect on the other end
269- ResourceDefinitionModify rdm = new ResourceDefinitionModify ();
270- Properties props = new Properties ();
271- props .put ("DrbdOptions/Net/allow-two-primaries" , "yes" );
272- rdm .setOverrideProps (props );
273- ApiCallRcList answers = api .resourceDefinitionModify (rscName , rdm );
274- if (answers .hasError ()) {
275- logger .error ("Unable to set 'allow-two-primaries' on " + rscName );
276- // do not fail here as adding allow-two-primaries property is only a problem while live migrating
277- }
296+ allow2PrimariesIfInUse (api , rscName );
278297 } catch (ApiException apiEx ) {
279298 logger .error (apiEx );
280299 // do not fail here as adding allow-two-primaries property is only a problem while live migrating
281300 }
282301 return true ;
283302 }
284303
304+ private boolean tryDisconnectLinstor (String volumePath , KVMStoragePool pool )
305+ {
306+ if (volumePath == null ) {
307+ return false ;
308+ }
309+
310+ logger .debug ("Linstor: Using storage pool: " + pool .getUuid ());
311+ final DevelopersApi api = getLinstorAPI (pool );
312+
313+ Optional <ResourceWithVolumes > optRsc ;
314+ try
315+ {
316+ List <ResourceWithVolumes > resources = api .viewResources (
317+ Collections .singletonList (localNodeName ),
318+ null ,
319+ null ,
320+ null ,
321+ null ,
322+ null );
323+
324+ optRsc = getResourceByPath (resources , volumePath );
325+ } catch (ApiException apiEx ) {
326+ // couldn't query linstor controller
327+ logger .error (apiEx .getBestMessage ());
328+ return false ;
329+ }
330+
331+
332+ if (optRsc .isPresent ()) {
333+ try {
334+ Resource rsc = optRsc .get ();
335+
336+ // if diskless resource remove it, in the worst case it will be transformed to a tiebreaker
337+ if (rsc .getFlags () != null &&
338+ rsc .getFlags ().contains (ApiConsts .FLAG_DRBD_DISKLESS ) &&
339+ !rsc .getFlags ().contains (ApiConsts .FLAG_TIE_BREAKER )) {
340+ ApiCallRcList delAnswers = api .resourceDelete (rsc .getName (), localNodeName );
341+ logLinstorAnswers (delAnswers );
342+ }
343+
344+ // remove allow-two-primaries
345+ ResourceDefinitionModify rdm = new ResourceDefinitionModify ();
346+ rdm .deleteProps (Collections .singletonList ("DrbdOptions/Net/allow-two-primaries" ));
347+ ApiCallRcList answers = api .resourceDefinitionModify (rsc .getName (), rdm );
348+ if (answers .hasError ()) {
349+ logger .error (
350+ String .format ("Failed to remove 'allow-two-primaries' on %s: %s" ,
351+ rsc .getName (), LinstorUtil .getBestErrorMessage (answers )));
352+ // do not fail here as removing allow-two-primaries property isn't fatal
353+ }
354+ } catch (ApiException apiEx ) {
355+ logger .error (apiEx .getBestMessage ());
356+ // do not fail here as removing allow-two-primaries property or deleting diskless isn't fatal
357+ }
358+
359+ return true ;
360+ }
361+
362+ logger .warn ("Linstor: Couldn't find resource for this path: " + volumePath );
363+ return false ;
364+ }
365+
285366 @ Override
286367 public boolean disconnectPhysicalDisk (String volumePath , KVMStoragePool pool )
287368 {
288- logger .debug ("Linstor: disconnectPhysicalDisk " + pool .getUuid () + ":" + volumePath );
289- return true ;
369+ logger .debug ("Linstor: disconnectPhysicalDisk {}:{}" , pool .getUuid (), volumePath );
370+ if (MapStorageUuidToStoragePool .containsValue (pool )) {
371+ return tryDisconnectLinstor (volumePath , pool );
372+ }
373+ return false ;
290374 }
291375
292376 @ Override
293377 public boolean disconnectPhysicalDisk (Map <String , String > volumeToDisconnect )
294378 {
379+ // as of now this is only relevant for iscsi targets
380+ logger .info ("Linstor: disconnectPhysicalDisk(Map<String, String> volumeToDisconnect) called?" );
295381 return false ;
296382 }
297383
298384 private Optional <ResourceWithVolumes > getResourceByPath (final List <ResourceWithVolumes > resources , String path ) {
299385 return resources .stream ()
300386 .filter (rsc -> rsc .getVolumes ().stream ()
301- .anyMatch (v -> v .getDevicePath (). equals ( path )))
387+ .anyMatch (v -> path . equals ( v .getDevicePath ())))
302388 .findFirst ();
303389 }
304390
@@ -319,42 +405,9 @@ public boolean disconnectPhysicalDiskByPath(String localPath)
319405 logger .debug ("Linstor: disconnectPhysicalDiskByPath " + localPath );
320406 final KVMStoragePool pool = optFirstPool .get ();
321407
322- logger .debug ("Linstor: Using storpool: " + pool .getUuid ());
323- final DevelopersApi api = getLinstorAPI (pool );
324-
325- try
326- {
327- List <ResourceWithVolumes > resources = api .viewResources (
328- Collections .singletonList (localNodeName ),
329- null ,
330- null ,
331- null ,
332- null ,
333- null );
334-
335- Optional <ResourceWithVolumes > rsc = getResourceByPath (resources , localPath );
336-
337- if (rsc .isPresent ())
338- {
339- ResourceDefinitionModify rdm = new ResourceDefinitionModify ();
340- rdm .deleteProps (Collections .singletonList ("DrbdOptions/Net/allow-two-primaries" ));
341- ApiCallRcList answers = api .resourceDefinitionModify (rsc .get ().getName (), rdm );
342- if (answers .hasError ()) {
343- logger .error (
344- String .format ("Failed to remove 'allow-two-primaries' on %s: %s" ,
345- rsc .get ().getName (), LinstorUtil .getBestErrorMessage (answers )));
346- // do not fail here as removing allow-two-primaries property isn't fatal
347- }
348-
349- return true ;
350- }
351- logger .warn ("Linstor: Couldn't find resource for this path: " + localPath );
352- } catch (ApiException apiEx ) {
353- logger .error (apiEx .getBestMessage ());
354- // do not fail here as removing allow-two-primaries property isn't fatal
355- }
408+ return tryDisconnectLinstor (localPath , pool );
356409 }
357- return true ;
410+ return false ;
358411 }
359412
360413 @ Override
@@ -520,4 +573,4 @@ public long getUsed(LinstorStoragePool pool) {
520573 throw new CloudRuntimeException (apiEx .getBestMessage (), apiEx );
521574 }
522575 }
523- }
576+ }
0 commit comments