This is an automated email from the ASF dual-hosted git repository.

harikrishna pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/cloudstack.git


The following commit(s) were added to refs/heads/main by this push:
     new 046014b4c55 NAS BnR: Create Instance from Backup issues (#11754)
046014b4c55 is described below

commit 046014b4c55056e96098c6a58da8fd88ef64f131
Author: Abhisar Sinha <[email protected]>
AuthorDate: Tue Oct 14 14:51:57 2025 +0530

    NAS BnR: Create Instance from Backup issues (#11754)
    
    * add createCrossZoneInstnaceEnabled to BackupOfferingResponse
    
    * show use IP Address from Backup button when orignal instance is expunged
    
    * Fix NPE in takeBackup if the  vm template is deleted.
    
    * Add since to Cross zone instance creation in BackupOfferingResponse.java
    
    Co-authored-by: Suresh Kumar Anaparti <[email protected]>
    
    * Store and show Guest os type in the backup metadata
    
    * show warning in create instance from backup form if guest os type is 
different
    
    * show warning in create instance from backup form if guest os type is 
different
    
    * backupvmexpunged -> isbackupvmexpunged
    
    * review comments
    
    * fix npe
    
    * improve err msg
    
    * err msg
    
    ---------
    
    Co-authored-by: Suresh Kumar Anaparti <[email protected]>
---
 .../org/apache/cloudstack/api/ApiConstants.java    |  1 +
 .../api/response/BackupOfferingResponse.java       |  8 +++++
 .../cloudstack/api/response/BackupResponse.java    |  8 +++++
 .../cloudstack/backup/dao/BackupOfferingDao.java   |  2 +-
 .../backup/dao/BackupOfferingDaoImpl.java          |  5 ++-
 server/src/main/java/com/cloud/api/ApiDBUtils.java | 12 +++++--
 .../main/java/com/cloud/api/ApiResponseHelper.java |  4 +--
 .../main/java/com/cloud/vm/UserVmManagerImpl.java  |  8 ++---
 .../cloudstack/backup/BackupManagerImpl.java       | 39 ++++++++++++++++------
 .../cloudstack/backup/BackupManagerTest.java       |  6 ++--
 ui/public/locales/en.json                          |  2 ++
 ui/src/components/view/BackupMetadata.vue          | 22 +++++++-----
 ui/src/components/view/DeployVMFromBackup.vue      | 35 +++++++++++++++++++
 ui/src/views/storage/CreateVMFromBackup.vue        | 33 +++++++-----------
 14 files changed, 133 insertions(+), 52 deletions(-)

diff --git a/api/src/main/java/org/apache/cloudstack/api/ApiConstants.java 
b/api/src/main/java/org/apache/cloudstack/api/ApiConstants.java
index 5d2bbe858d7..307104af92c 100644
--- a/api/src/main/java/org/apache/cloudstack/api/ApiConstants.java
+++ b/api/src/main/java/org/apache/cloudstack/api/ApiConstants.java
@@ -64,6 +64,7 @@ public class ApiConstants {
     public static final String BACKUP_STORAGE_LIMIT = "backupstoragelimit";
     public static final String BACKUP_STORAGE_TOTAL = "backupstoragetotal";
     public static final String BACKUP_VM_OFFERING_REMOVED = 
"vmbackupofferingremoved";
+    public static final String IS_BACKUP_VM_EXPUNGED = "isbackupvmexpunged";
     public static final String BACKUP_TOTAL = "backuptotal";
     public static final String BASE64_IMAGE = "base64image";
     public static final String BGP_PEERS = "bgppeers";
diff --git 
a/api/src/main/java/org/apache/cloudstack/api/response/BackupOfferingResponse.java
 
b/api/src/main/java/org/apache/cloudstack/api/response/BackupOfferingResponse.java
index 0e895fa4e96..4120f68d9da 100644
--- 
a/api/src/main/java/org/apache/cloudstack/api/response/BackupOfferingResponse.java
+++ 
b/api/src/main/java/org/apache/cloudstack/api/response/BackupOfferingResponse.java
@@ -61,6 +61,10 @@ public class BackupOfferingResponse extends BaseResponse {
     @Param(description = "zone name")
     private String zoneName;
 
+    @SerializedName(ApiConstants.CROSS_ZONE_INSTANCE_CREATION)
+    @Param(description = "the backups with this offering can be used to create 
Instances on all Zones", since = "4.22.0")
+    private Boolean crossZoneInstanceCreation;
+
     @SerializedName(ApiConstants.CREATED)
     @Param(description = "the date this backup offering was created")
     private Date created;
@@ -97,6 +101,10 @@ public class BackupOfferingResponse extends BaseResponse {
         this.zoneName = zoneName;
     }
 
+    public void setCrossZoneInstanceCreation(Boolean 
crossZoneInstanceCreation) {
+        this.crossZoneInstanceCreation = crossZoneInstanceCreation;
+    }
+
     public void setCreated(Date created) {
         this.created = created;
     }
diff --git 
a/api/src/main/java/org/apache/cloudstack/api/response/BackupResponse.java 
b/api/src/main/java/org/apache/cloudstack/api/response/BackupResponse.java
index 0ae558ac803..b9eca67d9a8 100644
--- a/api/src/main/java/org/apache/cloudstack/api/response/BackupResponse.java
+++ b/api/src/main/java/org/apache/cloudstack/api/response/BackupResponse.java
@@ -123,6 +123,10 @@ public class BackupResponse extends BaseResponse {
     @Param(description = "The backup offering corresponding to this backup was 
removed from the VM", since = "4.21.0")
     private Boolean vmOfferingRemoved;
 
+    @SerializedName(ApiConstants.IS_BACKUP_VM_EXPUNGED)
+    @Param(description = "Indicates whether the VM from which the backup was 
taken is expunged or not", since = "4.22.0")
+    private Boolean isVmExpunged;
+
     public String getId() {
         return id;
     }
@@ -306,4 +310,8 @@ public class BackupResponse extends BaseResponse {
     public void setVmOfferingRemoved(Boolean vmOfferingRemoved) {
         this.vmOfferingRemoved = vmOfferingRemoved;
     }
+
+    public void setVmExpunged(Boolean isVmExpunged) {
+        this.isVmExpunged = isVmExpunged;
+    }
 }
diff --git 
a/engine/schema/src/main/java/org/apache/cloudstack/backup/dao/BackupOfferingDao.java
 
b/engine/schema/src/main/java/org/apache/cloudstack/backup/dao/BackupOfferingDao.java
index d001de8b6c6..d40e828121e 100644
--- 
a/engine/schema/src/main/java/org/apache/cloudstack/backup/dao/BackupOfferingDao.java
+++ 
b/engine/schema/src/main/java/org/apache/cloudstack/backup/dao/BackupOfferingDao.java
@@ -24,7 +24,7 @@ import org.apache.cloudstack.backup.BackupOfferingVO;
 import com.cloud.utils.db.GenericDao;
 
 public interface BackupOfferingDao extends GenericDao<BackupOfferingVO, Long> {
-    BackupOfferingResponse newBackupOfferingResponse(BackupOffering policy);
+    BackupOfferingResponse newBackupOfferingResponse(BackupOffering policy, 
Boolean crossZoneInstanceCreation);
     BackupOffering findByExternalId(String externalId, Long zoneId);
     BackupOffering findByName(String name, Long zoneId);
 }
diff --git 
a/engine/schema/src/main/java/org/apache/cloudstack/backup/dao/BackupOfferingDaoImpl.java
 
b/engine/schema/src/main/java/org/apache/cloudstack/backup/dao/BackupOfferingDaoImpl.java
index 9d67d07fe5e..a41e4e70d33 100644
--- 
a/engine/schema/src/main/java/org/apache/cloudstack/backup/dao/BackupOfferingDaoImpl.java
+++ 
b/engine/schema/src/main/java/org/apache/cloudstack/backup/dao/BackupOfferingDaoImpl.java
@@ -50,7 +50,7 @@ public class BackupOfferingDaoImpl extends 
GenericDaoBase<BackupOfferingVO, Long
     }
 
     @Override
-    public BackupOfferingResponse newBackupOfferingResponse(BackupOffering 
offering) {
+    public BackupOfferingResponse newBackupOfferingResponse(BackupOffering 
offering, Boolean crossZoneInstanceCreation) {
         DataCenterVO zone = dataCenterDao.findById(offering.getZoneId());
 
         BackupOfferingResponse response = new BackupOfferingResponse();
@@ -64,6 +64,9 @@ public class BackupOfferingDaoImpl extends 
GenericDaoBase<BackupOfferingVO, Long
             response.setZoneId(zone.getUuid());
             response.setZoneName(zone.getName());
         }
+        if (crossZoneInstanceCreation) {
+            response.setCrossZoneInstanceCreation(true);
+        }
         response.setCreated(offering.getCreated());
         response.setObjectName("backupoffering");
         return response;
diff --git a/server/src/main/java/com/cloud/api/ApiDBUtils.java 
b/server/src/main/java/com/cloud/api/ApiDBUtils.java
index 80043d0e279..f7ffb039801 100644
--- a/server/src/main/java/com/cloud/api/ApiDBUtils.java
+++ b/server/src/main/java/com/cloud/api/ApiDBUtils.java
@@ -75,9 +75,11 @@ import org.apache.cloudstack.api.response.VolumeResponse;
 import org.apache.cloudstack.api.response.VpcOfferingResponse;
 import org.apache.cloudstack.api.response.ZoneResponse;
 import org.apache.cloudstack.backup.BackupOffering;
+import org.apache.cloudstack.backup.BackupRepository;
 import org.apache.cloudstack.backup.BackupSchedule;
 import org.apache.cloudstack.backup.dao.BackupDao;
 import org.apache.cloudstack.backup.dao.BackupOfferingDao;
+import org.apache.cloudstack.backup.dao.BackupRepositoryDao;
 import org.apache.cloudstack.backup.dao.BackupScheduleDao;
 import org.apache.cloudstack.context.CallContext;
 import 
org.apache.cloudstack.engine.orchestration.service.NetworkOrchestrationService;
@@ -493,6 +495,7 @@ public class ApiDBUtils {
     static BackupDao s_backupDao;
     static BackupScheduleDao s_backupScheduleDao;
     static BackupOfferingDao s_backupOfferingDao;
+    static BackupRepositoryDao s_backupRepositoryDao;
     static NicDao s_nicDao;
     static ResourceManagerUtil s_resourceManagerUtil;
     static SnapshotPolicyDetailsDao s_snapshotPolicyDetailsDao;
@@ -751,6 +754,8 @@ public class ApiDBUtils {
     @Inject
     private BackupOfferingDao backupOfferingDao;
     @Inject
+    private BackupRepositoryDao backupRepositoryDao;
+    @Inject
     private BackupScheduleDao backupScheduleDao;
     @Inject
     private NicDao nicDao;
@@ -899,6 +904,7 @@ public class ApiDBUtils {
         s_backupDao = backupDao;
         s_backupScheduleDao = backupScheduleDao;
         s_backupOfferingDao = backupOfferingDao;
+        s_backupRepositoryDao = backupRepositoryDao;
         s_resourceIconDao = resourceIconDao;
         s_resourceManagerUtil = resourceManagerUtil;
         s_objectStoreDao = objectStoreDao;
@@ -2297,8 +2303,10 @@ public class ApiDBUtils {
         return s_backupScheduleDao.newBackupScheduleResponse(schedule);
     }
 
-    public static BackupOfferingResponse 
newBackupOfferingResponse(BackupOffering policy) {
-        return s_backupOfferingDao.newBackupOfferingResponse(policy);
+    public static BackupOfferingResponse 
newBackupOfferingResponse(BackupOffering offering) {
+        BackupRepository repository = 
s_backupRepositoryDao.findByUuid(offering.getExternalId());
+        Boolean crossZoneInstanceCreationEnabled = repository != null ? 
Boolean.TRUE.equals(repository.crossZoneInstanceCreationEnabled()) : false;
+        return s_backupOfferingDao.newBackupOfferingResponse(offering, 
crossZoneInstanceCreationEnabled);
     }
 
     public static NicVO findByIp4AddressAndNetworkId(String ip4Address, long 
networkId) {
diff --git a/server/src/main/java/com/cloud/api/ApiResponseHelper.java 
b/server/src/main/java/com/cloud/api/ApiResponseHelper.java
index 74a252fe244..5331f20e8bc 100644
--- a/server/src/main/java/com/cloud/api/ApiResponseHelper.java
+++ b/server/src/main/java/com/cloud/api/ApiResponseHelper.java
@@ -5040,8 +5040,8 @@ public class ApiResponseHelper implements 
ResponseGenerator {
     }
 
     @Override
-    public BackupOfferingResponse createBackupOfferingResponse(BackupOffering 
policy) {
-        return ApiDBUtils.newBackupOfferingResponse(policy);
+    public BackupOfferingResponse createBackupOfferingResponse(BackupOffering 
offering) {
+        return ApiDBUtils.newBackupOfferingResponse(offering);
     }
 
     public ManagementServerResponse 
createManagementResponse(ManagementServerHost mgmt) {
diff --git a/server/src/main/java/com/cloud/vm/UserVmManagerImpl.java 
b/server/src/main/java/com/cloud/vm/UserVmManagerImpl.java
index bd9d2ea0cd7..96c87c5376d 100644
--- a/server/src/main/java/com/cloud/vm/UserVmManagerImpl.java
+++ b/server/src/main/java/com/cloud/vm/UserVmManagerImpl.java
@@ -9606,11 +9606,11 @@ public class UserVmManagerImpl extends ManagerBase 
implements UserVmManager, Vir
         } else {
             String serviceOfferingUuid = 
backup.getDetail(ApiConstants.SERVICE_OFFERING_ID);
             if (serviceOfferingUuid == null) {
-                throw new CloudRuntimeException("Backup doesn't contain 
service offering uuid. Please specify a valid service offering id while 
creating the instance");
+                throw new CloudRuntimeException("Backup doesn't contain a 
Service Offering UUID. Please specify a valid Service Offering while creating 
the Instance");
             }
             serviceOffering = 
serviceOfferingDao.findByUuid(serviceOfferingUuid);
             if (serviceOffering == null) {
-                throw new CloudRuntimeException("Unable to find service 
offering with the uuid stored in backup. Please specify a valid service 
offering id while creating instance");
+                throw new CloudRuntimeException("Unable to find Service 
Offering with the UUID stored in the Backup. Please specify a valid Service 
Offering while creating the Instance");
             }
         }
         verifyServiceOffering(cmd, serviceOffering);
@@ -9625,11 +9625,11 @@ public class UserVmManagerImpl extends ManagerBase 
implements UserVmManager, Vir
         } else {
             String templateUuid = backup.getDetail(ApiConstants.TEMPLATE_ID);
             if (templateUuid == null) {
-                throw new CloudRuntimeException("Backup doesn't contain 
Template uuid. Please specify a valid Template/ISO while creating the 
instance");
+                throw new CloudRuntimeException("Backup doesn't contain a 
Template UUID. Please specify a valid Template/ISO while creating the 
Instance");
             }
             template = _templateDao.findByUuid(templateUuid);
             if (template == null) {
-                throw new CloudRuntimeException("Unable to find template 
associated with the backup. Please specify a valid Template/ISO while creating 
instance");
+                throw new CloudRuntimeException("Unable to find Template with 
the UUID stored in the Backup. Please specify a valid Template/ISO while 
creating the Instance");
             }
         }
         verifyTemplate(cmd, template, serviceOffering.getId());
diff --git 
a/server/src/main/java/org/apache/cloudstack/backup/BackupManagerImpl.java 
b/server/src/main/java/org/apache/cloudstack/backup/BackupManagerImpl.java
index b78ce450f1d..54a809fc859 100644
--- a/server/src/main/java/org/apache/cloudstack/backup/BackupManagerImpl.java
+++ b/server/src/main/java/org/apache/cloudstack/backup/BackupManagerImpl.java
@@ -122,12 +122,14 @@ import com.cloud.projects.Project;
 import com.cloud.serializer.GsonHelper;
 import com.cloud.service.dao.ServiceOfferingDao;
 import com.cloud.storage.DiskOfferingVO;
+import com.cloud.storage.GuestOSVO;
 import com.cloud.storage.ScopeType;
 import com.cloud.storage.Storage;
 import com.cloud.storage.Volume;
 import com.cloud.storage.VolumeApiService;
 import com.cloud.storage.VolumeVO;
 import com.cloud.storage.dao.DiskOfferingDao;
+import com.cloud.storage.dao.GuestOSDao;
 import com.cloud.storage.dao.VMTemplateDao;
 import com.cloud.storage.dao.VolumeDao;
 import com.cloud.template.VirtualMachineTemplate;
@@ -232,6 +234,8 @@ public class BackupManagerImpl extends ManagerBase 
implements BackupManager {
     private ResourceLimitService resourceLimitMgr;
     @Inject
     private AlertManager alertManager;
+    @Inject
+    private GuestOSDao _guestOSDao;
 
     private AsyncJobDispatcher asyncJobDispatcher;
     private Timer backupTimer;
@@ -379,7 +383,15 @@ public class BackupManagerImpl extends ManagerBase 
implements BackupManager {
         ServiceOffering serviceOffering = 
serviceOfferingDao.findById(vm.getServiceOfferingId());
         details.put(ApiConstants.SERVICE_OFFERING_ID, 
serviceOffering.getUuid());
         VirtualMachineTemplate template = 
vmTemplateDao.findById(vm.getTemplateId());
-        details.put(ApiConstants.TEMPLATE_ID, template.getUuid());
+        if (template != null) {
+            long guestOSId = template.getGuestOSId();
+            details.put(ApiConstants.TEMPLATE_ID, template.getUuid());
+            GuestOSVO guestOS = _guestOSDao.findById(guestOSId);
+            if (guestOS != null) {
+                details.put(ApiConstants.OS_TYPE_ID, guestOS.getUuid());
+                details.put(ApiConstants.OS_NAME, guestOS.getDisplayName());
+            }
+        }
 
         List<VMInstanceDetailVO> vmDetails = 
vmInstanceDetailsDao.listDetails(vm.getId());
         HashMap<String, String> settings = new HashMap<>();
@@ -2143,7 +2155,6 @@ public class BackupManagerImpl extends ManagerBase 
implements BackupManager {
         if (details.containsKey(ApiConstants.TEMPLATE_ID)) {
             VirtualMachineTemplate template = 
vmTemplateDao.findByUuid(details.get(ApiConstants.TEMPLATE_ID));
             if (template != null) {
-                details.put(ApiConstants.TEMPLATE_ID, template.getUuid());
                 details.put(ApiConstants.TEMPLATE_NAME, template.getName());
                 details.put(ApiConstants.IS_ISO, 
String.valueOf(template.getFormat().equals(Storage.ImageFormat.ISO)));
             }
@@ -2151,7 +2162,6 @@ public class BackupManagerImpl extends ManagerBase 
implements BackupManager {
         if (details.containsKey(ApiConstants.SERVICE_OFFERING_ID)) {
             ServiceOffering serviceOffering = 
serviceOfferingDao.findByUuid(details.get(ApiConstants.SERVICE_OFFERING_ID));
             if (serviceOffering != null) {
-                details.put(ApiConstants.SERVICE_OFFERING_ID, 
serviceOffering.getUuid());
                 details.put(ApiConstants.SERVICE_OFFERING_NAME, 
serviceOffering.getName());
             }
         }
@@ -2186,10 +2196,15 @@ public class BackupManagerImpl extends ManagerBase 
implements BackupManager {
         response.setId(backup.getUuid());
         response.setName(backup.getName());
         response.setDescription(backup.getDescription());
-        response.setVmName(vm.getHostName());
-        response.setVmId(vm.getUuid());
-        if (vm.getBackupOfferingId() == null || vm.getBackupOfferingId() != 
backup.getBackupOfferingId()) {
-            response.setVmOfferingRemoved(true);
+        if (vm != null) {
+            response.setVmName(vm.getHostName());
+            response.setVmId(vm.getUuid());
+            if (vm.getBackupOfferingId() == null || vm.getBackupOfferingId() 
!= backup.getBackupOfferingId()) {
+                response.setVmOfferingRemoved(true);
+            }
+        }
+        if (vm == null || 
VirtualMachine.State.Expunging.equals(vm.getState())) {
+            response.setVmExpunged(true);
         }
         response.setExternalId(backup.getExternalId());
         response.setType(backup.getType());
@@ -2205,9 +2220,11 @@ public class BackupManagerImpl extends ManagerBase 
implements BackupManager {
             }
         }
         // ACS 4.20: For backups taken prior this release the 
backup.backed_volumes column would be empty hence use vm_instance.backup_volumes
-        String backedUpVolumes;
+        String backedUpVolumes = "";
         if (Objects.isNull(backup.getBackedUpVolumes())) {
-            backedUpVolumes = new 
Gson().toJson(vm.getBackupVolumeList().toArray(), Backup.VolumeInfo[].class);
+            if (vm != null) {
+                backedUpVolumes = new 
Gson().toJson(vm.getBackupVolumeList().toArray(), Backup.VolumeInfo[].class);
+            }
         } else {
             backedUpVolumes = new 
Gson().toJson(backup.getBackedUpVolumes().toArray(), Backup.VolumeInfo[].class);
         }
@@ -2223,7 +2240,9 @@ public class BackupManagerImpl extends ManagerBase 
implements BackupManager {
 
         if (Boolean.TRUE.equals(listVmDetails)) {
             Map<String, String> vmDetails = new HashMap<>();
-            vmDetails.put(ApiConstants.HYPERVISOR, 
vm.getHypervisorType().toString());
+            if (vm != null) {
+                vmDetails.put(ApiConstants.HYPERVISOR, 
vm.getHypervisorType().toString());
+            }
             Map<String, String> details = 
getDetailsFromBackupDetails(backup.getId());
             vmDetails.putAll(details);
             response.setVmDetails(vmDetails);
diff --git 
a/server/src/test/java/org/apache/cloudstack/backup/BackupManagerTest.java 
b/server/src/test/java/org/apache/cloudstack/backup/BackupManagerTest.java
index c9391211fac..8b13fd47494 100644
--- a/server/src/test/java/org/apache/cloudstack/backup/BackupManagerTest.java
+++ b/server/src/test/java/org/apache/cloudstack/backup/BackupManagerTest.java
@@ -49,6 +49,7 @@ import com.cloud.storage.Volume;
 import com.cloud.storage.VolumeApiService;
 import com.cloud.storage.VolumeVO;
 import com.cloud.storage.dao.DiskOfferingDao;
+import com.cloud.storage.dao.GuestOSDao;
 import com.cloud.storage.dao.VMTemplateDao;
 import com.cloud.storage.dao.VolumeDao;
 import com.cloud.user.Account;
@@ -237,6 +238,9 @@ public class BackupManagerTest {
     @Mock
     DomainDao domainDao;
 
+    @Mock
+    private GuestOSDao _guestOSDao;
+
     private Gson gson;
 
     private String[] hostPossibleValues = {"127.0.0.1", "hostname"};
@@ -1572,14 +1576,12 @@ public class BackupManagerTest {
 
         VMTemplateVO template = mock(VMTemplateVO.class);
         when(template.getFormat()).thenReturn(Storage.ImageFormat.QCOW2);
-        when(template.getUuid()).thenReturn(templateUuid);
         when(template.getName()).thenReturn("template1");
         when(vmTemplateDao.findByUuid(templateUuid)).thenReturn(template);
         Map<String, String> details = new HashMap<>();
         details.put(ApiConstants.TEMPLATE_ID, templateUuid);
 
         ServiceOfferingVO serviceOffering = mock(ServiceOfferingVO.class);
-        when(serviceOffering.getUuid()).thenReturn(serviceOfferingUuid);
         when(serviceOffering.getName()).thenReturn("service-offering1");
         
when(serviceOfferingDao.findByUuid(serviceOfferingUuid)).thenReturn(serviceOffering);
         details.put(ApiConstants.SERVICE_OFFERING_ID, serviceOfferingUuid);
diff --git a/ui/public/locales/en.json b/ui/public/locales/en.json
index 5f1621c334e..0b6a4e3ceb9 100644
--- a/ui/public/locales/en.json
+++ b/ui/public/locales/en.json
@@ -2887,6 +2887,8 @@
 "message.action.create.snapshot.from.vmsnapshot": "Please confirm that you 
want to create Snapshot from Instance Snapshot",
 "message.action.create.instance.from.backup": "Please confirm that you want to 
create a new Instance from the given Backup.<br>Click on configure to edit the 
parameters for the new Instance before creation.",
 "message.create.instance.from.backup.different.zone": "Creating Instance from 
Backup on a different Zone. Please ensure that the backup repository is 
accessible in the selected Zone.",
+"message.template.ostype.different.from.backup": "Selected Template has a 
different OS type than the Backup. Please proceed with caution.",
+"message.iso.ostype.different.from.backup": "Selected ISO has a different OS 
type than the Backup. Please proceed with caution.",
 "message.action.delete.asnrange": "Please confirm the AS range that you want 
to delete",
 "message.action.delete.autoscale.vmgroup": "Please confirm that you want to 
delete this autoscaling group.",
 "message.action.delete.backup.offering": "Please confirm that you want to 
delete this backup offering?",
diff --git a/ui/src/components/view/BackupMetadata.vue 
b/ui/src/components/view/BackupMetadata.vue
index fea93a4e9c7..b8bf7fda916 100644
--- a/ui/src/components/view/BackupMetadata.vue
+++ b/ui/src/components/view/BackupMetadata.vue
@@ -42,6 +42,11 @@
               {{ getTemplateDisplayName() }}
             </router-link>
           </div>
+          <div v-else-if="item === 'ostypeid'">
+            <router-link :to="{ path: '/guestos' + '/' + backupMetadata[item] 
}">
+              {{ backupMetadata.osname }}
+            </router-link>
+          </div>
           <div v-else-if="item === 'serviceofferingid'">
             <router-link :to="{ path: '/computeoffering/' + 
backupMetadata[item] }">
               {{ getServiceOfferingDisplayName() }}
@@ -84,15 +89,14 @@ export default {
       if (!this.backupMetadata || Object.keys(this.backupMetadata).length === 
0) {
         return []
       }
-      const fieldOrder = []
-      fieldOrder.push('templateid')
-      if (this.backupMetadata.isiso === 'true') {
-        fieldOrder.push('hypervisor')
-      }
-      fieldOrder.push('serviceofferingid')
-      fieldOrder.push('nics')
-      fieldOrder.push('vmsettings')
-
+      const fieldOrder = [
+        'templateid',
+        'ostypeid',
+        'hypervisor',
+        'serviceofferingid',
+        'nics',
+        'vmsettings'
+      ]
       return fieldOrder.filter(field => this.backupMetadata[field] !== 
undefined)
     },
     getNicEntities () {
diff --git a/ui/src/components/view/DeployVMFromBackup.vue 
b/ui/src/components/view/DeployVMFromBackup.vue
index 1f6a7cc2c8d..c1f0ffc6d14 100644
--- a/ui/src/components/view/DeployVMFromBackup.vue
+++ b/ui/src/components/view/DeployVMFromBackup.vue
@@ -168,6 +168,23 @@
                       :tabList="tabList"
                       :activeTabKey="tabKey"
                       @tabChange="key => onTabChange(key, 'tabKey')">
+                      <a-alert
+                        v-if="showOsTypeWarning && selectedTemplateIso"
+                        style="margin-bottom: 10px">
+                        <template #message>
+                          <div>
+                            {{ selectedTemplateIso.message }}<br>
+                            {{ selectedTemplateIso.label }} :
+                            <router-link :to="{ path: '/guestos/' + 
selectedTemplateIso.item.ostypeid }">
+                              {{ selectedTemplateIso.item.ostypename }}
+                            </router-link><br>
+                            {{ $t('label.backup') }} :
+                            <router-link :to="{ path: '/guestos/' + 
dataPreFill.ostypeid }">
+                              {{ dataPreFill.ostypename }}
+                            </router-link>
+                          </div>
+                        </template>
+                      </a-alert>
                       <div v-if="tabKey === 'templateid'">
                         {{ $t('message.template.desc') }}
                         <div v-if="isZoneSelectedMultiArch" style="width: 
100%; margin-top: 5px">
@@ -933,6 +950,7 @@ export default {
       dataPreFill: {},
       showDetails: false,
       showRootDiskSizeChanger: false,
+      showOsTypeWarning: false,
       showOverrideDiskOfferingOption: false,
       securitygroupids: [],
       rootDiskSizeFixed: 0,
@@ -978,6 +996,15 @@ export default {
     isNormalUserOrProject () {
       return ['User'].includes(this.$store.getters.userInfo.roletype) || 
store.getters.project.id
     },
+    selectedTemplateIso () {
+      if (this.tabKey === 'templateid' && this.template?.ostypeid) {
+        return { label: this.$t('label.template'), item: this.template, 
message: this.$t('message.template.ostype.different.from.backup') }
+      }
+      if (this.tabKey === 'isoid' && this.iso?.ostypeid) {
+        return { label: this.$t('label.iso'), item: this.iso, message: 
this.$t('message.iso.ostype.different.from.backup') }
+      }
+      return null
+    },
     diskSize () {
       const customRootDiskSize = _.get(this.instanceConfig, 'rootdisksize', 
null)
       const customDataDiskSize = _.get(this.instanceConfig, 'size', null)
@@ -1687,6 +1714,7 @@ export default {
           this.form.iothreadsenabled = template.details && 
Object.prototype.hasOwnProperty.call(template.details, 'iothreads')
           this.form.iodriverpolicy = template.details?.['io.policy']
           this.form.keyboard = template.details?.keyboard
+          this.showOsTypeWarning = this.dataPreFill.ostypeid && 
template.ostypeid !== this.dataPreFill.ostypeid
           if (template.details['vmware-to-kvm-mac-addresses']) {
             this.dataPreFill.macAddressArray = 
JSON.parse(template.details['vmware-to-kvm-mac-addresses'])
           }
@@ -1700,6 +1728,13 @@ export default {
         this.tabKey = 'isoid'
         this.form.isoid = value
         this.form.templateid = null
+        for (const key in this.options.isos) {
+          var iso = _.find(_.get(this.options.isos[key], 'iso', []), (option) 
=> option.id === value)
+          if (iso) {
+            this.showOsTypeWarning = this.dataPreFill.ostypeid && iso.ostypeid 
!== this.dataPreFill.ostypeid
+            break
+          }
+        }
       } else if (['cpuspeed', 'cpunumber', 'memory'].includes(name)) {
         this.vm[name] = value
         this.form[name] = value
diff --git a/ui/src/views/storage/CreateVMFromBackup.vue 
b/ui/src/views/storage/CreateVMFromBackup.vue
index 0edee18ddd3..8d29397e45a 100644
--- a/ui/src/views/storage/CreateVMFromBackup.vue
+++ b/ui/src/views/storage/CreateVMFromBackup.vue
@@ -29,7 +29,7 @@
         <a-form-item :label="$t('label.name.optional')" name="name">
           <a-input v-model:value="form.name" />
         </a-form-item>
-        <a-form-item v-if="!resource.virtualmachineid" 
name="preserveIpAddresses" style="margin-top: 8px">
+        <a-form-item v-if="resource.isbackupvmexpunged" 
name="preserveIpAddresses" style="margin-top: 8px">
           <a-switch v-model:checked="form.preserveIpAddresses" />
           <template #label>
             <tooltip-label :title="$t('label.use.backup.ip.address')" 
:tooltip="$t('label.use.backup.ip.address.tooltip')"/>
@@ -91,16 +91,16 @@ export default {
       required: true
     }
   },
-  created () {
-    this.fetchServiceOffering()
-    this.fetchBackupOffering().then(() => {
-      this.fetchBackupRepository()
-      this.loading = false
-    })
+  async created () {
+    await Promise.all[(
+      this.fetchServiceOffering(),
+      this.fetchBackupOffering()
+    )]
+    this.loading = false
   },
   methods: {
     fetchServiceOffering () {
-      getAPI('listServiceOfferings', {
+      return getAPI('listServiceOfferings', {
         zoneid: this.resource.zoneid,
         id: this.resource.vmdetails.serviceofferingid,
         listall: true
@@ -118,28 +118,19 @@ export default {
         this.backupOffering = backupOfferings[0]
       })
     },
-    fetchBackupRepository () {
-      if (this.backupOffering.provider !== 'nas') {
-        return
-      }
-      getAPI('listBackupRepositories', {
-        id: this.backupOffering.externalid
-      }).then(response => {
-        const backupRepositories = 
response.listbackuprepositoriesresponse.backuprepository || []
-        this.backupRepository = backupRepositories[0]
-      })
-    },
     populatePreFillData () {
       this.vmdetails = this.resource.vmdetails
       this.dataPreFill.zoneid = this.resource.zoneid
-      this.dataPreFill.crosszoneinstancecreation = 
this.backupRepository?.crosszoneinstancecreation || 
this.backupOffering.provider === 'dummy'
+      this.dataPreFill.crosszoneinstancecreation = 
this.backupOffering?.crosszoneinstancecreation || this.backupOffering.provider 
=== 'dummy'
       this.dataPreFill.isIso = (this.vmdetails.isiso === 'true')
+      this.dataPreFill.ostypeid = this.resource.vmdetails.ostypeid
+      this.dataPreFill.ostypename = this.resource.vmdetails.osname
       this.dataPreFill.backupid = this.resource.id
       this.dataPreFill.computeofferingid = this.vmdetails.serviceofferingid
       this.dataPreFill.templateid = this.vmdetails.templateid
       this.dataPreFill.allowtemplateisoselection = true
       this.dataPreFill.isoid = this.vmdetails.templateid
-      this.dataPreFill.allowIpAddressesFetch = !this.resource.virtualmachineid
+      this.dataPreFill.allowIpAddressesFetch = this.resource.isbackupvmexpunged
       if (this.vmdetails.nics) {
         const nics = JSON.parse(this.vmdetails.nics)
         this.dataPreFill.networkids = nics.map(nic => nic.networkid)

Reply via email to