diff --git a/api/src/main/java/com/cloud/vm/UserVmService.java b/api/src/main/java/com/cloud/vm/UserVmService.java index 6830d42de7b..258e87002da 100644 --- a/api/src/main/java/com/cloud/vm/UserVmService.java +++ b/api/src/main/java/com/cloud/vm/UserVmService.java @@ -434,6 +434,8 @@ public interface UserVmService { UserVm getUserVm(long vmId); + VirtualMachine getVm(long vmId); + /** * Migrate the given VM to the destination host provided. The API returns the migrated VM if migration succeeds. * Only Root 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 21088592d8e..7a52ade4dd8 100644 --- a/api/src/main/java/org/apache/cloudstack/api/ApiConstants.java +++ b/api/src/main/java/org/apache/cloudstack/api/ApiConstants.java @@ -455,6 +455,7 @@ public class ApiConstants { public static final String VM_AVAILABLE = "vmavailable"; public static final String VM_LIMIT = "vmlimit"; public static final String VM_TOTAL = "vmtotal"; + public static final String VM_TYPE = "vmtype"; public static final String VNET = "vnet"; public static final String IS_VOLATILE = "isvolatile"; public static final String VOLUME_ID = "volumeid"; @@ -619,6 +620,7 @@ public class ApiConstants { public static final String TRAFFIC_TYPE_IMPLEMENTOR = "traffictypeimplementor"; public static final String KEYWORD = "keyword"; public static final String LIST_ALL = "listall"; + public static final String LIST_SYSTEM_VMS = "listsystemvms"; public static final String IP_RANGES = "ipranges"; public static final String IPV6_ROUTING = "ip6routing"; public static final String IPV6_ROUTES = "ip6routes"; diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/vm/MigrateVirtualMachineWithVolumeCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/vm/MigrateVirtualMachineWithVolumeCmd.java index db513ff4dc0..8778d8ccbcd 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/vm/MigrateVirtualMachineWithVolumeCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/vm/MigrateVirtualMachineWithVolumeCmd.java @@ -30,6 +30,7 @@ import org.apache.cloudstack.api.Parameter; import org.apache.cloudstack.api.ResponseObject.ResponseView; import org.apache.cloudstack.api.ServerApiException; import org.apache.cloudstack.api.response.HostResponse; +import org.apache.cloudstack.api.response.SystemVmResponse; import org.apache.cloudstack.api.response.UserVmResponse; import org.apache.commons.collections.MapUtils; import org.apache.log4j.Logger; @@ -152,20 +153,17 @@ public class MigrateVirtualMachineWithVolumeCmd extends BaseAsyncCmd { @Override public void execute() { if (hostId == null && MapUtils.isEmpty(migrateVolumeTo)) { - throw new InvalidParameterValueException(String.format("Either %s or %s must be passed for migrating the VM", ApiConstants.HOST_ID, ApiConstants.MIGRATE_TO)); + throw new InvalidParameterValueException(String.format("Either %s or %s must be passed for migrating the VM.", ApiConstants.HOST_ID, ApiConstants.MIGRATE_TO)); } - UserVm userVm = _userVmService.getUserVm(getVirtualMachineId()); - if (userVm == null) { - throw new InvalidParameterValueException("Unable to find the VM by id=" + getVirtualMachineId()); + VirtualMachine virtualMachine = _userVmService.getVm(getVirtualMachineId()); + if (!VirtualMachine.State.Running.equals(virtualMachine.getState()) && hostId != null) { + throw new InvalidParameterValueException(String.format("%s is not in the Running state to migrate it to the new host.", virtualMachine)); } - if (!VirtualMachine.State.Running.equals(userVm.getState()) && hostId != null) { - throw new InvalidParameterValueException(String.format("VM ID: %s is not in Running state to migrate it to new host", userVm.getUuid())); - } - - if (!VirtualMachine.State.Stopped.equals(userVm.getState()) && hostId == null) { - throw new InvalidParameterValueException(String.format("VM ID: %s is not in Stopped state to migrate, use %s parameter to migrate it to a new host", userVm.getUuid(), ApiConstants.HOST_ID)); + if (!VirtualMachine.State.Stopped.equals(virtualMachine.getState()) && hostId == null) { + throw new InvalidParameterValueException(String.format("%s is not in the Stopped state to migrate, use the %s parameter to migrate it to a new host.", + virtualMachine, ApiConstants.HOST_ID)); } try { @@ -174,16 +172,15 @@ public class MigrateVirtualMachineWithVolumeCmd extends BaseAsyncCmd { Host destinationHost = _resourceService.getHost(getHostId()); // OfflineVmwareMigration: destination host would have to not be a required parameter for stopped VMs if (destinationHost == null) { - throw new InvalidParameterValueException("Unable to find the host to migrate the VM, host id =" + getHostId()); + s_logger.error(String.format("Unable to find the host with ID [%s].", getHostId())); + throw new InvalidParameterValueException("Unable to find the specified host to migrate the VM."); } migratedVm = _userVmService.migrateVirtualMachineWithVolume(getVirtualMachineId(), destinationHost, getVolumeToPool()); } else if (MapUtils.isNotEmpty(migrateVolumeTo)) { migratedVm = _userVmService.vmStorageMigration(getVirtualMachineId(), getVolumeToPool()); } if (migratedVm != null) { - UserVmResponse response = _responseGenerator.createUserVmResponse(ResponseView.Full, "virtualmachine", (UserVm)migratedVm).get(0); - response.setResponseName(getCommandName()); - setResponseObject(response); + setResponseBasedOnVmType(virtualMachine, migratedVm); } else { throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to migrate vm"); } @@ -195,4 +192,16 @@ public class MigrateVirtualMachineWithVolumeCmd extends BaseAsyncCmd { throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, e.getMessage()); } } + + private void setResponseBasedOnVmType(VirtualMachine virtualMachine, VirtualMachine migratedVm) { + if (VirtualMachine.Type.User.equals(virtualMachine.getType())) { + UserVmResponse userVmResponse = _responseGenerator.createUserVmResponse(ResponseView.Full, "virtualmachine", (UserVm) migratedVm).get(0); + userVmResponse.setResponseName(getCommandName()); + setResponseObject(userVmResponse); + return; + } + SystemVmResponse systemVmResponse = _responseGenerator.createSystemVmResponse(migratedVm); + systemVmResponse.setResponseName(getCommandName()); + setResponseObject(systemVmResponse); + } } diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/volume/ListVolumesCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/volume/ListVolumesCmd.java index b1d0d3d5b4f..fdac3ff2937 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/volume/ListVolumesCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/volume/ListVolumesCmd.java @@ -88,6 +88,10 @@ public class ListVolumesCmd extends BaseListTaggedResourcesCmd implements UserCm RoleType.Admin}) private Boolean display; + @Parameter(name = ApiConstants.LIST_SYSTEM_VMS, type = CommandType.BOOLEAN, description = "list system VMs; only ROOT admin is eligible to pass this parameter", since = "4.18", + authorized = { RoleType.Admin }) + private Boolean listSystemVms; + @Parameter(name = ApiConstants.STATE, type = CommandType.STRING, description = "state of the volume. Possible values are: Ready, Allocated, Destroy, Expunging, Expunged.") private String state; @@ -135,6 +139,10 @@ public class ListVolumesCmd extends BaseListTaggedResourcesCmd implements UserCm return storageId; } + public Boolean getListSystemVms() { + return listSystemVms; + } + @Override public Boolean getDisplay() { if (display != null) { diff --git a/api/src/main/java/org/apache/cloudstack/api/response/VolumeResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/VolumeResponse.java index a0f59ac3dbf..00a1eabc40b 100644 --- a/api/src/main/java/org/apache/cloudstack/api/response/VolumeResponse.java +++ b/api/src/main/java/org/apache/cloudstack/api/response/VolumeResponse.java @@ -96,6 +96,10 @@ public class VolumeResponse extends BaseResponseWithTagInformation implements Co @Param(description = "state of the virtual machine") private String virtualMachineState; + @SerializedName(ApiConstants.VM_TYPE) + @Param(description = "type of the virtual machine") + private String vmType; + @SerializedName(ApiConstants.PROVISIONINGTYPE) @Param(description = "provisioning type used to create volumes.") private String provisioningType; @@ -333,6 +337,10 @@ public class VolumeResponse extends BaseResponseWithTagInformation implements Co this.volumeType = volumeType; } + public void setVmType(String vmType) { + this.vmType = vmType; + } + public void setDeviceId(Long deviceId) { this.deviceId = deviceId; } @@ -666,6 +674,10 @@ public class VolumeResponse extends BaseResponseWithTagInformation implements Co return state; } + public String getVmType() { + return vmType; + } + public String getAccountName() { return accountName; } diff --git a/api/src/test/java/org/apache/cloudstack/api/command/admin/vm/MigrateVirtualMachineWithVolumeCmdTest.java b/api/src/test/java/org/apache/cloudstack/api/command/admin/vm/MigrateVirtualMachineWithVolumeCmdTest.java new file mode 100644 index 00000000000..7a98626ca45 --- /dev/null +++ b/api/src/test/java/org/apache/cloudstack/api/command/admin/vm/MigrateVirtualMachineWithVolumeCmdTest.java @@ -0,0 +1,226 @@ +// 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.command.admin.vm; + +import com.cloud.exception.InvalidParameterValueException; +import com.cloud.exception.ManagementServerException; +import com.cloud.exception.ResourceUnavailableException; +import com.cloud.exception.VirtualMachineMigrationException; +import com.cloud.host.Host; +import com.cloud.resource.ResourceService; +import com.cloud.uservm.UserVm; +import com.cloud.utils.db.UUIDManager; +import com.cloud.vm.UserVmService; +import com.cloud.vm.VirtualMachine; +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.ResponseGenerator; +import org.apache.cloudstack.api.ResponseObject; +import org.apache.cloudstack.api.ServerApiException; +import org.apache.cloudstack.api.response.SystemVmResponse; +import org.apache.cloudstack.api.response.UserVmResponse; +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.powermock.modules.junit4.PowerMockRunner; +import org.springframework.test.util.ReflectionTestUtils; + +import java.util.List; +import java.util.Map; + +@RunWith(PowerMockRunner.class) +public class MigrateVirtualMachineWithVolumeCmdTest { + @Mock + UserVmService userVmServiceMock; + + @Mock + UUIDManager uuidManagerMock; + + @Mock + ResourceService resourceServiceMock; + + @Mock + ResponseGenerator responseGeneratorMock; + + @Mock + VirtualMachine virtualMachineMock; + + @Mock + Host hostMock; + + @Spy + @InjectMocks + MigrateVirtualMachineWithVolumeCmd cmdSpy = new MigrateVirtualMachineWithVolumeCmd(); + + private Long hostId = 1L; + private Long virtualMachineUuid = 1L; + private String virtualMachineName = "VM-name"; + private Map migrateVolumeTo = Map.of("key","value"); + private SystemVmResponse systemVmResponse = new SystemVmResponse(); + private UserVmResponse userVmResponse = new UserVmResponse(); + + @Before + public void setup() { + Mockito.when(cmdSpy.getVirtualMachineId()).thenReturn(virtualMachineUuid); + Mockito.when(cmdSpy.getHostId()).thenReturn(hostId); + Mockito.when(cmdSpy.getVolumeToPool()).thenReturn(migrateVolumeTo); + } + + @Test + public void executeTestHostIdIsNullAndMigrateVolumeToIsNullThrowsInvalidParameterValueException(){ + ReflectionTestUtils.setField(cmdSpy, "hostId", null); + ReflectionTestUtils.setField(cmdSpy, "migrateVolumeTo", null); + + try { + cmdSpy.execute(); + } catch (Exception e) { + Assert.assertEquals(InvalidParameterValueException.class, e.getClass()); + String expected = String.format("Either %s or %s must be passed for migrating the VM.", ApiConstants.HOST_ID, ApiConstants.MIGRATE_TO); + Assert.assertEquals(expected , e.getMessage()); + } + } + + @Test + public void executeTestVMIsStoppedAndHostIdIsNotNullThrowsInvalidParameterValueException(){ + ReflectionTestUtils.setField(cmdSpy, "hostId", hostId); + ReflectionTestUtils.setField(cmdSpy, "migrateVolumeTo", migrateVolumeTo); + + Mockito.when(userVmServiceMock.getVm(Mockito.anyLong())).thenReturn(virtualMachineMock); + Mockito.when(virtualMachineMock.getState()).thenReturn(VirtualMachine.State.Stopped); + Mockito.when(virtualMachineMock.toString()).thenReturn(String.format("VM [uuid: %s, name: %s]", virtualMachineUuid, virtualMachineName)); + + try { + cmdSpy.execute(); + } catch (Exception e) { + Assert.assertEquals(InvalidParameterValueException.class, e.getClass()); + String expected = String.format("%s is not in the Running state to migrate it to the new host.", virtualMachineMock); + Assert.assertEquals(expected , e.getMessage()); + } + } + + @Test + public void executeTestVMIsRunningAndHostIdIsNullThrowsInvalidParameterValueException(){ + ReflectionTestUtils.setField(cmdSpy, "hostId", null); + ReflectionTestUtils.setField(cmdSpy, "migrateVolumeTo", migrateVolumeTo); + + Mockito.when(userVmServiceMock.getVm(Mockito.anyLong())).thenReturn(virtualMachineMock); + Mockito.when(virtualMachineMock.getState()).thenReturn(VirtualMachine.State.Running); + Mockito.when(virtualMachineMock.toString()).thenReturn(String.format("VM [uuid: %s, name: %s]", virtualMachineUuid, virtualMachineName)); + + try { + cmdSpy.execute(); + } catch (Exception e) { + Assert.assertEquals(InvalidParameterValueException.class, e.getClass()); + String expected = String.format("%s is not in the Stopped state to migrate, use the %s parameter to migrate it to a new host.", virtualMachineMock, + ApiConstants.HOST_ID); + Assert.assertEquals(expected , e.getMessage()); + } + } + + @Test + public void executeTestHostIdIsNullThrowsInvalidParameterValueException(){ + ReflectionTestUtils.setField(cmdSpy, "hostId", hostId); + ReflectionTestUtils.setField(cmdSpy, "migrateVolumeTo", migrateVolumeTo); + + Mockito.when(userVmServiceMock.getVm(Mockito.anyLong())).thenReturn(virtualMachineMock); + Mockito.when(virtualMachineMock.getState()).thenReturn(VirtualMachine.State.Running); + Mockito.when(resourceServiceMock.getHost(Mockito.anyLong())).thenReturn(null); + Mockito.when(uuidManagerMock.getUuid(Host.class, virtualMachineUuid)).thenReturn(virtualMachineUuid.toString()); + + try { + cmdSpy.execute(); + } catch (Exception e) { + Assert.assertEquals(InvalidParameterValueException.class, e.getClass()); + String expected = "Unable to find the specified host to migrate the VM."; + Assert.assertEquals(expected , e.getMessage()); + } + } + + @Test + public void executeTestHostIsNotNullMigratedVMIsNullThrowsServerApiException() throws ManagementServerException, ResourceUnavailableException, VirtualMachineMigrationException { + ReflectionTestUtils.setField(cmdSpy, "hostId", hostId); + ReflectionTestUtils.setField(cmdSpy, "migrateVolumeTo", migrateVolumeTo); + + Mockito.when(userVmServiceMock.getVm(Mockito.anyLong())).thenReturn(virtualMachineMock); + Mockito.when(virtualMachineMock.getState()).thenReturn(VirtualMachine.State.Running); + Mockito.when(resourceServiceMock.getHost(Mockito.anyLong())).thenReturn(hostMock); + Mockito.when(userVmServiceMock.migrateVirtualMachineWithVolume(virtualMachineUuid, hostMock, migrateVolumeTo)).thenReturn(null); + + try { + cmdSpy.execute(); + } catch (Exception e) { + Assert.assertEquals(ServerApiException.class, e.getClass()); + String expected = "Failed to migrate vm"; + Assert.assertEquals(expected , e.getMessage()); + } + } + + @Test + public void executeTestHostIsNullMigratedVMIsNullThrowsServerApiException() { + ReflectionTestUtils.setField(cmdSpy, "hostId", null); + ReflectionTestUtils.setField(cmdSpy, "migrateVolumeTo", migrateVolumeTo); + + Mockito.when(userVmServiceMock.getVm(Mockito.anyLong())).thenReturn(virtualMachineMock); + Mockito.when(virtualMachineMock.getState()).thenReturn(VirtualMachine.State.Stopped); + Mockito.when(userVmServiceMock.vmStorageMigration(virtualMachineUuid, migrateVolumeTo)).thenReturn(null); + + try { + cmdSpy.execute(); + } catch (Exception e) { + Assert.assertEquals(ServerApiException.class, e.getClass()); + String expected = "Failed to migrate vm"; + Assert.assertEquals(expected , e.getMessage()); + } + } + + @Test + public void executeTestSystemVMMigratedWithSuccess() { + ReflectionTestUtils.setField(cmdSpy, "hostId", null); + ReflectionTestUtils.setField(cmdSpy, "migrateVolumeTo", migrateVolumeTo); + + Mockito.when(userVmServiceMock.getVm(Mockito.anyLong())).thenReturn(virtualMachineMock); + Mockito.when(virtualMachineMock.getState()).thenReturn(VirtualMachine.State.Stopped); + Mockito.when(userVmServiceMock.vmStorageMigration(virtualMachineUuid, migrateVolumeTo)).thenReturn(virtualMachineMock); + Mockito.when(virtualMachineMock.getType()).thenReturn(VirtualMachine.Type.ConsoleProxy); + Mockito.when(responseGeneratorMock.createSystemVmResponse(virtualMachineMock)).thenReturn(systemVmResponse); + + cmdSpy.execute(); + + Mockito.verify(responseGeneratorMock, Mockito.times(1)).createSystemVmResponse(virtualMachineMock); + } + + @Test + public void executeTestUserVMMigratedWithSuccess() { + UserVm userVmMock = Mockito.mock(UserVm.class); + ReflectionTestUtils.setField(cmdSpy, "hostId", null); + ReflectionTestUtils.setField(cmdSpy, "migrateVolumeTo", migrateVolumeTo); + + Mockito.when(userVmServiceMock.getVm(Mockito.anyLong())).thenReturn(userVmMock); + Mockito.when(userVmMock.getState()).thenReturn(VirtualMachine.State.Stopped); + Mockito.when(userVmServiceMock.vmStorageMigration(virtualMachineUuid, migrateVolumeTo)).thenReturn(userVmMock); + Mockito.when(userVmMock.getType()).thenReturn(VirtualMachine.Type.User); + Mockito.when(responseGeneratorMock.createUserVmResponse(ResponseObject.ResponseView.Full, "virtualmachine", userVmMock)).thenReturn(List.of(userVmResponse)); + + cmdSpy.execute(); + + Mockito.verify(responseGeneratorMock, Mockito.times(1)).createUserVmResponse(ResponseObject.ResponseView.Full, "virtualmachine", userVmMock); + } +} diff --git a/server/src/main/java/com/cloud/api/query/QueryManagerImpl.java b/server/src/main/java/com/cloud/api/query/QueryManagerImpl.java index d2eeb01a776..d115a0b774b 100644 --- a/server/src/main/java/com/cloud/api/query/QueryManagerImpl.java +++ b/server/src/main/java/com/cloud/api/query/QueryManagerImpl.java @@ -2082,6 +2082,7 @@ public class QueryManagerImpl extends MutualExclusiveIdsManagerBase implements Q Long diskOffId = cmd.getDiskOfferingId(); Boolean display = cmd.getDisplay(); String state = cmd.getState(); + boolean shouldListSystemVms = shouldListSystemVms(cmd, caller.getId()); Long zoneId = cmd.getZoneId(); Long podId = cmd.getPodId(); @@ -2126,14 +2127,16 @@ public class QueryManagerImpl extends MutualExclusiveIdsManagerBase implements Q sb.and("display", sb.entity().isDisplayVolume(), SearchCriteria.Op.EQ); sb.and("state", sb.entity().getState(), SearchCriteria.Op.EQ); sb.and("stateNEQ", sb.entity().getState(), SearchCriteria.Op.NEQ); - sb.and().op("systemUse", sb.entity().isSystemUse(), SearchCriteria.Op.NEQ); - sb.or("nulltype", sb.entity().isSystemUse(), SearchCriteria.Op.NULL); - sb.cp(); - // display UserVM volumes only - sb.and().op("type", sb.entity().getVmType(), SearchCriteria.Op.NIN); - sb.or("nulltype", sb.entity().getVmType(), SearchCriteria.Op.NULL); - sb.cp(); + if (!shouldListSystemVms) { + sb.and().op("systemUse", sb.entity().isSystemUse(), SearchCriteria.Op.NEQ); + sb.or("nulltype", sb.entity().isSystemUse(), SearchCriteria.Op.NULL); + sb.cp(); + + sb.and().op("type", sb.entity().getVmType(), SearchCriteria.Op.NIN); + sb.or("nulltype", sb.entity().getVmType(), SearchCriteria.Op.NULL); + sb.cp(); + } // now set the SC criteria... SearchCriteria sc = sb.create(); @@ -2158,7 +2161,10 @@ public class QueryManagerImpl extends MutualExclusiveIdsManagerBase implements Q setIdsListToSearchCriteria(sc, ids); - sc.setParameters("systemUse", 1); + if (!shouldListSystemVms) { + sc.setParameters("systemUse", 1); + sc.setParameters("type", VirtualMachine.Type.ConsoleProxy, VirtualMachine.Type.SecondaryStorageVm, VirtualMachine.Type.DomainRouter); + } if (tags != null && !tags.isEmpty()) { SearchCriteria tagSc = _volumeJoinDao.createSearchCriteria(); @@ -2206,8 +2212,6 @@ public class QueryManagerImpl extends MutualExclusiveIdsManagerBase implements Q if (clusterId != null) { sc.setParameters("clusterId", clusterId); } - // Don't return DomR and ConsoleProxy volumes - sc.setParameters("type", VirtualMachine.Type.ConsoleProxy, VirtualMachine.Type.SecondaryStorageVm, VirtualMachine.Type.DomainRouter); if (state != null) { sc.setParameters("state", state); @@ -2232,6 +2236,10 @@ public class QueryManagerImpl extends MutualExclusiveIdsManagerBase implements Q return new Pair, Integer>(vrs, count); } + private boolean shouldListSystemVms(ListVolumesCmd cmd, Long callerId) { + return Boolean.TRUE.equals(cmd.getListSystemVms()) && _accountMgr.isRootAdmin(callerId); + } + @Override public ListResponse searchForDomains(ListDomainsCmd cmd) { Pair, Integer> result = searchForDomainsInternal(cmd); diff --git a/server/src/main/java/com/cloud/api/query/dao/VolumeJoinDaoImpl.java b/server/src/main/java/com/cloud/api/query/dao/VolumeJoinDaoImpl.java index 3aa4aff231e..46a1b20384e 100644 --- a/server/src/main/java/com/cloud/api/query/dao/VolumeJoinDaoImpl.java +++ b/server/src/main/java/com/cloud/api/query/dao/VolumeJoinDaoImpl.java @@ -98,6 +98,10 @@ public class VolumeJoinDaoImpl extends GenericDaoBaseWithTagInformation{{ $t('label.vmname') }}
- {{ resource.vmname || resource.vm || resource.virtualmachinename || resource.virtualmachineid }} + {{ resource.vmname || resource.vm || resource.virtualmachinename || resource.virtualmachineid }}
@@ -705,6 +705,7 @@ diff --git a/ui/src/config/section/infra/routers.js b/ui/src/config/section/infra/routers.js index 5c747082eaf..9e69d6bccb2 100644 --- a/ui/src/config/section/infra/routers.js +++ b/ui/src/config/section/infra/routers.js @@ -43,6 +43,9 @@ export default { name: 'router.health.checks', show: (record, route, user) => { return ['Running'].includes(record.state) && ['Admin'].includes(user.roletype) }, component: shallowRef(defineAsyncComponent(() => import('@views/infra/routers/RouterHealthCheck.vue'))) + }, { + name: 'volume', + component: shallowRef(defineAsyncComponent(() => import('@/components/view/VolumesTab.vue'))) }, { name: 'events', resourceType: 'DomainRouter', diff --git a/ui/src/config/section/infra/systemVms.js b/ui/src/config/section/infra/systemVms.js index 785f51f8191..70f52e3d899 100644 --- a/ui/src/config/section/infra/systemVms.js +++ b/ui/src/config/section/infra/systemVms.js @@ -32,6 +32,10 @@ export default { name: 'details', component: shallowRef(defineAsyncComponent(() => import('@/components/view/DetailsTab.vue'))) }, + { + name: 'volume', + component: shallowRef(defineAsyncComponent(() => import('@/components/view/VolumesTab.vue'))) + }, { name: 'events', resourceType: 'SystemVm', diff --git a/ui/src/utils/plugins.js b/ui/src/utils/plugins.js index 0941c426741..51ccacd9bff 100644 --- a/ui/src/utils/plugins.js +++ b/ui/src/utils/plugins.js @@ -485,3 +485,20 @@ export const genericUtilPlugin = { } } } + +export function createPathBasedOnVmType (vmtype, virtualmachineid) { + let path = '' + switch (vmtype) { + case 'ConsoleProxy': + case 'SecondaryStorageVm': + path = '/systemvm/' + break + case 'DomainRouter': + path = '/router/' + break + default: + path = '/vm/' + } + + return path + virtualmachineid +} diff --git a/ui/src/views/AutogenView.vue b/ui/src/views/AutogenView.vue index 85c90e6c3a6..ebcfa7c1a7c 100644 --- a/ui/src/views/AutogenView.vue +++ b/ui/src/views/AutogenView.vue @@ -847,6 +847,10 @@ export default { delete params.showunique } + if (['Admin'].includes(this.$store.getters.userInfo.roletype) && ['listVolumesMetrics', 'listVolumes'].includes(this.apiName)) { + params.listsystemvms = true + } + this.loading = true if (this.$route.params && this.$route.params.id) { params.id = this.$route.params.id diff --git a/ui/src/views/compute/InstanceTab.vue b/ui/src/views/compute/InstanceTab.vue index ec6c7c32299..c1cc93e1f74 100644 --- a/ui/src/views/compute/InstanceTab.vue +++ b/ui/src/views/compute/InstanceTab.vue @@ -33,31 +33,8 @@ {{ vm.isoname }}
{{ vm.isoid }} - - - - - - + + { - this.volumes = json.listvolumesresponse.volume - if (this.volumes) { - this.volumes.sort((a, b) => { return a.deviceid - b.deviceid }) - } - this.dataResource.volumes = this.volumes - }) api('listAnnotations', { entityid: this.dataResource.id, entitytype: 'VM', annotationfilter: 'all' }).then(json => { if (json.listannotationsresponse && json.listannotationsresponse.annotation) { this.annotations = json.listannotationsresponse.annotation