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 6fa1b4951564..0d39344a0a14 100644 --- a/api/src/main/java/org/apache/cloudstack/api/ApiConstants.java +++ b/api/src/main/java/org/apache/cloudstack/api/ApiConstants.java @@ -61,6 +61,8 @@ public class ApiConstants { public static final String BACKUP_AVAILABLE = "backupavailable"; public static final String BACKUP_ID = "backupid"; public static final String BACKUP_LIMIT = "backuplimit"; + public static final String BACKUP_PATH = "backuppath"; + public static final String BACKUP_PATHS = "backuppaths"; public static final String BACKUP_OFFERING_NAME = "backupofferingname"; public static final String BACKUP_OFFERING_ID = "backupofferingid"; public static final String BACKUP_OFFERING_DETAILS = "backupofferingdetails"; diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/backup/FinalizeBackupCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/backup/FinalizeBackupCmd.java index 45173f8668ee..d0ddd0ec75f6 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/backup/FinalizeBackupCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/backup/FinalizeBackupCmd.java @@ -79,7 +79,9 @@ public void execute() { throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to create Backup"); } - BackupResponse response = backupManager.createBackupResponse(backup, null); + boolean isCallerRootAdmin = _accountService.isRootAdmin(CallContext.current().getCallingAccountId()); + + BackupResponse response = backupManager.createBackupResponse(backup, null, isCallerRootAdmin); response.setResponseName(getCommandName()); setResponseObject(response); diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/backup/StartBackupCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/backup/StartBackupCmd.java index 1bf6d45db049..3fd41180b900 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/backup/StartBackupCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/backup/StartBackupCmd.java @@ -82,7 +82,8 @@ public String getDescription() { public void execute() { try { Backup backup = kvmBackupExportService.startBackup(this); - BackupResponse response = backupManager.createBackupResponse(backup, null); + boolean isCallerRootAdmin = _accountService.isRootAdmin(CallContext.current().getCallingAccountId()); + BackupResponse response = backupManager.createBackupResponse(backup, null, isCallerRootAdmin); response.setResponseName(getCommandName()); setResponseObject(response); diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/backup/ListBackupsCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/backup/ListBackupsCmd.java index 703fd9cd4358..a94151b9344a 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/backup/ListBackupsCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/backup/ListBackupsCmd.java @@ -148,12 +148,13 @@ public Boolean getListVmDetails() { ///////////////////////////////////////////////////// protected void setupResponseBackupList(final List backups, final Integer count) { + boolean isCallerRootAdmin = _accountService.isRootAdmin(CallContext.current().getCallingAccountId()); final List responses = new ArrayList<>(); for (Backup backup : backups) { if (backup == null) { continue; } - BackupResponse backupResponse = backupManager.createBackupResponse(backup, this.getListVmDetails()); + BackupResponse backupResponse = backupManager.createBackupResponse(backup, this.getListVmDetails(), isCallerRootAdmin); responses.add(backupResponse); } final ListResponse response = new ListResponse<>(); 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 70db01445edd..6f3d35235155 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 @@ -20,6 +20,7 @@ import org.apache.cloudstack.api.ApiConstants; import org.apache.cloudstack.api.BaseResponse; import org.apache.cloudstack.api.EntityReference; +import org.apache.cloudstack.api.response.helper.BackupVolumeNameAndPath; import org.apache.cloudstack.backup.Backup; import com.cloud.serializer.Param; @@ -99,6 +100,10 @@ public class BackupResponse extends BaseResponse { @Param(description = "Backup offering name") private String backupOfferingName; + @SerializedName(ApiConstants.BACKUP_PATHS) + @Param(description = "The backup paths in the secondary storage") + private Map backupPaths; + @SerializedName(ApiConstants.ACCOUNT_ID) @Param(description = "Account id") private String accountId; @@ -386,4 +391,12 @@ public void setHostId(String hostId) { public String getHostId() { return this.hostId; } + + public Map getBackupPaths() { + return backupPaths; + } + + public void setBackupPaths(Map backupPaths) { + this.backupPaths = backupPaths; + } } diff --git a/api/src/main/java/org/apache/cloudstack/api/response/helper/BackupVolumeNameAndPath.java b/api/src/main/java/org/apache/cloudstack/api/response/helper/BackupVolumeNameAndPath.java new file mode 100644 index 000000000000..9f6dc9f18307 --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/api/response/helper/BackupVolumeNameAndPath.java @@ -0,0 +1,51 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package org.apache.cloudstack.api.response.helper; + +import com.google.gson.annotations.SerializedName; +import org.apache.cloudstack.api.ApiConstants; + +public class BackupVolumeNameAndPath { + @SerializedName(ApiConstants.VOLUME_NAME) + private String volumeName; + + @SerializedName(ApiConstants.BACKUP_PATH) + private String backupPath; + + public BackupVolumeNameAndPath(String volumeName, String backupPath) { + this.volumeName = volumeName; + this.backupPath = backupPath; + } + + public String getVolumeName() { + return volumeName; + } + + public void setVolumeName(String volumeName) { + this.volumeName = volumeName; + } + + public String getBackupPath() { + return backupPath; + } + + public void setBackupPath(String backupPath) { + this.backupPath = backupPath; + } +} + diff --git a/api/src/main/java/org/apache/cloudstack/backup/BackupManager.java b/api/src/main/java/org/apache/cloudstack/backup/BackupManager.java index 30fc2bbec40d..aa4f43066f19 100644 --- a/api/src/main/java/org/apache/cloudstack/backup/BackupManager.java +++ b/api/src/main/java/org/apache/cloudstack/backup/BackupManager.java @@ -261,6 +261,8 @@ public interface BackupManager extends BackupService, Configurable, PluggableSer BackupResponse createBackupResponse(Backup backup, Boolean listVmDetails); + BackupResponse createBackupResponse(Backup backup, Boolean listVmDetails, boolean isCallerRootAdmin); + Capacity getBackupStorageUsedStats(Long zoneId); void checkAndRemoveBackupOfferingBeforeExpunge(VirtualMachine vm); diff --git a/engine/schema/src/main/java/org/apache/cloudstack/backup/dao/InternalBackupDataStoreDao.java b/engine/schema/src/main/java/org/apache/cloudstack/backup/dao/InternalBackupDataStoreDao.java index e71ffe5ebde9..16a470834617 100644 --- a/engine/schema/src/main/java/org/apache/cloudstack/backup/dao/InternalBackupDataStoreDao.java +++ b/engine/schema/src/main/java/org/apache/cloudstack/backup/dao/InternalBackupDataStoreDao.java @@ -17,9 +17,11 @@ package org.apache.cloudstack.backup.dao; import com.cloud.utils.db.GenericDao; +import org.apache.cloudstack.api.response.helper.BackupVolumeNameAndPath; import org.apache.cloudstack.backup.InternalBackupDataStoreVO; import java.util.List; +import java.util.Map; public interface InternalBackupDataStoreDao extends GenericDao { @@ -30,4 +32,6 @@ public interface InternalBackupDataStoreDao extends GenericDao listVolumeNamesAndBackupPaths(long backupId); } diff --git a/engine/schema/src/main/java/org/apache/cloudstack/backup/dao/InternalBackupDataStoreDaoImpl.java b/engine/schema/src/main/java/org/apache/cloudstack/backup/dao/InternalBackupDataStoreDaoImpl.java index 34a064ff62a2..0239d8c971df 100644 --- a/engine/schema/src/main/java/org/apache/cloudstack/backup/dao/InternalBackupDataStoreDaoImpl.java +++ b/engine/schema/src/main/java/org/apache/cloudstack/backup/dao/InternalBackupDataStoreDaoImpl.java @@ -19,19 +19,28 @@ import com.cloud.utils.db.GenericDaoBase; import com.cloud.utils.db.SearchBuilder; import com.cloud.utils.db.SearchCriteria; +import com.cloud.utils.db.TransactionLegacy; import com.cloud.utils.db.UpdateBuilder; +import org.apache.cloudstack.api.response.helper.BackupVolumeNameAndPath; import org.apache.cloudstack.backup.InternalBackupDataStoreVO; import javax.annotation.PostConstruct; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.HashMap; import java.util.List; +import java.util.Map; public class InternalBackupDataStoreDaoImpl extends GenericDaoBase implements InternalBackupDataStoreDao { - private SearchBuilder backupSearch; private static final String BACKUP_ID = "backup_id"; private static final String VOLUME_ID = "volume_id"; + private static final String SELECT_SECONDARY_PATH_AND_VOLUME_NAME = "select v.name, v.uuid, ibsr.`path` from internal_backup_store_ref ibsr join volumes v on ibsr.volume_id = v.id " + + "where ibsr.backup_id = ?"; + @PostConstruct protected void init() { backupSearch = createSearchBuilder(); @@ -71,4 +80,28 @@ public void updateVolumeId(long oldVolumeId, long newVolumeId) { UpdateBuilder ub = getUpdateBuilder(delta); update(ub, sc, null); } + + @Override + public Map listVolumeNamesAndBackupPaths(long backupId) { + Map volumeIdentifierToObjectMap = new HashMap<>(); + try (TransactionLegacy transactionLegacy = TransactionLegacy.currentTxn()) { + try (PreparedStatement preparedStatement = transactionLegacy.prepareStatement(SELECT_SECONDARY_PATH_AND_VOLUME_NAME)) { + preparedStatement.setLong(1, backupId); + + try (ResultSet resultSet = preparedStatement.executeQuery()) { + while (resultSet.next()) { + String volumeName = resultSet.getString(1); + String volumeUuid = resultSet.getString(2); + String backupPath = resultSet.getString(3); + + volumeIdentifierToObjectMap.put(volumeUuid, new BackupVolumeNameAndPath(volumeName, backupPath)); + } + } + } + } catch (SQLException e) { + logger.warn("Failed to find volume names and backup paths on secondary for backup [{}].", backupId, e); + } + return volumeIdentifierToObjectMap; + } + } 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 61e0b47a30ee..c540fe9537a2 100644 --- a/server/src/main/java/org/apache/cloudstack/backup/BackupManagerImpl.java +++ b/server/src/main/java/org/apache/cloudstack/backup/BackupManagerImpl.java @@ -86,6 +86,7 @@ import org.apache.cloudstack.backup.dao.BackupOfferingDetailsDao; import org.apache.cloudstack.backup.dao.BackupScheduleDao; import org.apache.cloudstack.backup.dao.BackupUsageMetricDao; +import org.apache.cloudstack.backup.dao.InternalBackupDataStoreDao; import org.apache.cloudstack.context.CallContext; import org.apache.cloudstack.framework.config.ConfigKey; import org.apache.cloudstack.framework.jobs.AsyncJobDispatcher; @@ -258,6 +259,8 @@ public class BackupManagerImpl extends ManagerBase implements BackupManager { private DomainHelper domainHelper; @Inject ReservationDao reservationDao; + @Inject + private InternalBackupDataStoreDao internalBackupDataStoreDao; private AsyncJobDispatcher asyncJobDispatcher; private Timer backupTimer; @@ -2654,12 +2657,18 @@ Map getDetailsFromBackupDetails(Long backupId) { @Override public BackupResponse createBackupResponse(Backup backup, Boolean listVmDetails) { + return createBackupResponse(backup, listVmDetails, false); + } + + @Override + public BackupResponse createBackupResponse(Backup backup, Boolean listVmDetails, boolean isCallerRootAdmin) { VMInstanceVO vm = vmInstanceDao.findByIdIncludingRemoved(backup.getVmId()); AccountVO account = accountDao.findByIdIncludingRemoved(backup.getAccountId()); DomainVO domain = domainDao.findByIdIncludingRemoved(backup.getDomainId()); DataCenterVO zone = dataCenterDao.findByIdIncludingRemoved(backup.getZoneId()); Long offeringId = backup.getBackupOfferingId(); BackupOffering offering = backupOfferingDao.findByIdIncludingRemoved(offeringId); + logger.trace("Got offering [{}] for backup [{}].", offering, backup); BackupResponse response = new BackupResponse(); response.setId(backup.getUuid()); @@ -2693,6 +2702,7 @@ public BackupResponse createBackupResponse(Backup backup, Boolean listVmDetails) } if (backup.getBackupScheduleId() != null) { BackupScheduleVO scheduleVO = backupScheduleDao.findById(backup.getBackupScheduleId()); + logger.trace("Got backup schedule [{}] for backup [{}].", scheduleVO, backup); if (scheduleVO != null) { response.setIntervalType(scheduleVO.getScheduleType().toString()); } @@ -2735,6 +2745,10 @@ public BackupResponse createBackupResponse(Backup backup, Boolean listVmDetails) response.setToCheckpointId(backup.getToCheckpointId()); } + if (offering != null && KBOSS_BACKUP_PROVIDER.equals(offering.getProvider()) && isCallerRootAdmin) { + response.setBackupPaths(internalBackupDataStoreDao.listVolumeNamesAndBackupPaths(backup.getId())); + } + response.setObjectName("backup"); return response; } diff --git a/server/src/test/java/org/apache/cloudstack/backup/BackupManagerResponsesTest.java b/server/src/test/java/org/apache/cloudstack/backup/BackupManagerResponsesTest.java new file mode 100644 index 000000000000..6eedf9912738 --- /dev/null +++ b/server/src/test/java/org/apache/cloudstack/backup/BackupManagerResponsesTest.java @@ -0,0 +1,282 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package org.apache.cloudstack.backup; + +import com.cloud.dc.DataCenter; +import com.cloud.dc.DataCenterVO; +import com.cloud.dc.dao.DataCenterDao; +import com.cloud.domain.DomainVO; +import com.cloud.domain.dao.DomainDao; +import com.cloud.hypervisor.Hypervisor; +import com.cloud.network.dao.NetworkDao; +import com.cloud.network.dao.NetworkVO; +import com.cloud.service.ServiceOfferingVO; +import com.cloud.service.dao.ServiceOfferingDao; +import com.cloud.storage.Storage; +import com.cloud.storage.VMTemplateVO; +import com.cloud.storage.dao.VMTemplateDao; +import com.cloud.user.AccountVO; +import com.cloud.user.dao.AccountDao; +import com.cloud.vm.VMInstanceVO; +import com.cloud.vm.VirtualMachine; +import com.cloud.vm.dao.VMInstanceDao; +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.response.BackupResponse; +import org.apache.cloudstack.api.response.helper.BackupVolumeNameAndPath; +import org.apache.cloudstack.backup.dao.BackupDetailsDao; +import org.apache.cloudstack.backup.dao.BackupOfferingDao; +import org.apache.cloudstack.backup.dao.InternalBackupDataStoreDao; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.mockito.Spy; +import org.mockito.junit.MockitoJUnitRunner; +import org.springframework.test.util.ReflectionTestUtils; + +import java.util.HashMap; +import java.util.Map; +import java.util.UUID; + +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +@RunWith(MockitoJUnitRunner.class) +public class BackupManagerResponsesTest { + @Spy + @InjectMocks + private BackupManagerImpl backupManager = new BackupManagerImpl(); + + @Mock + private VMInstanceDao vmInstanceDao; + @Mock + private AccountDao accountDao; + @Mock + private DomainDao domainDao; + @Mock + private DataCenterDao dataCenterDao; + @Mock + private BackupOfferingDao backupOfferingDao; + @Mock + private VMTemplateDao vmTemplateDao; + @Mock + private ServiceOfferingDao serviceOfferingDao; + @Mock + private NetworkDao networkDao; + @Mock + private BackupDetailsDao backupDetailsDao; + @Mock + private InternalBackupDataStoreDao internalBackupDataStoreDao; + + // Entities + private BackupVO backup; + private VMInstanceVO virtualMachine; + private AccountVO account; + private DomainVO domain; + private DataCenterVO zone; + private BackupOfferingVO backupOffering; + private VMTemplateVO template; + private ServiceOfferingVO serviceOffering; + private NetworkVO network; + + @Before + public void setup() { + Long vmId = 1L; + Long accountId = 2L; + Long domainId = 3L; + Long zoneId = 4L; + Long vmOfferingId = 5L; + Long backupOfferingId = 6L; + Long backupId = 7L; + Long templateId = 8L; + String templateUuid = "template-uuid1"; + String serviceOfferingUuid = "service-offering-uuid1"; + + backup = new BackupVO(); + ReflectionTestUtils.setField(backup, "id", backupId); + ReflectionTestUtils.setField(backup, "uuid", "backup-uuid"); + backup.setVmId(vmId); + backup.setAccountId(accountId); + backup.setDomainId(domainId); + backup.setZoneId(zoneId); + backup.setBackupOfferingId(backupOfferingId); + backup.setType("Full"); + backup.setBackupScheduleId(null); + + virtualMachine = new VMInstanceVO(vmId, 0L, "test-vm", "test-vm", VirtualMachine.Type.User, + 0L, Hypervisor.HypervisorType.Simulator, 0L, domainId, accountId, 0L, false); + virtualMachine.setDataCenterId(zoneId); + virtualMachine.setBackupOfferingId(vmOfferingId); + virtualMachine.setTemplateId(templateId); + + account = new AccountVO(); + account.setUuid("account-uuid"); + account.setAccountName("test-account"); + + domain = new DomainVO(); + domain.setUuid("domain-uuid"); + domain.setName("test-domain"); + + zone = new DataCenterVO(1L, "test-zone", null, null, null, null, null, null, null, null, DataCenter.NetworkType.Advanced, null, null); + zone.setUuid("zone-uuid"); + + backupOffering = new BackupOfferingVO(); + ReflectionTestUtils.setField(backupOffering, "id", backupOfferingId); + ReflectionTestUtils.setField(backupOffering, "uuid", "offering-uuid"); + backupOffering.setName("test-offering"); + + Mockito.when(vmInstanceDao.findByIdIncludingRemoved(vmId)).thenReturn(virtualMachine); + Mockito.when(accountDao.findByIdIncludingRemoved(accountId)).thenReturn(account); + Mockito.when(domainDao.findByIdIncludingRemoved(domainId)).thenReturn(domain); + Mockito.when(dataCenterDao.findByIdIncludingRemoved(zoneId)).thenReturn(zone); + Mockito.when(backupOfferingDao.findByIdIncludingRemoved(backupOfferingId)).thenReturn(backupOffering); + + template = new VMTemplateVO(); + template.setFormat(Storage.ImageFormat.QCOW2); + template.setName("template1"); + when(vmTemplateDao.findByUuid(templateUuid)).thenReturn(template); + + + serviceOffering = mock(ServiceOfferingVO.class); + when(serviceOffering.getName()).thenReturn("service-offering1"); + when(serviceOfferingDao.findByUuid(serviceOfferingUuid)).thenReturn(serviceOffering); + + network = new NetworkVO(); + network.setName("network1"); + when(networkDao.findByUuid("network-uuid1")).thenReturn(network); + + + Map details = new HashMap<>(); + details.put(ApiConstants.TEMPLATE_ID, templateUuid); + details.put(ApiConstants.SERVICE_OFFERING_ID, serviceOfferingUuid); + details.put(ApiConstants.NICS, "[{\"networkid\":\"network-uuid1\"}]"); + Mockito.when(backupDetailsDao.listDetailsKeyPairs(backup.getId(), true)).thenReturn(details); + } + + @Test + public void createBackupResponseTestBasicResponse() { + BackupResponse response = backupManager.createBackupResponse(backup, true, true); + + assertFullBackupResponseFields(response); + Assert.assertNull(response.getBackupPaths()); + } + + @Test + public void createBackupResponseTestKBOSSResponseAdmin() { + ReflectionTestUtils.setField(backupOffering, "provider", BackupManagerImpl.KBOSS_BACKUP_PROVIDER); + + Map backupJoins = getMapOfBackupJoins(1); + Mockito.when(internalBackupDataStoreDao.listVolumeNamesAndBackupPaths(Mockito.anyLong())).thenReturn(backupJoins); + + BackupResponse response = backupManager.createBackupResponse(backup, true, true); + + assertFullBackupResponseFields(response); + Assert.assertFalse(response.getBackupPaths().isEmpty()); + + for (Map.Entry backupJoin : backupJoins.entrySet()) { + Assert.assertTrue(response.getBackupPaths().containsKey(backupJoin.getKey())); + Assert.assertEquals(backupJoin.getValue().getBackupPath(), response.getBackupPaths().get(backupJoin.getKey()).getBackupPath()); + Assert.assertEquals(backupJoin.getValue().getVolumeName(), response.getBackupPaths().get(backupJoin.getKey()).getVolumeName()); + } + } + + @Test + public void createBackupResponseTestKBOSSResponseMultipleEqualNamedVolumes() { + ReflectionTestUtils.setField(backupOffering, "provider", BackupManagerImpl.KBOSS_BACKUP_PROVIDER); + + int backupAmount = 3; + Map backupJoins = getMapOfBackupJoins(backupAmount); + Mockito.when(internalBackupDataStoreDao.listVolumeNamesAndBackupPaths(Mockito.anyLong())).thenReturn(backupJoins); + + BackupResponse response = backupManager.createBackupResponse(backup, true, true); + + assertFullBackupResponseFields(response); + Assert.assertFalse(response.getBackupPaths().isEmpty()); + Assert.assertEquals(backupAmount, response.getBackupPaths().size()); + + for (Map.Entry backupJoin : backupJoins.entrySet()) { + Assert.assertTrue(response.getBackupPaths().containsKey(backupJoin.getKey())); + Assert.assertEquals(backupJoin.getValue().getBackupPath(), response.getBackupPaths().get(backupJoin.getKey()).getBackupPath()); + Assert.assertEquals(backupJoin.getValue().getVolumeName(), response.getBackupPaths().get(backupJoin.getKey()).getVolumeName()); + } + } + + @Test + public void createBackupResponseTestKBOSSResponseNormalUser() { + ReflectionTestUtils.setField(backupOffering, "provider", BackupManagerImpl.KBOSS_BACKUP_PROVIDER); + + BackupResponse response = backupManager.createBackupResponse(backup, true, false); + + assertFullBackupResponseFields(response); + Assert.assertNull(response.getBackupPaths()); + } + + @Test + public void createBackupResponseTestKBOSSResponseNullOffering() { + Mockito.when(backupOfferingDao.findByIdIncludingRemoved(Mockito.anyLong())).thenReturn(null); + + BackupResponse response = backupManager.createBackupResponse(backup, true, true); + + assertBasicBackupResponseFields(response); + Assert.assertNull(response.getBackupPaths()); + } + + private void assertFullBackupResponseFields(BackupResponse response) { + assertBasicBackupResponseFields(response); + assertBackupResponseOfferingFields(response); + } + + private void assertBasicBackupResponseFields(BackupResponse response) { + Assert.assertEquals("backup-uuid", response.getId()); + Assert.assertEquals("test-vm", response.getVmName()); + Assert.assertEquals("account-uuid", response.getAccountId()); + Assert.assertEquals("test-account", response.getAccount()); + Assert.assertEquals("domain-uuid", response.getDomainId()); + Assert.assertEquals("test-domain", response.getDomain()); + Assert.assertEquals("zone-uuid", response.getZoneId()); + Assert.assertEquals("test-zone", response.getZone()); + Assert.assertEquals("MANUAL", response.getIntervalType()); + Assert.assertEquals("{serviceofferingid=service-offering-uuid1, isiso=false, hypervisor=Simulator, " + + "nics=[{\"networkid\":\"network-uuid1\",\"networkname\":\"network1\"}], serviceofferingname=service-offering1, " + + "templatename=template1, templateid=template-uuid1}", response.getVmDetails().toString()); + Assert.assertEquals(true, response.getVmOfferingRemoved()); + } + + private void assertBackupResponseOfferingFields(BackupResponse response) { + Assert.assertEquals("offering-uuid", response.getBackupOfferingId()); + Assert.assertEquals("test-offering", response.getBackupOffering()); + } + + private Map getMapOfBackupJoins(int backups) { + String basePath = "/backup/path/"; + String baseName = "VOL-"; + Map backupJoins = new HashMap<>(); + + for (int i = 0; i < backups; i++) { + String path = basePath + i; + String name = baseName + i; + + backupJoins.put(UUID.randomUUID().toString(), new BackupVolumeNameAndPath(path, name)); + } + + return backupJoins; + } +} 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 3f8d55977504..26d3bc34221b 100644 --- a/server/src/test/java/org/apache/cloudstack/backup/BackupManagerTest.java +++ b/server/src/test/java/org/apache/cloudstack/backup/BackupManagerTest.java @@ -71,7 +71,6 @@ import org.apache.cloudstack.api.command.user.backup.DeleteBackupScheduleCmd; import org.apache.cloudstack.api.command.user.backup.ListBackupOfferingsCmd; import org.apache.cloudstack.api.command.user.backup.ListBackupScheduleCmd; -import org.apache.cloudstack.api.response.BackupResponse; import org.apache.cloudstack.backup.dao.BackupDao; import org.apache.cloudstack.backup.dao.BackupDetailsDao; import org.apache.cloudstack.backup.dao.BackupUsageMetricDao; @@ -94,7 +93,6 @@ import com.cloud.api.query.vo.UserVmJoinVO; import com.cloud.capacity.CapacityVO; import com.cloud.configuration.Resource; -import com.cloud.dc.DataCenter; import com.cloud.dc.DataCenterVO; import com.cloud.dc.dao.DataCenterDao; import com.cloud.domain.Domain; @@ -116,7 +114,6 @@ import com.cloud.service.ServiceOfferingVO; import com.cloud.service.dao.ServiceOfferingDao; import com.cloud.storage.DiskOfferingVO; -import com.cloud.storage.Storage; import com.cloud.storage.VMTemplateVO; import com.cloud.storage.Volume; import com.cloud.storage.VolumeApiService; @@ -1833,95 +1830,6 @@ public void testDeleteBackupBlockedByPendingJobs() throws ResourceAllocationExce backupManager.deleteBackup(backupId, false); } - @Test - public void testNewBackupResponse() { - Long vmId = 1L; - Long accountId = 2L; - Long domainId = 3L; - Long zoneId = 4L; - Long vmOfferingId = 5L; - Long backupOfferingId = 6L; - Long backupId = 7L; - Long templateId = 8L; - String templateUuid = "template-uuid1"; - String serviceOfferingUuid = "service-offering-uuid1"; - - BackupVO backup = new BackupVO(); - ReflectionTestUtils.setField(backup, "id", backupId); - ReflectionTestUtils.setField(backup, "uuid", "backup-uuid"); - backup.setVmId(vmId); - backup.setAccountId(accountId); - backup.setDomainId(domainId); - backup.setZoneId(zoneId); - backup.setBackupOfferingId(backupOfferingId); - backup.setType("Full"); - backup.setBackupScheduleId(null); - - VMInstanceVO vm = new VMInstanceVO(vmId, 0L, "test-vm", "test-vm", VirtualMachine.Type.User, - 0L, Hypervisor.HypervisorType.Simulator, 0L, domainId, accountId, 0L, false); - vm.setDataCenterId(zoneId); - vm.setBackupOfferingId(vmOfferingId); - vm.setTemplateId(templateId); - - AccountVO account = new AccountVO(); - account.setUuid("account-uuid"); - account.setAccountName("test-account"); - - DomainVO domain = new DomainVO(); - domain.setUuid("domain-uuid"); - domain.setName("test-domain"); - - DataCenterVO zone = new DataCenterVO(1L, "test-zone", null, null, null, null, null, null, null, null, DataCenter.NetworkType.Advanced, null, null); - zone.setUuid("zone-uuid"); - - BackupOfferingVO offering = Mockito.mock(BackupOfferingVO.class); - Mockito.when(offering.getUuid()).thenReturn("offering-uuid"); - Mockito.when(offering.getName()).thenReturn("test-offering"); - - Mockito.when(vmInstanceDao.findByIdIncludingRemoved(vmId)).thenReturn(vm); - Mockito.when(accountDao.findByIdIncludingRemoved(accountId)).thenReturn(account); - Mockito.when(domainDao.findByIdIncludingRemoved(domainId)).thenReturn(domain); - Mockito.when(dataCenterDao.findByIdIncludingRemoved(zoneId)).thenReturn(zone); - Mockito.when(backupOfferingDao.findByIdIncludingRemoved(backupOfferingId)).thenReturn(offering); - - VMTemplateVO template = mock(VMTemplateVO.class); - when(template.getFormat()).thenReturn(Storage.ImageFormat.QCOW2); - when(template.getName()).thenReturn("template1"); - when(vmTemplateDao.findByUuid(templateUuid)).thenReturn(template); - Map details = new HashMap<>(); - details.put(ApiConstants.TEMPLATE_ID, templateUuid); - - ServiceOfferingVO serviceOffering = mock(ServiceOfferingVO.class); - when(serviceOffering.getName()).thenReturn("service-offering1"); - when(serviceOfferingDao.findByUuid(serviceOfferingUuid)).thenReturn(serviceOffering); - details.put(ApiConstants.SERVICE_OFFERING_ID, serviceOfferingUuid); - - NetworkVO network = mock(NetworkVO.class); - when(network.getName()).thenReturn("network1"); - when(networkDao.findByUuid("network-uuid1")).thenReturn(network); - details.put(ApiConstants.NICS, "[{\"networkid\":\"network-uuid1\"}]"); - - Mockito.when(backupDetailsDao.listDetailsKeyPairs(backup.getId(), true)).thenReturn(details); - - BackupResponse response = backupManager.createBackupResponse(backup, true); - - Assert.assertEquals("backup-uuid", response.getId()); - Assert.assertEquals("test-vm", response.getVmName()); - Assert.assertEquals("account-uuid", response.getAccountId()); - Assert.assertEquals("test-account", response.getAccount()); - Assert.assertEquals("domain-uuid", response.getDomainId()); - Assert.assertEquals("test-domain", response.getDomain()); - Assert.assertEquals("zone-uuid", response.getZoneId()); - Assert.assertEquals("test-zone", response.getZone()); - Assert.assertEquals("offering-uuid", response.getBackupOfferingId()); - Assert.assertEquals("test-offering", response.getBackupOffering()); - Assert.assertEquals("MANUAL", response.getIntervalType()); - Assert.assertEquals("{serviceofferingid=service-offering-uuid1, isiso=false, hypervisor=Simulator, " + - "nics=[{\"networkid\":\"network-uuid1\",\"networkname\":\"network1\"}], serviceofferingname=service-offering1, " + - "templatename=template1, templateid=template-uuid1}", response.getVmDetails().toString()); - Assert.assertEquals(true, response.getVmOfferingRemoved()); - } - @Test public void validateAndGetDefaultBackupRetentionIfRequiredTestReturnZeroAsDefaultValue() { int retention = backupManager.validateAndGetDefaultBackupRetentionIfRequired(null, backupOfferingVOMock, null); diff --git a/ui/public/config.json b/ui/public/config.json index 1ca42f04b67c..81d3938a5dee 100644 --- a/ui/public/config.json +++ b/ui/public/config.json @@ -105,155 +105,7 @@ "imageSelectionInterface": "modern", "showUserCategoryForModernImageSelection": true, "showAllCategoryForModernImageSelection": false, - "docHelpMappings": { - "adminguide/accounts.html": "adminguide/accounts.html", - "adminguide/accounts.html#domains": "adminguide/accounts.html#domains", - "adminguide/accounts.html#keypairs": "adminguide/accounts.html#keypairs", - "adminguide/accounts.html#roles": "adminguide/accounts.html#roles", - "adminguide/accounts.html#users": "adminguide/accounts.html#users", - "adminguide/accounts.html#using-an-ldap-server-for-user-authentication": "adminguide/accounts.html#using-an-ldap-server-for-user-authentication", - "adminguide/autoscale_with_virtual_router.html": "adminguide/autoscale_with_virtual_router.html", - "adminguide/events.html": "adminguide/events.html", - "adminguide/events.html#creating-webhooks": "adminguide/events.html#creating-webhooks", - "adminguide/events.html#deleting-and-archiving-events-and-alerts": "adminguide/events.html#deleting-and-archiving-events-and-alerts", - "adminguide/extensions.html": "adminguide/extensions.html", - "adminguide/extensions.html#custom-actions": "adminguide/extensions.html#custom-actions", - "adminguide/guest_os.html#guest-os": "adminguide/guest_os.html#guest-os", - "adminguide/guest_os.html#guest-os-categories": "adminguide/guest_os.html#guest-os-categories", - "adminguide/guest_os.html#guest-os-hypervisor-mapping": "adminguide/guest_os.html#guest-os-hypervisor-mapping", - "adminguide/hosts.html#disabling-and-enabling-zones-pods-and-clusters": "adminguide/hosts.html#disabling-and-enabling-zones-pods-and-clusters", - "adminguide/hosts.html?highlight=Hypervisor%20capabilities#hypervisor-capabilities": "adminguide/hosts.html?highlight=Hypervisor%20capabilities#hypervisor-capabilities", - "adminguide/hosts.html#kvm-rolling-maintenance": "adminguide/hosts.html#kvm-rolling-maintenance", - "adminguide/hosts.html#maintaining-hypervisors-on-hosts": "adminguide/hosts.html#maintaining-hypervisors-on-hosts", - "adminguide/hosts.html#out-of-band-management": "adminguide/hosts.html#out-of-band-management", - "adminguide/hosts.html#removing-hosts": "adminguide/hosts.html#removing-hosts", - "adminguide/index.html#tuning": "adminguide/index.html#tuning", - "adminguide/kms.html#adding-an-hsm-profile": "adminguide/kms.html#adding-an-hsm-profile", - "adminguide/kms.html#creating-a-kms-key": "adminguide/kms.html#creating-a-kms-key", - "adminguide/kms.html#migrating-existing-volumes-to-kms": "adminguide/kms.html#migrating-existing-volumes-to-kms", - "adminguide/kms.html#rotating-a-kms-key": "adminguide/kms.html#rotating-a-kms-key", - "adminguide/management.html#administrator-alerts": "adminguide/management.html#administrator-alerts", - "adminguide/management.html#metrics": "adminguide/management.html#metrics", - "adminguide/management.html#reporting-cpu-sockets": "adminguide/management.html#reporting-cpu-sockets", - "adminguide/nas_plugin.html": "adminguide/nas_plugin.html", - "adminguide/networking_and_traffic.html#acl-on-private-gateway": "adminguide/networking_and_traffic.html#acl-on-private-gateway", - "adminguide/networking_and_traffic.html#adding-an-additional-guest-network": "adminguide/networking_and_traffic.html#adding-an-additional-guest-network", - "adminguide/networking_and_traffic.html#adding-a-private-gateway-to-a-vpc": "adminguide/networking_and_traffic.html#adding-a-private-gateway-to-a-vpc", - "adminguide/networking_and_traffic.html#adding-a-security-group": "adminguide/networking_and_traffic.html#adding-a-security-group", - "adminguide/networking_and_traffic.html#adding-a-virtual-private-cloud": "adminguide/networking_and_traffic.html#adding-a-virtual-private-cloud", - "adminguide/networking_and_traffic.html#advanced-zone-physical-network-configuration": "adminguide/networking_and_traffic.html#advanced-zone-physical-network-configuration", - "adminguide/networking_and_traffic.html#basic-zone-physical-network-configuration": "adminguide/networking_and_traffic.html#basic-zone-physical-network-configuration", - "adminguide/networking_and_traffic.html#configure-guest-traffic-in-an-advanced-zone": "adminguide/networking_and_traffic.html#configure-guest-traffic-in-an-advanced-zone", - "adminguide/networking_and_traffic.html#configuring-a-virtual-private-cloud": "adminguide/networking_and_traffic.html#configuring-a-virtual-private-cloud", - "adminguide/networking_and_traffic.html#configuring-network-access-control-list": "adminguide/networking_and_traffic.html#configuring-network-access-control-list", - "adminguide/networking_and_traffic.html#creating-acl-lists": "adminguide/networking_and_traffic.html#creating-acl-lists", - "adminguide/networking_and_traffic.html#creating-and-updating-a-vpn-customer-gateway": "adminguide/networking_and_traffic.html#creating-and-updating-a-vpn-customer-gateway", - "adminguide/networking_and_traffic.html#creating-an-internal-lb-rule": "adminguide/networking_and_traffic.html#creating-an-internal-lb-rule", - "adminguide/networking_and_traffic.html#creating-a-vpn-connection": "adminguide/networking_and_traffic.html#creating-a-vpn-connection", - "adminguide/networking_and_traffic.html#creating-a-vpn-gateway-for-the-vpc": "adminguide/networking_and_traffic.html#creating-a-vpn-gateway-for-the-vpc", - "adminguide/networking_and_traffic.html#enabling-or-disabling-static-nat": "adminguide/networking_and_traffic.html#enabling-or-disabling-static-nat", - "adminguide/networking_and_traffic.html#load-balancing-across-tiers": "adminguide/networking_and_traffic.html#load-balancing-across-tiers", - "adminguide/networking_and_traffic.html#releasing-an-ip-address-alloted-to-a-vpc": "adminguide/networking_and_traffic.html#releasing-an-ip-address-alloted-to-a-vpc", - "adminguide/networking_and_traffic.html#reserving-public-ip-addresses-and-vlans-for-accounts": "adminguide/networking_and_traffic.html#reserving-public-ip-addresses-and-vlans-for-accounts", - "adminguide/networking_and_traffic.html#restarting-and-removing-a-vpn-connection": "adminguide/networking_and_traffic.html#restarting-and-removing-a-vpn-connection", - "adminguide/networking_and_traffic.html#security-groups": "adminguide/networking_and_traffic.html#security-groups", - "adminguide/networking_and_traffic.html#setting-up-a-site-to-site-vpn-connection": "adminguide/networking_and_traffic.html#setting-up-a-site-to-site-vpn-connection", - "adminguide/networking_and_traffic.html#updating-and-removing-a-vpn-customer-gateway": "adminguide/networking_and_traffic.html#updating-and-removing-a-vpn-customer-gateway", - "adminguide/networking.html#creating-a-new-network-offering": "adminguide/networking.html#creating-a-new-network-offering", - "adminguide/networking.html#network-offerings": "adminguide/networking.html#network-offerings", - "adminguide/networking.html#network-service-providers": "adminguide/networking.html#network-service-providers", - "adminguide/networking/vnf_templates_appliances.html#deploying-vnf-appliances": "adminguide/networking/vnf_templates_appliances.html#deploying-vnf-appliances", - "adminguide/object_storage.html#update-bucket": "adminguide/object_storage.html#update-bucket", - "adminguide/projects.html": "adminguide/projects.html", - "adminguide/projects.html#accepting-a-membership-invitation": "adminguide/projects.html#accepting-a-membership-invitation", - "adminguide/projects.html#adding-project-members-from-the-ui": "adminguide/projects.html#adding-project-members-from-the-ui", - "adminguide/projects.html#creating-a-new-project": "adminguide/projects.html#creating-a-new-project", - "adminguide/projects.html#sending-project-membership-invitations": "adminguide/projects.html#sending-project-membership-invitations", - "adminguide/projects.html#suspending-or-deleting-a-project": "adminguide/projects.html#suspending-or-deleting-a-project", - "adminguide/reliability.html#ha-for-hosts": "adminguide/reliability.html#ha-for-hosts", - "adminguide/service_offerings.html#compute-and-disk-service-offerings": "adminguide/service_offerings.html#compute-and-disk-service-offerings", - "adminguide/service_offerings.html#creating-a-new-compute-offering": "adminguide/service_offerings.html#creating-a-new-compute-offering", - "adminguide/service_offerings.html#creating-a-new-disk-offering": "adminguide/service_offerings.html#creating-a-new-disk-offering", - "adminguide/service_offerings.html#creating-a-new-system-service-offering": "adminguide/service_offerings.html#creating-a-new-system-service-offering", - "adminguide/service_offerings.html#modifying-or-deleting-a-service-offering": "adminguide/service_offerings.html#modifying-or-deleting-a-service-offering", - "adminguide/service_offerings.html#system-service-offerings": "adminguide/service_offerings.html#system-service-offerings", - "adminguide/storage.html#creating-a-new-file-share": "adminguide/storage.html#creating-a-new-file-share", - "adminguide/storage.html#creating-a-new-volume": "adminguide/storage.html#creating-a-new-volume", - "adminguide/storage.html#id2": "adminguide/storage.html#id2", - "adminguide/storage.html#lifecycle-operations": "adminguide/storage.html#lifecycle-operations", - "adminguide/storage.html#object-storage": "adminguide/storage.html#object-storage", - "adminguide/storage.html#primary-storage": "adminguide/storage.html#primary-storage", - "adminguide/storage.html#resizing-volumes": "adminguide/storage.html#resizing-volumes", - "adminguide/storage.html#secondary-storage": "adminguide/storage.html#secondary-storage", - "adminguide/storage.html#uploading-an-existing-volume-to-a-virtual-machine": "adminguide/storage.html#uploading-an-existing-volume-to-a-virtual-machine", - "adminguide/storage.html#working-with-volumes": "adminguide/storage.html#working-with-volumes", - "adminguide/storage.html#working-with-volume-snapshots": "adminguide/storage.html#working-with-volume-snapshots", - "adminguide/systemvm.html": "adminguide/systemvm.html", - "adminguide/systemvm.html#upgrading-virtual-routers": "adminguide/systemvm.html#upgrading-virtual-routers", - "adminguide/systemvm.html#virtual-router": "adminguide/systemvm.html#virtual-router", - "adminguide/templates.html": "adminguide/templates.html", - "adminguide/templates.html#attaching-an-iso-to-a-vm": "adminguide/templates.html#attaching-an-iso-to-a-vm", - "adminguide/templates.html#exporting-templates": "adminguide/templates.html#exporting-templates", - "adminguide/templates.html#id10": "adminguide/templates.html#id10", - "adminguide/templates.html#sharing-templates-with-other-accounts-projects": "adminguide/templates.html#sharing-templates-with-other-accounts-projects", - "adminguide/templates.html#uploading-templates-and-isos-from-a-local-computer": "adminguide/templates.html#uploading-templates-and-isos-from-a-local-computer", - "adminguide/templates.html#uploading-templates-from-a-remote-http-server": "adminguide/templates.html#uploading-templates-from-a-remote-http-server", - "adminguide/templates.html#working-with-isos": "adminguide/templates.html#working-with-isos", - "adminguide/virtual_machines.html": "adminguide/virtual_machines.html", - "adminguide/virtual_machines.html#affinity-groups": "adminguide/virtual_machines.html#affinity-groups", - "adminguide/virtual_machines.html#backup-offerings": "adminguide/virtual_machines.html#backup-offerings", - "adminguide/virtual_machines.html#change-affinity-group-for-an-existing-vm": "adminguide/virtual_machines.html#change-affinity-group-for-an-existing-vm", - "adminguide/virtual_machines.html#changing-the-vm-name-os-or-group": "adminguide/virtual_machines.html#changing-the-vm-name-os-or-group", - "adminguide/virtual_machines.html#creating-a-new-affinity-group": "adminguide/virtual_machines.html#creating-a-new-affinity-group", - "adminguide/virtual_machines.html#creating-a-new-instance-from-backup": "adminguide/virtual_machines.html#creating-a-new-instance-from-backup", - "adminguide/virtual_machines.html#creating-the-ssh-keypair": "adminguide/virtual_machines.html#creating-the-ssh-keypair", - "adminguide/virtual_machines.html#creating-vm-backups": "adminguide/virtual_machines.html#creating-vm-backups", - "adminguide/virtual_machines.html#creating-vms": "adminguide/virtual_machines.html#creating-vms", - "adminguide/virtual_machines.html#delete-an-affinity-group": "adminguide/virtual_machines.html#delete-an-affinity-group", - "adminguide/virtual_machines.html#deleting-vms": "adminguide/virtual_machines.html#deleting-vms", - "adminguide/virtual_machines.html#how-to-dynamically-scale-cpu-and-ram": "adminguide/virtual_machines.html#how-to-dynamically-scale-cpu-and-ram", - "adminguide/virtual_machines.html#importing-and-unmanaging-virtual-machine": "adminguide/virtual_machines.html#importing-and-unmanaging-virtual-machine", - "adminguide/virtual_machines.html#importing-and-unmanaging-volume": "adminguide/virtual_machines.html#importing-and-unmanaging-volume", - "adminguide/virtual_machines.html#importing-backup-offerings": "adminguide/virtual_machines.html#importing-backup-offerings", - "adminguide/virtual_machines.html#moving-vms-between-hosts-manual-live-migration": "adminguide/virtual_machines.html#moving-vms-between-hosts-manual-live-migration", - "adminguide/virtual_machines.html#resetting-ssh-keys": "adminguide/virtual_machines.html#resetting-ssh-keys", - "adminguide/virtual_machines.html#resetting-userdata": "adminguide/virtual_machines.html#resetting-userdata", - "adminguide/virtual_machines.html#restoring-instance-backups": "adminguide/virtual_machines.html#restoring-instance-backups", - "adminguide/virtual_machines.html#restoring-vm-backups": "adminguide/virtual_machines.html#restoring-vm-backups", - "adminguide/virtual_machines.html#stopping-and-starting-vms": "adminguide/virtual_machines.html#stopping-and-starting-vms", - "adminguide/virtual_machines.html#user-data-and-meta-data": "adminguide/virtual_machines.html#user-data-and-meta-data", - "adminguide/virtual_machines.html#using-ssh-keys-for-authentication": "adminguide/virtual_machines.html#using-ssh-keys-for-authentication", - "adminguide/virtual_machines.html#virtual-machine-snapshots": "adminguide/virtual_machines.html#virtual-machine-snapshots", - "adminguide/webhooks.html": "adminguide/webhooks.html", - "conceptsandterminology/concepts.html#about-clusters": "conceptsandterminology/concepts.html#about-clusters", - "conceptsandterminology/concepts.html#about-hosts": "conceptsandterminology/concepts.html#about-hosts", - "conceptsandterminology/concepts.html#about-pods": "conceptsandterminology/concepts.html#about-pods", - "conceptsandterminology/concepts.html#about-zones": "conceptsandterminology/concepts.html#about-zones", - "conceptsandterminology/concepts.html#management-server-overview": "conceptsandterminology/concepts.html#management-server-overview", - "conceptsandterminology/network_setup.html#vlan-allocation-example": "conceptsandterminology/network_setup.html#vlan-allocation-example", - "installguide/configuration.html#adding-a-cluster": "installguide/configuration.html#adding-a-cluster", - "installguide/configuration.html#adding-a-host": "installguide/configuration.html#adding-a-host", - "installguide/configuration.html#adding-a-pod": "installguide/configuration.html#adding-a-pod", - "installguide/configuration.html#adding-a-zone": "installguide/configuration.html#adding-a-zone", - "installguide/configuration.html#add-object-storage": "installguide/configuration.html#add-object-storage", - "installguide/configuration.html#add-primary-storage": "installguide/configuration.html#add-primary-storage", - "installguide/configuration.html#add-secondary-storage": "installguide/configuration.html#add-secondary-storage", - "installguide/configuration.html#create-bucket": "installguide/configuration.html#create-bucket", - "plugins/cloudian-connector.html": "plugins/cloudian-connector.html", - "plugins/cloudstack-kubernetes-service.html": "plugins/cloudstack-kubernetes-service.html", - "plugins/cloudstack-kubernetes-service.html#creating-a-new-kubernetes-cluster": "plugins/cloudstack-kubernetes-service.html#creating-a-new-kubernetes-cluster", - "plugins/cloudstack-kubernetes-service.html#deleting-kubernetes-cluster": "plugins/cloudstack-kubernetes-service.html#deleting-kubernetes-cluster", - "plugins/cloudstack-kubernetes-service.html#kubernetes-supported-versions": "plugins/cloudstack-kubernetes-service.html#kubernetes-supported-versions", - "plugins/cloudstack-kubernetes-service.html#scaling-kubernetes-cluster": "plugins/cloudstack-kubernetes-service.html#scaling-kubernetes-cluster", - "plugins/cloudstack-kubernetes-service.html#starting-a-stopped-kubernetes-cluster": "plugins/cloudstack-kubernetes-service.html#starting-a-stopped-kubernetes-cluster", - "plugins/cloudstack-kubernetes-service.html#stopping-kubernetes-cluster": "plugins/cloudstack-kubernetes-service.html#stopping-kubernetes-cluster", - "plugins/cloudstack-kubernetes-service.html#upgrading-kubernetes-cluster": "plugins/cloudstack-kubernetes-service.html#upgrading-kubernetes-cluster", - "plugins/nuage-plugin.html?#optional-create-and-enable-vpc-offering": "plugins/nuage-plugin.html?#optional-create-and-enable-vpc-offering", - "plugins/nuage-plugin.html?#vpc-offerings": "plugins/nuage-plugin.html?#vpc-offerings", - "plugins/quota.html": "plugins/quota.html", - "plugins/quota.html#quota-credits": "plugins/quota.html#quota-credits", - "plugins/quota.html#quota-tariff": "plugins/quota.html#quota-tariff" - }, + "docHelpMappings": {}, "notifyLatestCSVersion": true, "showSearchFilters": true, "announcementBanner": { diff --git a/ui/public/locales/en.json b/ui/public/locales/en.json index 23e7f41de927..ebc7e8736234 100644 --- a/ui/public/locales/en.json +++ b/ui/public/locales/en.json @@ -500,6 +500,7 @@ "label.backup.restore": "Restore Instance backup", "label.backup.schedule.create.failed": "Failed to create Backup Schedule", "label.backuplimit": "Backup Limits", +"label.backuppaths": "Backup secondary storage paths", "label.backup.schedules": "Backup Schedules", "label.backup.storage": "Backup Storage", "label.backupstoragelimit": "Backup Storage Limits (GiB)", diff --git a/ui/public/locales/pt_BR.json b/ui/public/locales/pt_BR.json index 154c046099db..34ef615b6afa 100644 --- a/ui/public/locales/pt_BR.json +++ b/ui/public/locales/pt_BR.json @@ -444,6 +444,7 @@ "label.backingup": "Salvando", "label.backup.attach.restore": "Restaurar e anexar volume de backup", "label.backuplimit": "Limite de backups", +"label.backuppaths": "Caminho dos backups no armazenamento secund\u00e1rio", "label.backup.storage": "Armazenamento de backup", "label.backupstoragelimit": "Limite de armazenamento de backup (GiB)", "label.backup.configure.schedule": "Configurar Agendamento de Backup", diff --git a/ui/src/components/view/DetailsTab.vue b/ui/src/components/view/DetailsTab.vue index 9f94bf3a27d4..b6ca1442ec98 100644 --- a/ui/src/components/view/DetailsTab.vue +++ b/ui/src/components/view/DetailsTab.vue @@ -68,6 +68,11 @@ {{ volume.type }} - {{ volume.path }} ({{ parseFloat(volume.size / (1024.0 * 1024.0 * 1024.0)).toFixed(1) }} GiB) +
+
+ {{ backupVolumeInfo.volumename }} - {{ backupVolumeInfo.backuppath }} +
+
{{ dataResource.rootdisksize }} GB diff --git a/ui/src/config/section/storage.js b/ui/src/config/section/storage.js index 75bdfd4d5fa6..5b8625a2c6ce 100644 --- a/ui/src/config/section/storage.js +++ b/ui/src/config/section/storage.js @@ -494,7 +494,7 @@ export default { permission: ['listBackups'], params: { listvmdetails: 'true' }, columns: ['name', 'status', 'compressionstatus', 'validationstatus', 'size', 'virtualsize', 'virtualmachinename', 'backupofferingname', 'intervaltype', 'type', 'created', 'account', 'domain', 'zone'], - details: ['name', 'description', 'virtualmachinename', 'id', 'intervaltype', 'type', 'externalid', 'size', 'virtualsize', 'volumes', 'backupofferingname', 'zone', 'account', 'domain', 'created'], + details: ['name', 'description', 'virtualmachinename', 'id', 'intervaltype', 'type', 'externalid', 'size', 'virtualsize', 'volumes', 'backupofferingname', 'backuppaths', 'zone', 'account', 'domain', 'created'], searchFilters: ['name', 'zoneid', 'domainid', 'account', 'backupofferingid', 'status', 'type', 'virtualmachineid'], tabs: [ {