2020import java .lang .reflect .Modifier ;
2121import java .net .URI ;
2222import java .net .URISyntaxException ;
23+ import java .util .ArrayList ;
2324import java .util .Arrays ;
2425import java .util .HashMap ;
2526import java .util .List ;
4243import com .cloud .hypervisor .kvm .resource .KVMHABase .PoolType ;
4344import com .cloud .hypervisor .kvm .resource .KVMHAMonitor ;
4445import com .cloud .storage .Storage ;
46+ import com .cloud .storage .StorageManager ;
4547import com .cloud .storage .Storage .StoragePoolType ;
4648import com .cloud .storage .StorageLayer ;
4749import com .cloud .storage .Volume ;
50+ import com .cloud .utils .StringUtils ;
4851import com .cloud .utils .Pair ;
4952import com .cloud .utils .Ternary ;
5053import com .cloud .utils .exception .CloudRuntimeException ;
@@ -164,13 +167,30 @@ public boolean connectPhysicalDisk(StoragePoolType type, String poolUuid, String
164167 return adaptor .connectPhysicalDisk (volPath , pool , details , false );
165168 }
166169
170+ private static class ConnectingDiskInfo {
171+ ConnectingDiskInfo (VolumeObjectTO volume , StorageAdaptor adaptor , KVMStoragePool pool , Map <String , String > details ) {
172+ this .volume = volume ;
173+ this .adapter = adaptor ;
174+ this .pool = pool ;
175+ this .details = details ;
176+ }
177+ VolumeObjectTO volume ;
178+ KVMStoragePool pool = null ;
179+ StorageAdaptor adapter = null ;
180+ Map <String ,String > details = null ;
181+ }
182+
167183 public boolean connectPhysicalDisksViaVmSpec (VirtualMachineTO vmSpec , boolean isVMMigrate ) {
168184 boolean result = false ;
169185
170186 final String vmName = vmSpec .getName ();
171187
172188 List <DiskTO > disks = Arrays .asList (vmSpec .getDisks ());
173189
190+
191+ // disks that connect in background
192+ List <ConnectingDiskInfo > connectingDisks = new ArrayList <>();
193+
174194 for (DiskTO disk : disks ) {
175195 if (disk .getType () == Volume .Type .ISO ) {
176196 result = true ;
@@ -187,17 +207,79 @@ public boolean connectPhysicalDisksViaVmSpec(VirtualMachineTO vmSpec, boolean is
187207 KVMStoragePool pool = getStoragePool (store .getPoolType (), store .getUuid ());
188208 StorageAdaptor adaptor = getStorageAdaptor (pool .getType ());
189209
190- result = adaptor .connectPhysicalDisk (vol .getPath (), pool , disk .getDetails (), isVMMigrate );
210+ if (adaptor instanceof AsyncPhysicalDiskConnectorDecorator ) {
211+ // If the adaptor supports async disk connection, we can start the connection
212+ // and return immediately, allowing the connection to complete in the background.
213+ result = ((AsyncPhysicalDiskConnectorDecorator ) adaptor ).startConnectPhysicalDisk (vol .getPath (), pool , disk .getDetails ());
214+ if (!result ) {
215+ logger .error ("Failed to start connecting disks via vm spec for vm: " + vmName + " volume:" + vol .toString ());
216+ return false ;
217+ }
191218
192- if (!result ) {
193- logger .error ("Failed to connect disks via vm spec for vm: " + vmName + " volume:" + vol .toString ());
194- return result ;
219+ // add disk to list of disks to check later
220+ connectingDisks .add (new ConnectingDiskInfo (vol , adaptor , pool , disk .getDetails ()));
221+ } else {
222+ result = adaptor .connectPhysicalDisk (vol .getPath (), pool , disk .getDetails (), isVMMigrate );
223+
224+ if (!result ) {
225+ logger .error ("Failed to connect disks via vm spec for vm: " + vmName + " volume:" + vol .toString ());
226+ return result ;
227+ }
228+ }
229+ }
230+
231+ // if we have any connecting disks to check, wait for them to connect or timeout
232+ if (!connectingDisks .isEmpty ()) {
233+ for (ConnectingDiskInfo connectingDisk : connectingDisks ) {
234+ StorageAdaptor adaptor = connectingDisk .adapter ;
235+ KVMStoragePool pool = connectingDisk .pool ;
236+ VolumeObjectTO volume = connectingDisk .volume ;
237+ Map <String , String > details = connectingDisk .details ;
238+ long diskWaitTimeMillis = getDiskWaitTimeMillis (details );
239+
240+ // wait for the disk to connect
241+ long startTime = System .currentTimeMillis ();
242+ while (System .currentTimeMillis () - startTime < diskWaitTimeMillis ) {
243+ if (((AsyncPhysicalDiskConnectorDecorator ) adaptor ).isConnected (volume .getPath (), pool , details )) {
244+ logger .debug (String .format ("Disk %s connected successfully for VM %s" , volume .getPath (), vmName ));
245+ break ;
246+ }
247+
248+ sleep (1000 ); // wait for 1 second before checking again
249+ }
195250 }
196251 }
197252
198253 return result ;
199254 }
200255
256+ private long getDiskWaitTimeMillis (Map <String ,String > details ) {
257+ int waitTimeInSec = 60 ; // default wait time in seconds
258+ if (details != null && details .containsKey (StorageManager .STORAGE_POOL_DISK_WAIT .toString ())) {
259+ String waitTime = details .get (StorageManager .STORAGE_POOL_DISK_WAIT .toString ());
260+ if (StringUtils .isNotEmpty (waitTime )) {
261+ waitTimeInSec = Integer .valueOf (waitTime ).intValue ();
262+ logger .debug (String .format ("%s set to %s" , waitTimeInSec , StorageManager .STORAGE_POOL_DISK_WAIT .toString ()));
263+ }
264+ } else {
265+ // wait at least 60 seconds even if input was lower
266+ if (waitTimeInSec < 60 ) {
267+ logger .debug (String .format ("%s was less than 60s. Increasing to 60s default." , StorageManager .STORAGE_POOL_DISK_WAIT .toString ()));
268+ waitTimeInSec = 60 ;
269+ }
270+ }
271+ return waitTimeInSec * 1000 ; // convert to milliseconds
272+ }
273+
274+ private boolean sleep (long millis ) {
275+ try {
276+ Thread .sleep (millis );
277+ return true ;
278+ } catch (InterruptedException e ) {
279+ return false ;
280+ }
281+ }
282+
201283 public boolean disconnectPhysicalDisk (Map <String , String > volumeToDisconnect ) {
202284 logger .debug (String .format ("Disconnect physical disks using volume map: %s" , volumeToDisconnect .toString ()));
203285 if (MapUtils .isEmpty (volumeToDisconnect )) {
0 commit comments