|
57 | 57 | import com.cloud.exception.ConcurrentOperationException; |
58 | 58 | import com.cloud.exception.InsufficientCapacityException; |
59 | 59 | import com.cloud.exception.InvalidParameterValueException; |
| 60 | +import com.cloud.exception.ManagementServerException; |
60 | 61 | import com.cloud.exception.ResourceAllocationException; |
61 | 62 | import com.cloud.exception.ResourceUnavailableException; |
| 63 | +import com.cloud.exception.VirtualMachineMigrationException; |
62 | 64 | import com.cloud.gpu.GPU; |
63 | 65 | import com.cloud.hypervisor.Hypervisor.HypervisorType; |
64 | 66 | import com.cloud.hypervisor.dao.HypervisorCapabilitiesDao; |
65 | 67 | import com.cloud.projects.Project.ListProjectResourcesCriteria; |
| 68 | +import com.cloud.service.ServiceOfferingVO; |
| 69 | +import com.cloud.service.dao.ServiceOfferingDao; |
66 | 70 | import com.cloud.service.dao.ServiceOfferingDetailsDao; |
67 | 71 | import com.cloud.storage.GuestOSVO; |
68 | 72 | import com.cloud.storage.Snapshot; |
|
89 | 93 | import com.cloud.utils.db.Filter; |
90 | 94 | import com.cloud.utils.db.SearchBuilder; |
91 | 95 | import com.cloud.utils.db.SearchCriteria; |
| 96 | +import com.cloud.utils.db.Transaction; |
| 97 | +import com.cloud.utils.db.TransactionCallbackWithException; |
| 98 | +import com.cloud.utils.db.TransactionCallbackWithExceptionNoReturn; |
| 99 | +import com.cloud.utils.db.TransactionStatus; |
92 | 100 | import com.cloud.utils.exception.CloudRuntimeException; |
| 101 | +import com.cloud.vm.UserVmDetailVO; |
| 102 | +import com.cloud.vm.UserVmManager; |
93 | 103 | import com.cloud.vm.UserVmVO; |
94 | 104 | import com.cloud.vm.VMInstanceVO; |
95 | 105 | import com.cloud.vm.VirtualMachine; |
|
102 | 112 | import com.cloud.vm.VmWorkJobHandlerProxy; |
103 | 113 | import com.cloud.vm.VmWorkSerializer; |
104 | 114 | import com.cloud.vm.dao.UserVmDao; |
| 115 | +import com.cloud.vm.dao.UserVmDetailsDao; |
105 | 116 | import com.cloud.vm.dao.VMInstanceDao; |
106 | 117 | import com.cloud.vm.snapshot.dao.VMSnapshotDao; |
| 118 | +import com.cloud.vm.snapshot.dao.VMSnapshotDetailsDao; |
107 | 119 |
|
108 | 120 | @Component |
109 | 121 | public class VMSnapshotManagerImpl extends MutualExclusiveIdsManagerBase implements VMSnapshotManager, VMSnapshotService, VmWorkJobHandler { |
@@ -135,6 +147,14 @@ public class VMSnapshotManagerImpl extends MutualExclusiveIdsManagerBase impleme |
135 | 147 |
|
136 | 148 | @Inject |
137 | 149 | VmWorkJobDao _workJobDao; |
| 150 | + @Inject |
| 151 | + protected UserVmManager _userVmManager; |
| 152 | + @Inject |
| 153 | + protected ServiceOfferingDao _serviceOfferingDao; |
| 154 | + @Inject |
| 155 | + protected UserVmDetailsDao _userVmDetailsDao; |
| 156 | + @Inject |
| 157 | + protected VMSnapshotDetailsDao _vmSnapshotDetailsDao; |
138 | 158 |
|
139 | 159 | VmWorkJobHandlerProxy _jobHandlerProxy = new VmWorkJobHandlerProxy(this); |
140 | 160 |
|
@@ -346,21 +366,65 @@ public VMSnapshot allocVMSnapshot(Long vmId, String vsDisplayName, String vsDesc |
346 | 366 | vmSnapshotType = VMSnapshot.Type.DiskAndMemory; |
347 | 367 |
|
348 | 368 | try { |
349 | | - VMSnapshotVO vmSnapshotVo = |
350 | | - new VMSnapshotVO(userVmVo.getAccountId(), userVmVo.getDomainId(), vmId, vsDescription, vmSnapshotName, vsDisplayName, userVmVo.getServiceOfferingId(), |
351 | | - vmSnapshotType, null); |
352 | | - VMSnapshot vmSnapshot = _vmSnapshotDao.persist(vmSnapshotVo); |
353 | | - if (vmSnapshot == null) { |
354 | | - throw new CloudRuntimeException("Failed to create snapshot for vm: " + vmId); |
355 | | - } |
356 | | - return vmSnapshot; |
| 369 | + return createAndPersistVMSnapshot(userVmVo, vsDescription, vmSnapshotName, vsDisplayName, vmSnapshotType); |
357 | 370 | } catch (Exception e) { |
358 | 371 | String msg = e.getMessage(); |
359 | 372 | s_logger.error("Create vm snapshot record failed for vm: " + vmId + " due to: " + msg); |
360 | 373 | } |
361 | 374 | return null; |
362 | 375 | } |
363 | 376 |
|
| 377 | + /** |
| 378 | + * Create, persist and return vm snapshot for userVmVo with given parameters. |
| 379 | + * Persistence and support for custom service offerings are done on the same transaction |
| 380 | + * @param userVmVo user vm |
| 381 | + * @param vmId vm id |
| 382 | + * @param vsDescription vm description |
| 383 | + * @param vmSnapshotName vm snapshot name |
| 384 | + * @param vsDisplayName vm snapshot display name |
| 385 | + * @param vmSnapshotType vm snapshot type |
| 386 | + * @return vm snapshot |
| 387 | + * @throws CloudRuntimeException if vm snapshot couldn't be persisted |
| 388 | + */ |
| 389 | + protected VMSnapshot createAndPersistVMSnapshot(UserVmVO userVmVo, String vsDescription, String vmSnapshotName, String vsDisplayName, VMSnapshot.Type vmSnapshotType) { |
| 390 | + final Long vmId = userVmVo.getId(); |
| 391 | + final Long serviceOfferingId = userVmVo.getServiceOfferingId(); |
| 392 | + final VMSnapshotVO vmSnapshotVo = |
| 393 | + new VMSnapshotVO(userVmVo.getAccountId(), userVmVo.getDomainId(), vmId, vsDescription, vmSnapshotName, vsDisplayName, serviceOfferingId, |
| 394 | + vmSnapshotType, null); |
| 395 | + return Transaction.execute(new TransactionCallbackWithException<VMSnapshot, CloudRuntimeException>() { |
| 396 | + @Override |
| 397 | + public VMSnapshot doInTransaction(TransactionStatus status) { |
| 398 | + VMSnapshot vmSnapshot = _vmSnapshotDao.persist(vmSnapshotVo); |
| 399 | + if (vmSnapshot == null) { |
| 400 | + throw new CloudRuntimeException("Failed to create snapshot for vm: " + vmId); |
| 401 | + } |
| 402 | + addSupportForCustomServiceOffering(vmId, serviceOfferingId, vmSnapshot.getId()); |
| 403 | + return vmSnapshot; |
| 404 | + } |
| 405 | + }); |
| 406 | + } |
| 407 | + |
| 408 | + /** |
| 409 | + * Add entries on vm_snapshot_details if service offering is dynamic. This will allow setting details when revert to vm snapshot |
| 410 | + * @param vmId vm id |
| 411 | + * @param serviceOfferingId service offering id |
| 412 | + * @param vmSnapshotId vm snapshot id |
| 413 | + */ |
| 414 | + protected void addSupportForCustomServiceOffering(long vmId, long serviceOfferingId, long vmSnapshotId) { |
| 415 | + ServiceOfferingVO serviceOfferingVO = _serviceOfferingDao.findById(serviceOfferingId); |
| 416 | + if (serviceOfferingVO.isDynamic()) { |
| 417 | + List<UserVmDetailVO> vmDetails = _userVmDetailsDao.listDetails(vmId); |
| 418 | + List<VMSnapshotDetailsVO> vmSnapshotDetails = new ArrayList<VMSnapshotDetailsVO>(); |
| 419 | + for (UserVmDetailVO detail : vmDetails) { |
| 420 | + if(detail.getName().equalsIgnoreCase("cpuNumber") || detail.getName().equalsIgnoreCase("cpuSpeed") || detail.getName().equalsIgnoreCase("memory")) { |
| 421 | + vmSnapshotDetails.add(new VMSnapshotDetailsVO(vmSnapshotId, detail.getName(), detail.getValue(), detail.isDisplay())); |
| 422 | + } |
| 423 | + } |
| 424 | + _vmSnapshotDetailsDao.saveDetails(vmSnapshotDetails); |
| 425 | + } |
| 426 | + } |
| 427 | + |
364 | 428 | @Override |
365 | 429 | public String getName() { |
366 | 430 | return _name; |
@@ -654,16 +718,76 @@ else if (jobResult instanceof Throwable) |
654 | 718 | } |
655 | 719 | } |
656 | 720 |
|
| 721 | + /** |
| 722 | + * If snapshot was taken with a different service offering than actual used in vm, should change it back to it |
| 723 | + * @param userVm vm to change service offering (if necessary) |
| 724 | + * @param vmSnapshotVo vm snapshot |
| 725 | + */ |
| 726 | + protected void updateUserVmServiceOffering(UserVm userVm, VMSnapshotVO vmSnapshotVo) { |
| 727 | + if (vmSnapshotVo.getServiceOfferingId() != userVm.getServiceOfferingId()) { |
| 728 | + changeUserVmServiceOffering(userVm, vmSnapshotVo); |
| 729 | + } |
| 730 | + } |
| 731 | + |
| 732 | + /** |
| 733 | + * Get user vm details as a map |
| 734 | + * @param userVm user vm |
| 735 | + * @return map |
| 736 | + */ |
| 737 | + protected Map<String, String> getVmMapDetails(UserVm userVm) { |
| 738 | + List<UserVmDetailVO> userVmDetails = _userVmDetailsDao.listDetails(userVm.getId()); |
| 739 | + Map<String, String> details = new HashMap<String, String>(); |
| 740 | + for (UserVmDetailVO detail : userVmDetails) { |
| 741 | + details.put(detail.getName(), detail.getValue()); |
| 742 | + } |
| 743 | + return details; |
| 744 | + } |
| 745 | + |
| 746 | + /** |
| 747 | + * Update service offering on {@link userVm} to the one specified in {@link vmSnapshotVo} |
| 748 | + * @param userVm user vm to be updated |
| 749 | + * @param vmSnapshotVo vm snapshot |
| 750 | + */ |
| 751 | + protected void changeUserVmServiceOffering(UserVm userVm, VMSnapshotVO vmSnapshotVo) { |
| 752 | + Map<String, String> vmDetails = getVmMapDetails(userVm); |
| 753 | + boolean result = upgradeUserVmServiceOffering(userVm.getId(), vmSnapshotVo.getServiceOfferingId(), vmDetails); |
| 754 | + if (! result){ |
| 755 | + throw new CloudRuntimeException("VM Snapshot reverting failed due to vm service offering couldn't be changed to the one used when snapshot was taken"); |
| 756 | + } |
| 757 | + s_logger.debug("Successfully changed service offering to " + vmSnapshotVo.getServiceOfferingId() + " for vm " + userVm.getId()); |
| 758 | + } |
| 759 | + |
| 760 | + /** |
| 761 | + * Upgrade virtual machine {@linkplain vmId} to new service offering {@linkplain serviceOfferingId} |
| 762 | + * @param vmId vm id |
| 763 | + * @param serviceOfferingId service offering id |
| 764 | + * @param details vm details |
| 765 | + * @return if operation was successful |
| 766 | + */ |
| 767 | + protected boolean upgradeUserVmServiceOffering(Long vmId, Long serviceOfferingId, Map<String, String> details) { |
| 768 | + boolean result; |
| 769 | + try { |
| 770 | + result = _userVmManager.upgradeVirtualMachine(vmId, serviceOfferingId, details); |
| 771 | + if (! result){ |
| 772 | + s_logger.error("Couldn't change service offering for vm " + vmId + " to " + serviceOfferingId); |
| 773 | + } |
| 774 | + return result; |
| 775 | + } catch (ConcurrentOperationException | ResourceUnavailableException | ManagementServerException | VirtualMachineMigrationException e) { |
| 776 | + s_logger.error("Couldn't change service offering for vm " + vmId + " to " + serviceOfferingId + " due to: " + e.getMessage()); |
| 777 | + return false; |
| 778 | + } |
| 779 | + } |
| 780 | + |
657 | 781 | private UserVm orchestrateRevertToVMSnapshot(Long vmSnapshotId) throws InsufficientCapacityException, ResourceUnavailableException, ConcurrentOperationException { |
658 | 782 |
|
659 | 783 | // check if VM snapshot exists in DB |
660 | | - VMSnapshotVO vmSnapshotVo = _vmSnapshotDao.findById(vmSnapshotId); |
| 784 | + final VMSnapshotVO vmSnapshotVo = _vmSnapshotDao.findById(vmSnapshotId); |
661 | 785 | if (vmSnapshotVo == null) { |
662 | 786 | throw new InvalidParameterValueException( |
663 | 787 | "unable to find the vm snapshot with id " + vmSnapshotId); |
664 | 788 | } |
665 | 789 | Long vmId = vmSnapshotVo.getVmId(); |
666 | | - UserVmVO userVm = _userVMDao.findById(vmId); |
| 790 | + final UserVmVO userVm = _userVMDao.findById(vmId); |
667 | 791 | // check if VM exists |
668 | 792 | if (userVm == null) { |
669 | 793 | throw new InvalidParameterValueException("Revert vm to snapshot: " |
@@ -721,13 +845,37 @@ private UserVm orchestrateRevertToVMSnapshot(Long vmSnapshotId) throws Insuffici |
721 | 845 | try { |
722 | 846 | VMSnapshotStrategy strategy = findVMSnapshotStrategy(vmSnapshotVo); |
723 | 847 | strategy.revertVMSnapshot(vmSnapshotVo); |
| 848 | + Transaction.execute(new TransactionCallbackWithExceptionNoReturn<CloudRuntimeException>() { |
| 849 | + @Override |
| 850 | + public void doInTransactionWithoutResult(TransactionStatus status) throws CloudRuntimeException { |
| 851 | + revertUserVmDetailsFromVmSnapshot(userVm, vmSnapshotVo); |
| 852 | + updateUserVmServiceOffering(userVm, vmSnapshotVo); |
| 853 | + } |
| 854 | + }); |
724 | 855 | return userVm; |
725 | 856 | } catch (Exception e) { |
726 | 857 | s_logger.debug("Failed to revert vmsnapshot: " + vmSnapshotId, e); |
727 | 858 | throw new CloudRuntimeException(e.getMessage()); |
728 | 859 | } |
729 | 860 | } |
730 | 861 |
|
| 862 | + /** |
| 863 | + * Update or add user vm details from vm snapshot for vms with custom service offerings |
| 864 | + * @param userVm user vm |
| 865 | + * @param vmSnapshotVo vm snapshot |
| 866 | + */ |
| 867 | + protected void revertUserVmDetailsFromVmSnapshot(UserVmVO userVm, VMSnapshotVO vmSnapshotVo) { |
| 868 | + ServiceOfferingVO serviceOfferingVO = _serviceOfferingDao.findById(vmSnapshotVo.getServiceOfferingId()); |
| 869 | + if (serviceOfferingVO.isDynamic()) { |
| 870 | + List<VMSnapshotDetailsVO> vmSnapshotDetails = _vmSnapshotDetailsDao.listDetails(vmSnapshotVo.getId()); |
| 871 | + List<UserVmDetailVO> userVmDetails = new ArrayList<UserVmDetailVO>(); |
| 872 | + for (VMSnapshotDetailsVO detail : vmSnapshotDetails) { |
| 873 | + userVmDetails.add(new UserVmDetailVO(userVm.getId(), detail.getName(), detail.getValue(), detail.isDisplay())); |
| 874 | + } |
| 875 | + _userVmDetailsDao.saveDetails(userVmDetails); |
| 876 | + } |
| 877 | + } |
| 878 | + |
731 | 879 | @Override |
732 | 880 | public RestoreVMSnapshotCommand createRestoreCommand(UserVmVO userVm, List<VMSnapshotVO> vmSnapshotVOs) { |
733 | 881 | if (!HypervisorType.KVM.equals(userVm.getHypervisorType())) |
|
0 commit comments