214214import com .cloud .utils .component .ComponentContext ;
215215import com .cloud .utils .component .ManagerBase ;
216216import com .cloud .utils .db .Filter ;
217+ import com .cloud .utils .db .Transaction ;
218+ import com .cloud .utils .db .TransactionCallbackWithException ;
217219import com .cloud .utils .exception .CloudRuntimeException ;
218220import com .cloud .vm .NicVO ;
219221import com .cloud .vm .UserVmManager ;
@@ -377,7 +379,7 @@ protected void waitForJobCompletion(long jobId) {
377379 final long deadline = System .nanoTime () + timeoutNanos ;
378380 long sleepMillis = 500 ;
379381 while (true ) {
380- AsyncJobVO job = asyncJobDao .findById (jobId );
382+ AsyncJobVO job = asyncJobDao .findByIdIncludingRemoved (jobId );
381383 if (job == null ) {
382384 logger .warn ("Async job with ID {} not found" , jobId );
383385 return ;
@@ -568,8 +570,8 @@ protected ServiceOffering getServiceOfferingIdForVmCreation(com.cloud.dc.DataCen
568570 return offering ;
569571 }
570572
571- protected GuestOS getGuestOsForInstance (Vm request , boolean isWorkerVm ) {
572- if (isWorkerVm ) {
573+ protected GuestOS getGuestOsForInstance (Vm request ) {
574+ if (request . isWorkerVm () ) {
573575 GuestOS os = guestOSDao .findOneByDisplayName (WORKER_VM_GUEST_OS );
574576 if (os == null ) {
575577 logger .warn ("Guest OS with name {} for worker VM not found, VM will be created with default guest OS" ,
@@ -719,11 +721,48 @@ protected String getValidatedInstanceType(Vm request) {
719721 return instanceType ;
720722 }
721723
724+ protected UserVm createVmWithForRestoreIfNeeded (DeployVMCmdByAdmin cmd , Vm request ) {
725+ return Transaction .execute ((TransactionCallbackWithException <UserVm , CloudRuntimeException >) status -> {
726+ final String uuid = request .getInstanceId ();
727+ if (StringUtils .isNotBlank (uuid )) {
728+ logger .debug ("UUID for new VM needs to be {}" , uuid );
729+ updateOldInstanceUuid (uuid );
730+ cmd .setCustomId (uuid );
731+ }
732+ try {
733+ return userVmManager .createVirtualMachine (cmd );
734+ } catch (InsufficientCapacityException | ResourceUnavailableException |
735+ ResourceAllocationException | CloudRuntimeException e ) {
736+ throw new CloudRuntimeException ("Failed to create VM: " + e .getMessage (), e );
737+ }
738+ });
739+ }
740+
741+ private void updateOldInstanceUuid (String uuid ) {
742+ UserVmVO oldVM = userVmDao .findByUuidIncludingRemoved (uuid );
743+ if (oldVM == null ) {
744+ return ;
745+ }
746+ if (oldVM .getRemoved () == null ) {
747+ logger .error ("Found existing VM with the UUID from request data and it is not removed. Cannot " +
748+ "proceed with UUID update. VM with conflicting UUID: {}" , oldVM );
749+ throw new CloudRuntimeException ("VM with ID " + uuid + " already exists and is not removed. Cannot proceed with VM creation for restore." );
750+ }
751+ logger .debug ("Found removed VM with UUID from request data. Updating its UUID to a new random " +
752+ "value to free up the requested UUID for the new VM. Old VM: {}" , uuid , oldVM );
753+ UserVmVO updateObj = userVmDao .createForUpdate (oldVM .getId ());
754+ updateObj .setUuid (UUID .randomUUID ().toString ());
755+ userVmDao .update (updateObj .getId (), updateObj );
756+ }
757+
722758 protected Pair <Vm , UserVm > createInstance (com .cloud .dc .DataCenter zone , Long clusterId , Account owner , Long domainId ,
723759 String accountName , Long projectId , String name , String displayName , String serviceOfferingUuid ,
724760 int cpu , int memory , String templateUuid , GuestOS guestOs , String userdata , ApiConstants .BootType bootType ,
725- ApiConstants .BootMode bootMode , String affinityGroupId , String userDataId , String sshKeyPairNames ,
726- String instanceType , Map <String , String > details ) {
761+ ApiConstants .BootMode bootMode , Vm request ) {
762+ final String affinityGroupId = request .getAffinityGroupId ();
763+ final String userDataId = request .getUserDataId ();
764+ final String sshKeyPairNames = request .getSshKeyPairNames ();
765+ final Map <String , String > details = request .getDetails ();
727766 Account account = owner != null ? owner : CallContext .current ().getCallingAccount ();
728767 ServiceOffering serviceOffering = getServiceOfferingIdForVmCreation (zone , account , serviceOfferingUuid , cpu ,
729768 memory );
@@ -773,30 +812,27 @@ protected Pair<Vm, UserVm> createInstance(com.cloud.dc.DataCenter zone, Long clu
773812 if (StringUtils .isNotBlank (sshKeyPairNames )) {
774813 cmd .setSshKeyPairNames (getValidatedSshKeyPairNames (sshKeyPairNames , owner ));
775814 }
815+ final String instanceType = getValidatedInstanceType (request );
776816 cmd .setInstanceType (StringUtils .trimToNull (instanceType ));
777817 cmd .setHypervisor (Hypervisor .HypervisorType .KVM .name ());
778- Map <String , String > instanceDetails = getDetailsForInstanceCreation (userdata , serviceOffering , details );
818+ Map <String , String > instanceDetails = getDetailsForInstanceCreation (request , serviceOffering , details );
779819 if (MapUtils .isNotEmpty (instanceDetails )) {
780820 Map <Integer , Map <String , String >> map = new HashMap <>();
781821 map .put (0 , instanceDetails );
782822 cmd .setDetails (map );
783823 }
784824 cmd .setBlankInstance (true );
785- try {
786- UserVm vm = userVmManager .createVirtualMachine (cmd );
787- vm = userVmManager .finalizeCreateVirtualMachine (vm .getId ());
788- UserVmJoinVO vo = userVmJoinDao .findById (vm .getId ());
789- Vm vmObj = UserVmJoinVOToVmConverter .toVm (vo , this ::getHostById , this ::getDetailsByInstanceId ,
790- this ::listTagsByInstanceId , this ::listDiskAttachmentsByInstanceId , this ::listNicsByInstance ,
791- null , null , false );
792- return new Pair <>(vmObj , vm );
793- } catch (InsufficientCapacityException | ResourceUnavailableException | ResourceAllocationException | CloudRuntimeException e ) {
794- throw new CloudRuntimeException ("Failed to create VM: " + e .getMessage (), e );
795- }
825+ UserVm vm = createVmWithForRestoreIfNeeded (cmd , request );
826+ vm = userVmManager .finalizeCreateVirtualMachine (vm .getId ());
827+ UserVmJoinVO vo = userVmJoinDao .findById (vm .getId ());
828+ Vm vmObj = UserVmJoinVOToVmConverter .toVm (vo , this ::getHostById , this ::getDetailsByInstanceId ,
829+ this ::listTagsByInstanceId , this ::listDiskAttachmentsByInstanceId , this ::listNicsByInstance ,
830+ null , null , false );
831+ return new Pair <>(vmObj , vm );
796832 }
797833
798834 @ NotNull
799- protected static Map <String , String > getDetailsForInstanceCreation (String userdata , ServiceOffering serviceOffering ,
835+ protected static Map <String , String > getDetailsForInstanceCreation (Vm request , ServiceOffering serviceOffering ,
800836 Map <String , String > existingDetails ) {
801837 Map <String , String > details = new HashMap <>();
802838 List <String > detailsTobeSkipped = List .of (
@@ -810,7 +846,7 @@ protected static Map<String, String> getDetailsForInstanceCreation(String userda
810846 details .put (entry .getKey (), entry .getValue ());
811847 }
812848 }
813- if (StringUtils . isNotEmpty ( userdata )) {
849+ if (request . isWorkerVm ( )) {
814850 // Assumption: Only worker VM will have userdata and it needs CPU mode
815851 details .put (VmDetailConstants .GUEST_CPU_MODE , WORKER_VM_GUEST_CPU_MODE );
816852 }
@@ -1277,8 +1313,9 @@ public Vm getInstance(String uuid, boolean includeTags, boolean includeDisks, bo
12771313 @ ApiAccess (command = DeployVMCmd .class )
12781314 public Vm createInstance (Vm request ) {
12791315 if (request == null ) {
1280- throw new InvalidParameterValueException ("Request disk data is empty" );
1316+ throw new InvalidParameterValueException ("Request VM data is empty" );
12811317 }
1318+ logger .debug ("VM create request [worker-vm: {}]" , request .isWorkerVm ());
12821319 OvfXmlUtil .updateFromConfiguration (request );
12831320 String name = request .getName ();
12841321 if (StringUtils .isBlank (name )) {
@@ -1334,13 +1371,11 @@ public Vm createInstance(Vm request) {
13341371 if (request .getTemplate () != null && StringUtils .isNotEmpty (request .getTemplate ().getId ())) {
13351372 templateUuid = request .getTemplate ().getId ();
13361373 }
1337- GuestOS guestOs = getGuestOsForInstance (request , request .isWorkerVm ());
1338- String instanceType = getValidatedInstanceType (request );
1374+ GuestOS guestOs = getGuestOsForInstance (request );
13391375 Pair <Vm , UserVm > result = createInstance (zone , clusterId , owner , ownerDetails .first (), ownerDetails .second (),
13401376 ownerDetails .third (), name , displayName , serviceOfferingUuid , cpu , memoryMB , templateUuid , guestOs ,
1341- userdata , bootOptions .first (), bootOptions .second (), request .getAffinityGroupId (),
1342- request .getUserDataId (), request .getSshKeyPairNames (), instanceType , request .getDetails ());
1343- saveInstanceAdditionalDetails (request , result .second ());
1377+ userdata , bootOptions .first (), bootOptions .second (), request );
1378+ saveInstanceRestoreConfig (request , result .second ());
13441379 return result .first ();
13451380 }
13461381
0 commit comments