Merge branch '4.20' into 4.22

This commit is contained in:
Daan Hoogland 2026-02-05 15:32:36 +01:00
commit cd6a8f61ca
11 changed files with 326 additions and 75 deletions

View File

@ -153,8 +153,8 @@ public abstract class BaseUpdateTemplateOrIsoCmd extends BaseCmd {
return (Map) (paramsCollection.toArray())[0];
}
public boolean isCleanupDetails(){
return cleanupDetails == null ? false : cleanupDetails.booleanValue();
public boolean isCleanupDetails() {
return cleanupDetails != null && cleanupDetails;
}
public CPU.CPUArch getCPUArch() {

View File

@ -460,7 +460,7 @@ public class VMTemplateDaoImpl extends GenericDaoBase<VMTemplateVO, Long> implem
if (detailsStr == null) {
return;
}
List<VMTemplateDetailVO> details = new ArrayList<VMTemplateDetailVO>();
List<VMTemplateDetailVO> details = new ArrayList<>();
for (String key : detailsStr.keySet()) {
VMTemplateDetailVO detail = new VMTemplateDetailVO(tmpl.getId(), key, detailsStr.get(key), true);
details.add(detail);
@ -482,7 +482,7 @@ public class VMTemplateDaoImpl extends GenericDaoBase<VMTemplateVO, Long> implem
}
if (tmplt.getDetails() != null) {
List<VMTemplateDetailVO> details = new ArrayList<VMTemplateDetailVO>();
List<VMTemplateDetailVO> details = new ArrayList<>();
for (String key : tmplt.getDetails().keySet()) {
details.add(new VMTemplateDetailVO(tmplt.getId(), key, tmplt.getDetails().get(key), true));
}

View File

@ -63,20 +63,20 @@
<!-- Plugins versions -->
<cs.antrun-plugin.version>1.8</cs.antrun-plugin.version>
<cs.builder-helper-plugin.version>3.0.0</cs.builder-helper-plugin.version>
<cs.checkstyle-plugin.version>3.1.0</cs.checkstyle-plugin.version>
<cs.checkstyle-plugin.version>3.6.0</cs.checkstyle-plugin.version>
<cs.jacoco-plugin.version>0.8.11</cs.jacoco-plugin.version>
<cs.compiler-plugin.version>3.8.1</cs.compiler-plugin.version>
<cs.dependency-plugin.version>3.1.1</cs.dependency-plugin.version>
<cs.dependency-plugin.version>3.9.0</cs.dependency-plugin.version>
<cs.failsafe-plugin.version>2.22.2</cs.failsafe-plugin.version>
<cs.spotbugs.version>3.1.12</cs.spotbugs.version>
<cs.spotbugs-maven-plugin.version>3.1.12.2</cs.spotbugs-maven-plugin.version>
<cs.jar-plugin.version>3.2.0</cs.jar-plugin.version>
<cs.pmd-plugin.version>3.12.0</cs.pmd-plugin.version>
<cs.pmd-plugin.version>3.28.0</cs.pmd-plugin.version>
<cs.project-info-plugin.version>3.0.0</cs.project-info-plugin.version>
<cs.owasp.dependency-checker-plugin.version>7.4.4</cs.owasp.dependency-checker-plugin.version>
<cs.release-plugin.version>2.5.3</cs.release-plugin.version>
<cs.resources-plugin.version>3.1.0</cs.resources-plugin.version>
<cs.site-plugin.version>3.8.2</cs.site-plugin.version>
<cs.site-plugin.version>3.21.0</cs.site-plugin.version>
<cs.surefire-plugin.version>2.22.2</cs.surefire-plugin.version>
<cs.clover-maven-plugin.version>4.4.1</cs.clover-maven-plugin.version>
<cs.exec-maven-plugin.version>3.2.0</cs.exec-maven-plugin.version>

View File

@ -2256,7 +2256,7 @@ public class TemplateManagerImpl extends ManagerBase implements TemplateManager,
templateTag == null &&
forCks == null &&
arch == null &&
(! cleanupDetails && details == null) //update details in every case except this one
(!cleanupDetails && details == null) //update details in every case except this one
);
if (!updateNeeded) {
return template;
@ -2360,8 +2360,7 @@ public class TemplateManagerImpl extends ManagerBase implements TemplateManager,
if (cleanupDetails) {
template.setDetails(null);
_tmpltDetailsDao.removeDetails(id);
}
else if (details != null && !details.isEmpty()) {
} else if (details != null && !details.isEmpty()) {
template.setDetails(details);
_tmpltDao.saveDetails(template);
}

View File

@ -23,6 +23,7 @@ import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.inject.Inject;
import javax.naming.ConfigurationException;
@ -182,6 +183,11 @@ public class VMSnapshotManagerImpl extends MutualExclusiveIdsManagerBase impleme
Long.class, "vm.job.check.interval", "3000",
"Interval in milliseconds to check if the job is complete", false);
private static final Set<String> VM_SNAPSHOT_CUSTOM_SERVICE_OFFERING_DETAILS = Set.of(
VmDetailConstants.CPU_NUMBER.toLowerCase(),
VmDetailConstants.CPU_SPEED.toLowerCase(),
VmDetailConstants.MEMORY.toLowerCase());
@Override
public boolean configure(String name, Map<String, Object> params) throws ConfigurationException {
_name = name;
@ -471,7 +477,8 @@ public class VMSnapshotManagerImpl extends MutualExclusiveIdsManagerBase impleme
}
/**
* Add entries on vm_snapshot_details if service offering is dynamic. This will allow setting details when revert to vm snapshot
* Add entries about cpu, cpu_speed and memory in vm_snapshot_details if service offering is dynamic.
* This will allow setting details when revert to vm snapshot.
* @param vmId vm id
* @param serviceOfferingId service offering id
* @param vmSnapshotId vm snapshot id
@ -482,7 +489,7 @@ public class VMSnapshotManagerImpl extends MutualExclusiveIdsManagerBase impleme
List<VMInstanceDetailVO> vmDetails = _vmInstanceDetailsDao.listDetails(vmId);
List<VMSnapshotDetailsVO> vmSnapshotDetails = new ArrayList<VMSnapshotDetailsVO>();
for (VMInstanceDetailVO detail : vmDetails) {
if(detail.getName().equalsIgnoreCase(VmDetailConstants.CPU_NUMBER) || detail.getName().equalsIgnoreCase(VmDetailConstants.CPU_SPEED) || detail.getName().equalsIgnoreCase(VmDetailConstants.MEMORY)) {
if (VM_SNAPSHOT_CUSTOM_SERVICE_OFFERING_DETAILS.contains(detail.getName().toLowerCase())) {
vmSnapshotDetails.add(new VMSnapshotDetailsVO(vmSnapshotId, detail.getName(), detail.getValue(), detail.isDisplay()));
}
}
@ -935,7 +942,7 @@ public class VMSnapshotManagerImpl extends MutualExclusiveIdsManagerBase impleme
Transaction.execute(new TransactionCallbackWithExceptionNoReturn<CloudRuntimeException>() {
@Override
public void doInTransactionWithoutResult(TransactionStatus status) throws CloudRuntimeException {
revertUserVmDetailsFromVmSnapshot(userVm, vmSnapshotVo);
revertCustomServiceOfferingDetailsFromVmSnapshot(userVm, vmSnapshotVo);
updateUserVmServiceOffering(userVm, vmSnapshotVo);
}
});
@ -947,19 +954,19 @@ public class VMSnapshotManagerImpl extends MutualExclusiveIdsManagerBase impleme
}
/**
* Update or add user vm details from vm snapshot for vms with custom service offerings
* Update or add user vm details (cpu, cpu_speed and memory) from vm snapshot for vms with custom service offerings
* @param userVm user vm
* @param vmSnapshotVo vm snapshot
*/
protected void revertUserVmDetailsFromVmSnapshot(UserVmVO userVm, VMSnapshotVO vmSnapshotVo) {
protected void revertCustomServiceOfferingDetailsFromVmSnapshot(UserVmVO userVm, VMSnapshotVO vmSnapshotVo) {
ServiceOfferingVO serviceOfferingVO = _serviceOfferingDao.findById(vmSnapshotVo.getServiceOfferingId());
if (serviceOfferingVO.isDynamic()) {
List<VMSnapshotDetailsVO> vmSnapshotDetails = _vmSnapshotDetailsDao.listDetails(vmSnapshotVo.getId());
List<VMInstanceDetailVO> userVmDetails = new ArrayList<VMInstanceDetailVO>();
for (VMSnapshotDetailsVO detail : vmSnapshotDetails) {
userVmDetails.add(new VMInstanceDetailVO(userVm.getId(), detail.getName(), detail.getValue(), detail.isDisplay()));
if (VM_SNAPSHOT_CUSTOM_SERVICE_OFFERING_DETAILS.contains(detail.getName().toLowerCase())) {
_vmInstanceDetailsDao.addDetail(userVm.getId(), detail.getName(), detail.getValue(), detail.isDisplay());
}
}
_vmInstanceDetailsDao.saveDetails(userVmDetails);
}
}

View File

@ -51,6 +51,7 @@ import com.cloud.vm.UserVmManager;
import com.cloud.vm.UserVmVO;
import com.cloud.vm.VirtualMachine.State;
import com.cloud.vm.VirtualMachineManager;
import com.cloud.vm.VmDetailConstants;
import com.cloud.vm.dao.UserVmDao;
import com.cloud.vm.dao.VMInstanceDetailsDao;
import com.cloud.vm.dao.VMInstanceDao;
@ -67,7 +68,6 @@ import org.junit.Before;
import org.junit.Test;
import org.mockito.ArgumentCaptor;
import org.mockito.Captor;
import org.mockito.ArgumentMatchers;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.mockito.Spy;
@ -79,13 +79,18 @@ import java.util.List;
import java.util.Map;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@ -225,13 +230,13 @@ public class VMSnapshotManagerTest {
when(_serviceOfferingDao.findById(SERVICE_OFFERING_ID)).thenReturn(serviceOffering);
for (ResourceDetail detail : Arrays.asList(userVmDetailCpuNumber, vmSnapshotDetailCpuNumber)) {
when(detail.getName()).thenReturn("cpuNumber");
when(detail.getName()).thenReturn(VmDetailConstants.CPU_NUMBER);
when(detail.getValue()).thenReturn("2");
when(detail.isDisplay()).thenReturn(true);
}
for (ResourceDetail detail : Arrays.asList(userVmDetailMemory, vmSnapshotDetailMemory)) {
when(detail.getName()).thenReturn("memory");
when(detail.getName()).thenReturn(VmDetailConstants.MEMORY);
when(detail.getValue()).thenReturn("2048");
when(detail.isDisplay()).thenReturn(true);
}
@ -348,12 +353,12 @@ public class VMSnapshotManagerTest {
@Test
public void testUpdateUserVmServiceOfferingDifferentServiceOffering() throws ConcurrentOperationException, ResourceUnavailableException, ManagementServerException, VirtualMachineMigrationException {
when(userVm.getServiceOfferingId()).thenReturn(SERVICE_OFFERING_DIFFERENT_ID);
when(_userVmManager.upgradeVirtualMachine(ArgumentMatchers.eq(TEST_VM_ID), ArgumentMatchers.eq(SERVICE_OFFERING_ID), mapDetailsCaptor.capture())).thenReturn(true);
when(_userVmManager.upgradeVirtualMachine(eq(TEST_VM_ID), eq(SERVICE_OFFERING_ID), mapDetailsCaptor.capture())).thenReturn(true);
_vmSnapshotMgr.updateUserVmServiceOffering(userVm, vmSnapshotVO);
verify(_vmSnapshotMgr).changeUserVmServiceOffering(userVm, vmSnapshotVO);
verify(_vmSnapshotMgr).getVmMapDetails(userVm);
verify(_vmSnapshotMgr).upgradeUserVmServiceOffering(ArgumentMatchers.eq(userVm), ArgumentMatchers.eq(SERVICE_OFFERING_ID), mapDetailsCaptor.capture());
verify(_vmSnapshotMgr).upgradeUserVmServiceOffering(eq(userVm), eq(SERVICE_OFFERING_ID), mapDetailsCaptor.capture());
}
@Test
@ -368,18 +373,18 @@ public class VMSnapshotManagerTest {
@Test
public void testChangeUserVmServiceOffering() throws ConcurrentOperationException, ResourceUnavailableException, ManagementServerException, VirtualMachineMigrationException {
when(_userVmManager.upgradeVirtualMachine(ArgumentMatchers.eq(TEST_VM_ID), ArgumentMatchers.eq(SERVICE_OFFERING_ID), mapDetailsCaptor.capture())).thenReturn(true);
when(_userVmManager.upgradeVirtualMachine(eq(TEST_VM_ID), eq(SERVICE_OFFERING_ID), mapDetailsCaptor.capture())).thenReturn(true);
_vmSnapshotMgr.changeUserVmServiceOffering(userVm, vmSnapshotVO);
verify(_vmSnapshotMgr).getVmMapDetails(userVm);
verify(_vmSnapshotMgr).upgradeUserVmServiceOffering(ArgumentMatchers.eq(userVm), ArgumentMatchers.eq(SERVICE_OFFERING_ID), mapDetailsCaptor.capture());
verify(_vmSnapshotMgr).upgradeUserVmServiceOffering(eq(userVm), eq(SERVICE_OFFERING_ID), mapDetailsCaptor.capture());
}
@Test(expected=CloudRuntimeException.class)
public void testChangeUserVmServiceOfferingFailOnUpgradeVMServiceOffering() throws ConcurrentOperationException, ResourceUnavailableException, ManagementServerException, VirtualMachineMigrationException {
when(_userVmManager.upgradeVirtualMachine(ArgumentMatchers.eq(TEST_VM_ID), ArgumentMatchers.eq(SERVICE_OFFERING_ID), mapDetailsCaptor.capture())).thenReturn(false);
when(_userVmManager.upgradeVirtualMachine(eq(TEST_VM_ID), eq(SERVICE_OFFERING_ID), mapDetailsCaptor.capture())).thenReturn(false);
_vmSnapshotMgr.changeUserVmServiceOffering(userVm, vmSnapshotVO);
verify(_vmSnapshotMgr).getVmMapDetails(userVm);
verify(_vmSnapshotMgr).upgradeUserVmServiceOffering(ArgumentMatchers.eq(userVm), ArgumentMatchers.eq(SERVICE_OFFERING_ID), mapDetailsCaptor.capture());
verify(_vmSnapshotMgr).upgradeUserVmServiceOffering(eq(userVm), eq(SERVICE_OFFERING_ID), mapDetailsCaptor.capture());
}
@Test
@ -396,16 +401,27 @@ public class VMSnapshotManagerTest {
@Test
public void testRevertUserVmDetailsFromVmSnapshotNotDynamicServiceOffering() {
_vmSnapshotMgr.revertUserVmDetailsFromVmSnapshot(vmMock, vmSnapshotVO);
_vmSnapshotMgr.revertCustomServiceOfferingDetailsFromVmSnapshot(vmMock, vmSnapshotVO);
verify(_vmSnapshotDetailsDao, never()).listDetails(anyLong());
}
@Test
public void testRevertUserVmDetailsFromVmSnapshotDynamicServiceOffering() {
when(serviceOffering.isDynamic()).thenReturn(true);
_vmSnapshotMgr.revertUserVmDetailsFromVmSnapshot(vmMock, vmSnapshotVO);
verify(_vmSnapshotDetailsDao).listDetails(VM_SNAPSHOT_ID);
verify(_vmInstanceDetailsDao).saveDetails(listUserVmDetailsCaptor.capture());
}
VMSnapshotDetailsVO uefiSnapshotDetail = new VMSnapshotDetailsVO(VM_SNAPSHOT_ID, "UEFI", "SECURE", true);
List<VMSnapshotDetailsVO> snapshotDetailsWithUefi = Arrays.asList(
vmSnapshotDetailCpuNumber, vmSnapshotDetailMemory, uefiSnapshotDetail);
when(_vmSnapshotDetailsDao.listDetails(VM_SNAPSHOT_ID)).thenReturn(snapshotDetailsWithUefi);
_vmSnapshotMgr.revertCustomServiceOfferingDetailsFromVmSnapshot(vmMock, vmSnapshotVO);
verify(_vmSnapshotDetailsDao).listDetails(VM_SNAPSHOT_ID);
verify(_vmInstanceDetailsDao, never()).saveDetails(any());
ArgumentCaptor<String> detailNameCaptor = ArgumentCaptor.forClass(String.class);
verify(_vmInstanceDetailsDao, times(2)).addDetail(eq(TEST_VM_ID), detailNameCaptor.capture(), anyString(), anyBoolean());
List<String> appliedNames = detailNameCaptor.getAllValues();
assertTrue(appliedNames.contains(VmDetailConstants.CPU_NUMBER));
assertTrue(appliedNames.contains(VmDetailConstants.MEMORY));
assertFalse("UEFI must not be applied from snapshot so that existing UEFI setting is preserved", appliedNames.contains("UEFI"));
}
}

View File

@ -19,11 +19,8 @@
under the License.
-->
<ruleset name="Maven Ruleset"
xmlns="http://pmd.sf.net/ruleset/1.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://pmd.sf.net/ruleset/1.0.0 http://pmd.sf.net/ruleset_xml_schema.xsd"
xsi:noNamespaceSchemaLocation="http://pmd.sf.net/ruleset_xml_schema.xsd">
<ruleset name="CloudStack PMD Ruleset"
xmlns="http://pmd.sourceforge.net/ruleset/2.0.0">
<description>
Ruleset that brings all the rulesets we want from the pmd jar, because
@ -31,16 +28,16 @@
to add our own future rulesets, if any.
</description>
<rule ref="rulesets/java/basic.xml"/>
<rule ref="rulesets/java/braces.xml"/>
<rule ref="rulesets/java/clone.xml"/>
<rule ref="rulesets/java/codesize.xml"/>
<rule ref="rulesets/java/comments.xml">
<rule ref="category/java/basic.xml"/>
<rule ref="category/java/braces.xml"/>
<rule ref="category/java/clone.xml"/>
<rule ref="category/java/codesize.xml"/>
<rule ref="category/java/comments.xml">
<!-- We shouldn't limit the number of lines in the header of a class -->
<exclude name="CommentSize"/>
<exclude name="CommentRequired" />
</rule>
<rule ref="rulesets/java/controversial.xml">
<rule ref="category/java/controversial.xml">
<!-- The rule is good, but is not properly applied. It forces you to statically declare it as ConcurrentHashMap -->
<exclude name="UseConcurrentHashMap"/>
<exclude name="CallSuperInConstructor"/>
@ -50,35 +47,35 @@
<exclude name="DataflowAnomalyAnalysis" />
<exclude name="UseObjectForClearerAPI" />
</rule>
<rule ref="rulesets/java/coupling.xml">
<rule ref="category/java/coupling.xml">
<exclude name="ExcessiveImports" />
<exclude name="LawOfDemeter"/>
</rule>
<rule ref="rulesets/java/design.xml">
<rule ref="category/java/design.xml">
<exclude name="ConstructorCallsOverridableMethod"/>
<exclude name="AbstractClassWithoutAbstractMethod"/>
<exclude name="AvoidSynchronizedAtMethodLevel"/>
</rule>
<rule ref="rulesets/java/empty.xml"/>
<rule ref="rulesets/java/finalizers.xml"/>
<rule ref="rulesets/java/imports.xml"/>
<rule ref="rulesets/java/j2ee.xml"/>
<rule ref="rulesets/java/junit.xml"/>
<rule ref="rulesets/java/logging-java.xml"/>
<rule ref="rulesets/java/naming.xml">
<rule ref="category/java/empty.xml"/>
<rule ref="category/java/finalizers.xml"/>
<rule ref="category/java/imports.xml"/>
<rule ref="category/java/j2ee.xml"/>
<rule ref="category/java/junit.xml"/>
<rule ref="category/java/logging-java.xml"/>
<rule ref="category/java/naming.xml">
<exclude name="ShortVariable"/>
<exclude name="AbstractNaming"/>
</rule>
<rule ref="rulesets/java/naming.xml/LongVariable">
<rule ref="category/java/naming.xml/LongVariable">
<properties>
<property name="minimum" value="32"/>
</properties>
</rule>
<rule ref="rulesets/java/optimizations.xml"/>
<rule ref="rulesets/java/strictexception.xml"/>
<rule ref="rulesets/java/strings.xml"/>
<rule ref="rulesets/java/sunsecure.xml"/>
<rule ref="rulesets/java/typeresolution.xml"/>
<rule ref="rulesets/java/unnecessary.xml"/>
<rule ref="rulesets/java/unusedcode.xml"/>
<rule ref="category/java/optimizations.xml"/>
<rule ref="category/java/strictexception.xml"/>
<rule ref="category/java/strings.xml"/>
<rule ref="category/java/sunsecure.xml"/>
<rule ref="category/java/typeresolution.xml"/>
<rule ref="category/java/unnecessary.xml"/>
<rule ref="category/java/unusedcode.xml"/>
</ruleset>

View File

@ -1017,6 +1017,7 @@
"label.endpoint": "Endpoint",
"label.endport": "End port",
"label.enter.account.name": "Enter the account name",
"label.enter.domain.name": "Enter the domain name",
"label.enter.code": "Enter 2FA code to verify",
"label.enter.static.pin": "Enter static PIN to verify",
"label.enter.token": "Enter token",
@ -3270,6 +3271,9 @@
"message.delete.account.processing": "Deleting account",
"message.delete.account.success": "Successfully deleted account",
"message.delete.account.warning": "Deleting this account will delete all of the instances, volumes and snapshots associated with the account.",
"message.delete.domain.confirm": "Please confirm that you want to delete this domain by entering the name of the domain below.",
"message.delete.domain.warning": "All associated accounts, users, VMs, and sub-domains will be permanently deleted. This action cannot be undone.",
"message.delete.domain.failed": "Delete domain failed",
"message.delete.acl.processing": "Removing ACL rule...",
"message.delete.acl.rule": "Remove ACL rule",
"message.delete.acl.rule.failed": "Failed to remove ACL rule.",

View File

@ -0,0 +1,155 @@
// 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.
<template>
<a-modal
:visible="true"
:title="$t('label.action.delete.domain') + ': ' + domain.name"
:okText="$t('label.delete.domain')"
okType="danger"
:confirmLoading="loading"
:ok-button-props="{ disabled: !canDelete }"
@cancel="emitClose"
@ok="emitConfirm">
<a-alert
type="warning"
show-icon
style="margin-bottom: 16px">
<template #message>
<div v-html="$t('message.delete.domain.warning')"></div>
</template>
</a-alert>
<a-spin v-if="loading" />
<a-table
v-else
size="small"
:columns="columns"
:dataSource="accountVmSummary"
:pagination="false"
rowKey="account" />
<div style="margin-top: 16px">
<a-alert style="margin-bottom: 10px">
<template #message>
<div v-html="$t('message.delete.domain.confirm')"></div>
</template>
</a-alert>
<a-input
v-model:value="confirmText"
:placeholder="$t('label.enter.domain.name')" />
</div>
</a-modal>
</template>
<script>
import { api } from '@/api'
export default {
name: 'DomainDeleteConfirm',
props: {
domain: {
type: Object,
required: true
}
},
data () {
return {
loading: false,
confirmText: '',
accountVmSummary: []
}
},
computed: {
canDelete () {
return this.confirmText.trim() === this.domain.name.trim()
},
columns () {
return [
{ title: this.$t('label.account'), dataIndex: 'account' },
{ title: this.$t('label.total') + ' VMs', dataIndex: 'total' },
{ title: this.$t('label.running') + ' VMs', dataIndex: 'running' },
{ title: this.$t('label.stopped') + ' VMs', dataIndex: 'stopped' }
]
}
},
mounted () {
this.fetchDomainImpact()
},
methods: {
emitClose () {
this.$emit('close')
},
emitConfirm () {
if (this.canDelete) {
this.$emit('confirm')
}
},
async fetchDomainImpact () {
this.loading = true
try {
const accResp = await api('listAccounts', {
domainid: this.domain.id,
listall: true
})
const accounts =
accResp.listaccountsresponse &&
accResp.listaccountsresponse.account
? accResp.listaccountsresponse.account
: []
const vmResp = await api('listVirtualMachines', {
domainid: this.domain.id,
listall: true
})
const vms =
vmResp.listvirtualmachinesresponse &&
vmResp.listvirtualmachinesresponse.virtualmachine
? vmResp.listvirtualmachinesresponse.virtualmachine
: []
this.accountVmSummary = accounts.map(account => {
const accountVms = vms.filter(vm => vm.account === account.name)
const running = accountVms.filter(vm => vm.state === 'Running').length
const stopped = accountVms.length - running
return {
account: account.name,
total: accountVms.length,
running,
stopped
}
})
} catch (e) {
this.$notification.error({
message: this.$t('message.request.failed'),
description: e.response?.headers['x-description'] || this.$t('message.request.failed')
})
} finally {
this.loading = false
}
}
}
}
</script>
<style scoped>
</style>

View File

@ -74,6 +74,11 @@
:resource="resource"
:action="action"/>
</div>
<domain-delete-confirm
v-if="showDeleteConfirm"
:domain="deleteDomainResource"
@close="showDeleteConfirm = false"
@confirm="confirmDeleteDomain" />
</div>
</template>
@ -87,6 +92,7 @@ import ActionButton from '@/components/view/ActionButton'
import TreeView from '@/components/view/TreeView'
import DomainActionForm from '@/views/iam/DomainActionForm'
import ResourceView from '@/components/view/ResourceView'
import DomainDeleteConfirm from '@/components/view/DomainDeleteConfirm'
import eventBus from '@/config/eventBus'
export default {
@ -96,7 +102,8 @@ export default {
ActionButton,
TreeView,
DomainActionForm,
ResourceView
ResourceView,
DomainDeleteConfirm
},
mixins: [mixinDevice],
data () {
@ -111,7 +118,9 @@ export default {
action: {},
dataView: false,
domainStore: {},
treeDeletedKey: null
treeDeletedKey: null,
showDeleteConfirm: false,
deleteDomainResource: null
}
},
computed: {
@ -205,7 +214,12 @@ export default {
})
},
execAction (action) {
this.treeDeletedKey = action.api === 'deleteDomain' ? this.resource.key : null
if (action.api === 'deleteDomain') {
this.deleteDomainResource = this.resource
this.showDeleteConfirm = true
return
}
this.treeDeletedKey = null
this.actionData = []
this.action = action
this.action.params = store.getters.apis[this.action.api].params
@ -316,6 +330,42 @@ export default {
closeAction () {
this.showAction = false
},
confirmDeleteDomain () {
const domain = this.deleteDomainResource
const params = { id: domain.id, cleanup: true }
api('deleteDomain', params).then(json => {
const jobId = json.deletedomainresponse.jobid
this.$pollJob({
jobId,
title: this.$t('label.action.delete.domain'),
description: domain.name,
loadingMessage: `${this.$t('label.action.delete.domain')} ${domain.name}`,
successMessage: `${this.$t('label.action.delete.domain')} ${domain.name}`,
catchMessage: this.$t('error.fetching.async.job.result'),
successMethod: () => {
this.$router.replace({ path: '/domain' })
this.resource = {}
this.treeSelected = {}
this.treeDeletedKey = null
this.treeViewKey += 1
this.$nextTick(() => {
this.fetchData()
})
}
})
}).catch(error => {
this.$notification.error({
message: this.$t('message.request.failed'),
description: error.response?.headers['x-description'] || this.$t('message.request.failed')
})
}).finally(() => {
this.showDeleteConfirm = false
this.deleteDomainResource = null
this.treeDeletedKey = null
})
},
forceRerender () {
this.treeViewKey += 1
}

View File

@ -261,6 +261,8 @@ export default {
userdatapolicylist: {},
architectureTypes: {},
originalOstypeid: null
detailsFields: [],
details: {}
}
},
beforeCreate () {
@ -315,17 +317,10 @@ export default {
}
}
}
const resourceDetailsFields = []
if (this.resource.hypervisor === 'KVM') {
resourceDetailsFields.push('rootDiskController')
this.detailsFields.push('rootDiskController')
} else if (this.resource.hypervisor === 'VMware' && !this.resource.deployasis) {
resourceDetailsFields.push(...['rootDiskController', 'nicAdapter', 'keyboard'])
}
for (var detailsField of resourceDetailsFields) {
var detailValue = this.resource?.details?.[detailsField] || null
if (detailValue) {
this.form[detailValue] = fieldValue
}
this.detailsFields.push(...['rootDiskController', 'nicAdapter', 'keyboard'])
}
},
fetchData () {
@ -336,6 +331,7 @@ export default {
this.fetchKeyboardTypes()
this.fetchUserdata()
this.fetchUserdataPolicy()
this.fetchDetails()
},
isValidValueForKey (obj, key) {
if (this.emptyAllowedFields.includes(key) && obj[key] === '') {
@ -380,6 +376,10 @@ export default {
id: 'virtio',
description: 'virtio'
})
controller.push({
id: 'virtio-blk',
description: 'virtio-blk'
})
} else if (hyperVisor === 'VMware') {
controller.push({
id: '',
@ -506,6 +506,25 @@ export default {
this.userdata.loading = false
})
},
fetchDetails () {
const params = {}
params.id = this.resource.id
params.templatefilter = 'all'
api('listTemplates', params).then(response => {
if (response?.listtemplatesresponse?.template?.length > 0) {
this.details = response.listtemplatesresponse.template[0].details
if (this.details) {
for (var detailsField of this.detailsFields) {
var detailValue = this.details?.[detailsField] || null
if (detailValue) {
this.form[detailsField] = detailValue
}
}
}
}
})
},
handleSubmit (e) {
e.preventDefault()
if (this.loading) return
@ -515,10 +534,14 @@ export default {
const params = {
id: this.resource.id
}
const detailsField = ['rootDiskController', 'nicAdapter', 'keyboard']
if (this.details) {
Object.keys(this.details).forEach((detail, index) => {
params['details[0].' + detail] = this.details[detail]
})
}
for (const key in values) {
if (!this.isValidValueForKey(values, key)) continue
if (detailsField.includes(key)) {
if (this.detailsFields.includes(key)) {
params['details[0].' + key] = values[key]
continue
}