Merge release branch 4.18 to main

* 4.18:
  server: do not check zone imagestores for directdownload template delete (#7607)
  UI: Zone wizard fix (#7588)
This commit is contained in:
Daan Hoogland 2023-07-04 10:17:43 +02:00
commit fbb9dec6e5
7 changed files with 101 additions and 37 deletions

View File

@ -274,7 +274,7 @@ public interface ConfigurationService {
/**
* Edits a zone in the database. Will not allow you to edit DNS values if there are VMs in the specified zone.
*
* @param UpdateZoneCmd
* @param cmd command object containing the id of the zone to update and relevant attributes
* @return Updated zone
*/
DataCenter editZone(UpdateZoneCmd cmd);
@ -282,8 +282,7 @@ public interface ConfigurationService {
/**
* Deletes a zone from the database. Will not allow you to delete zones that are being used anywhere in the system.
*
* @param userId
* @param zoneId
* @param cmd command object containg the zoneid
*/
boolean deleteZone(DeleteZoneCmd cmd);
@ -319,13 +318,12 @@ public interface ConfigurationService {
Vlan updateVlanAndPublicIpRange(UpdateVlanIpRangeCmd cmd) throws ConcurrentOperationException,
ResourceUnavailableException, ResourceAllocationException;
/**
* Marks the the account with the default zone-id.
* Marks the account with the default zone-id.
*
* @param accountName
* @param domainId
* @param zoneId
* @param defaultZoneId
* @return The new account object
* @throws ,
*/
Account markDefaultZone(String accountName, long domainId, long defaultZoneId);

View File

@ -41,6 +41,7 @@ import java.util.stream.Collectors;
import javax.inject.Inject;
import javax.naming.ConfigurationException;
import javax.persistence.EntityExistsException;
import org.apache.cloudstack.api.command.admin.zone.AddVmwareDcCmd;
import org.apache.cloudstack.api.command.admin.zone.ImportVsphereStoragePoliciesCmd;
@ -1171,12 +1172,7 @@ public class VmwareManagerImpl extends ManagerBase implements VmwareManager, Vmw
// Association of VMware DC to zone is not allowed if zone already has resources added.
validateZoneWithResources(zoneId, "add VMware datacenter to zone");
// Check if DC is already part of zone
// In that case vmware_data_center table should have the DC
vmwareDc = vmwareDcDao.getVmwareDatacenterByGuid(vmwareDcName + "@" + vCenterHost);
if (vmwareDc != null) {
throw new ResourceInUseException("This DC is already part of other CloudStack zone(s). Cannot add this DC to more zones.");
}
checkIfDcIsUsed(vCenterHost, vmwareDcName, zoneId);
VmwareContext context = null;
DatacenterMO dcMo = null;
@ -1210,11 +1206,9 @@ public class VmwareManagerImpl extends ManagerBase implements VmwareManager, Vmw
throw new ResourceInUseException("This DC is being managed by other CloudStack deployment. Cannot add this DC to zone.");
}
// Add DC to database into vmware_data_center table
vmwareDc = new VmwareDatacenterVO(guid, vmwareDcName, vCenterHost, userName, password);
vmwareDc = vmwareDcDao.persist(vmwareDc);
vmwareDc = createOrUpdateDc(guid, vmwareDcName, vCenterHost, userName, password);
// Map zone with vmware datacenter
// Map zone with vmware datacenter
vmwareDcZoneMap = new VmwareDatacenterZoneMapVO(zoneId, vmwareDc.getId());
vmwareDcZoneMap = vmwareDatacenterZoneMapDao.persist(vmwareDcZoneMap);
@ -1243,6 +1237,41 @@ public class VmwareManagerImpl extends ManagerBase implements VmwareManager, Vmw
return vmwareDc;
}
VmwareDatacenterVO createOrUpdateDc(String guid, String name, String host, String user, String password) {
VmwareDatacenterVO vmwareDc = new VmwareDatacenterVO(guid, name, host, user, password);
// Add DC to database into vmware_data_center table
try {
vmwareDc = vmwareDcDao.persist(vmwareDc);
} catch (EntityExistsException e) {
// if that fails just get the record as is
vmwareDc = vmwareDcDao.getVmwareDatacenterByGuid(guid);
// we could now update the `vmwareDC` with the user supplied `password`, `user`, `name` and `host`,
// but let's assume user error for now
}
return vmwareDc;
}
/**
* Check if DC is already part of zone
* In that case vmware_data_center table should have the DC and a dc zone mapping should exist
*
* @param vCenterHost
* @param vmwareDcName
* @param zoneId
* @throws ResourceInUseException if the DC can not be used.
*/
private void checkIfDcIsUsed(String vCenterHost, String vmwareDcName, Long zoneId) throws ResourceInUseException {
VmwareDatacenterVO vmwareDc;
vmwareDc = vmwareDcDao.getVmwareDatacenterByGuid(vmwareDcName + "@" + vCenterHost);
if (vmwareDc != null) {
VmwareDatacenterZoneMapVO mapping = vmwareDatacenterZoneMapDao.findByVmwareDcId(vmwareDc.getId());
if (mapping != null && Long.compare(zoneId, mapping.getZoneId()) == 0) {
throw new ResourceInUseException(String.format("This DC (%s) is already part of other CloudStack zone (%d). Cannot add this DC to more zones.", vmwareDc.getUuid(), zoneId));
}
}
}
@Override
@ActionEvent(eventType = EventTypes.EVENT_ZONE_EDIT, eventDescription = "updating VMware datacenter")
public VmwareDatacenter updateVmwareDatacenter(UpdateVmwareDcCmd cmd) {

View File

@ -2369,10 +2369,8 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati
throw new CloudRuntimeException(errorMsg + "there are Secondary storages in this zone");
}
// Check if there are any non-removed VMware datacenters in the zone.
//if (_vmwareDatacenterZoneMapDao.findByZoneId(zoneId) != null) {
// throw new CloudRuntimeException(errorMsg + "there are VMware datacenters in this zone.");
//}
// We could check if there are any non-removed VMware datacenters in the zone. EWe don´t care.
// These can continu to exist as long as the mapping will be gone (see line deleteZone
}
private void checkZoneParameters(final String zoneName, final String dns1, final String dns2, final String internalDns1, final String internalDns2, final boolean checkForDuplicates, final Long domainId,
@ -2512,6 +2510,8 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati
for (final VlanVO vlan : vlans) {
_vlanDao.remove(vlan.getId());
}
// we should actually find the mapping and remove if it exists
// but we don't know about vmware/plugin/hypervisors at this point
final boolean success = _zoneDao.remove(zoneId);

View File

@ -552,7 +552,7 @@ public class ResourceManagerImpl extends ManagerBase implements ResourceManager,
}
// save cluster details for later cluster/host cross-checking
final Map<String, String> details = new HashMap<String, String>();
final Map<String, String> details = new HashMap<>();
details.put("url", url);
details.put("username", StringUtils.defaultString(username));
details.put("password", StringUtils.defaultString(password));

View File

@ -177,7 +177,10 @@ public class HypervisorTemplateAdapter extends TemplateAdapterBase {
return ans.getTemplateSize();
}
private void checkZoneImageStores(final List<Long> zoneIdList) {
protected void checkZoneImageStores(final VMTemplateVO template, final List<Long> zoneIdList) {
if (template.isDirectDownload()) {
return;
}
if (zoneIdList != null && CollectionUtils.isEmpty(storeMgr.getImageStoresByScope(new ZoneScope(zoneIdList.get(0))))) {
throw new InvalidParameterValueException("Failed to find a secondary storage in the specified zone.");
}
@ -677,14 +680,14 @@ public class HypervisorTemplateAdapter extends TemplateAdapterBase {
if (template.getTemplateType() == TemplateType.SYSTEM) {
throw new InvalidParameterValueException("The DomR template cannot be deleted.");
}
checkZoneImageStores(profile.getZoneIdList());
checkZoneImageStores(profile.getTemplate(), profile.getZoneIdList());
return profile;
}
@Override
public TemplateProfile prepareDelete(DeleteIsoCmd cmd) {
TemplateProfile profile = super.prepareDelete(cmd);
checkZoneImageStores(profile.getZoneIdList());
checkZoneImageStores(profile.getTemplate(), profile.getZoneIdList());
return profile;
}
}

View File

@ -18,6 +18,13 @@
package com.cloud.template;
import static org.mockito.Mockito.any;
import static org.mockito.Mockito.anyLong;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
@ -29,6 +36,7 @@ import java.util.Map;
import java.util.concurrent.ExecutionException;
import org.apache.cloudstack.engine.subsystem.api.storage.DataStore;
import org.apache.cloudstack.engine.subsystem.api.storage.DataStoreManager;
import org.apache.cloudstack.engine.subsystem.api.storage.TemplateDataFactory;
import org.apache.cloudstack.engine.subsystem.api.storage.TemplateInfo;
import org.apache.cloudstack.engine.subsystem.api.storage.TemplateService;
@ -44,7 +52,7 @@ import org.apache.cloudstack.storage.datastore.db.TemplateDataStoreVO;
import org.apache.cloudstack.storage.image.datastore.ImageStoreEntity;
import org.junit.Assert;
import org.junit.Before;
//import org.junit.Test;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
@ -61,23 +69,17 @@ import com.cloud.event.EventTypes;
import com.cloud.event.UsageEventUtils;
import com.cloud.event.UsageEventVO;
import com.cloud.event.dao.UsageEventDao;
import com.cloud.exception.InvalidParameterValueException;
import com.cloud.storage.Storage.ImageFormat;
import com.cloud.storage.TemplateProfile;
import com.cloud.storage.VMTemplateStorageResourceAssoc.Status;
import com.cloud.storage.dao.VMTemplateZoneDao;
import com.cloud.storage.VMTemplateVO;
import com.cloud.storage.dao.VMTemplateZoneDao;
import com.cloud.user.AccountVO;
import com.cloud.user.ResourceLimitService;
import com.cloud.user.dao.AccountDao;
import com.cloud.utils.component.ComponentContext;
import static org.mockito.Mockito.any;
import static org.mockito.Mockito.anyLong;
import static org.mockito.Mockito.when;
import static org.mockito.Mockito.eq;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.mock;
@RunWith(PowerMockRunner.class)
@PrepareForTest(ComponentContext.class)
public class HypervisorTemplateAdapterTest {
@ -118,6 +120,9 @@ public class HypervisorTemplateAdapterTest {
@Mock
ConfigurationDao _configDao;
@Mock
DataStoreManager storeMgr;
@InjectMocks
HypervisorTemplateAdapter _adapter;
@ -282,4 +287,27 @@ public class HypervisorTemplateAdapterTest {
cleanupUsageUtils();
}
@Test
public void testCheckZoneImageStoresDirectDownloadTemplate() {
VMTemplateVO templateVO = Mockito.mock(VMTemplateVO.class);
Mockito.when(templateVO.isDirectDownload()).thenReturn(true);
_adapter.checkZoneImageStores(templateVO, List.of(1L));
}
@Test
public void testCheckZoneImageStoresRegularTemplateWithStore() {
VMTemplateVO templateVO = Mockito.mock(VMTemplateVO.class);
Mockito.when(templateVO.isDirectDownload()).thenReturn(false);
Mockito.when(storeMgr.getImageStoresByScope(Mockito.any())).thenReturn(List.of(Mockito.mock(DataStore.class)));
_adapter.checkZoneImageStores(templateVO, List.of(1L));
}
@Test(expected = InvalidParameterValueException.class)
public void testCheckZoneImageStoresRegularTemplateNoStore() {
VMTemplateVO templateVO = Mockito.mock(VMTemplateVO.class);
Mockito.when(templateVO.isDirectDownload()).thenReturn(false);
Mockito.when(storeMgr.getImageStoresByScope(Mockito.any())).thenReturn(new ArrayList<>());
_adapter.checkZoneImageStores(templateVO, List.of(1L));
}
}

View File

@ -345,7 +345,7 @@ export default {
params.internaldns1 = this.prefillContent?.internalDns1 || null
params.internaldns2 = this.prefillContent?.internalDns2 || null
params.domain = this.prefillContent?.networkDomain || null
params.isedge = this.prefillContent?.zoneSuperType === 'Edge' || false
params.isedge = this.isEdgeZone
try {
if (!this.stepData.stepMove.includes('createZone')) {
@ -835,7 +835,10 @@ export default {
const params = {}
params.zoneId = this.stepData.zoneReturned.id
params.name = this.prefillContent?.podName || this.stepData.zoneReturned.type === 'Edge' ? 'Pod-' + this.stepData.zoneReturned.name : null
params.name = this.prefillContent?.podName || null
if (this.isEdgeZone) {
params.name = 'Pod-' + this.stepData.zoneReturned.name
}
params.gateway = this.prefillContent?.podReservedGateway || null
params.netmask = this.prefillContent?.podReservedNetmask || null
params.startIp = this.prefillContent?.podReservedStartIp || null
@ -1218,7 +1221,10 @@ export default {
}
params.clustertype = clusterType
params.podId = this.stepData.podReturned.id
let clusterName = this.prefillContent.clusterName || this.stepData.zoneReturned.type === 'Edge' ? 'Cluster-' + this.stepData.zoneReturned.name : null
let clusterName = this.prefillContent?.clusterName || null
if (this.isEdgeZone) {
clusterName = 'Cluster-' + this.stepData.zoneReturned.name
}
if (hypervisor === 'VMware') {
params.username = this.prefillContent?.vCenterUsername || null
@ -2051,7 +2057,7 @@ export default {
return new Promise((resolve, reject) => {
let message = ''
api('addCluster', args).then(json => {
api('addCluster', args, 'POST').then(json => {
const result = json.addclusterresponse.cluster[0]
resolve(result)
}).catch(error => {