api,ui: multi arch improvements (#10289)

This commit is contained in:
Abhishek Kumar 2025-04-25 14:32:27 +05:30 committed by GitHub
parent 1adfaf90ad
commit 12c077d704
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
90 changed files with 2816 additions and 735 deletions

View File

@ -16,52 +16,55 @@
// under the License.
package com.cloud.cpu;
import com.cloud.utils.exception.CloudRuntimeException;
import org.apache.commons.lang3.StringUtils;
import java.util.LinkedHashMap;
import java.util.Map;
public class CPU {
public enum CPUArch {
x86("i686", 32),
amd64("x86_64", 64),
arm64("aarch64", 64);
public static final String archX86Identifier = "i686";
public static final String archX86_64Identifier = "x86_64";
public static final String archARM64Identifier = "aarch64";
private final String type;
private final int bits;
public static class CPUArch {
private static final Map<String, CPUArch> cpuArchMap = new LinkedHashMap<>();
public static final CPUArch archX86 = new CPUArch(archX86Identifier, 32);
public static final CPUArch amd64 = new CPUArch(archX86_64Identifier, 64);
public static final CPUArch arm64 = new CPUArch(archARM64Identifier, 64);
private String type;
private int bits;
public CPUArch(String type, int bits) {
CPUArch(String type, int bits) {
this.type = type;
this.bits = bits;
cpuArchMap.put(type, this);
}
public static CPUArch getDefault() {
return amd64;
}
public String getType() {
return this.type;
return type;
}
public int getBits() {
return this.bits;
return bits;
}
public static CPUArch fromType(String type) {
if (StringUtils.isBlank(type)) {
return amd64;
return getDefault();
}
switch (type) {
case archX86Identifier: return archX86;
case archX86_64Identifier: return amd64;
case archARM64Identifier: return arm64;
default: throw new CloudRuntimeException(String.format("Unsupported arch type: %s", type));
for (CPUArch arch : values()) {
if (arch.type.equals(type)) {
return arch;
}
}
throw new IllegalArgumentException("Unsupported arch type: " + type);
}
public static String getTypesAsCSV() {
StringBuilder sb = new StringBuilder();
for (CPUArch arch : values()) {
sb.append(arch.getType()).append(",");
}
if (sb.length() > 0) {
sb.setLength(sb.length() - 1);
}
return sb.toString();
}
}
}

View File

@ -19,7 +19,6 @@ package org.apache.cloudstack.api.command.admin.cluster;
import java.util.ArrayList;
import java.util.List;
import org.apache.cloudstack.api.APICommand;
import org.apache.cloudstack.api.ApiConstants;
import org.apache.cloudstack.api.BaseListCmd;
@ -28,7 +27,9 @@ import org.apache.cloudstack.api.response.ClusterResponse;
import org.apache.cloudstack.api.response.ListResponse;
import org.apache.cloudstack.api.response.PodResponse;
import org.apache.cloudstack.api.response.ZoneResponse;
import org.apache.commons.lang3.StringUtils;
import com.cloud.cpu.CPU;
import com.cloud.org.Cluster;
import com.cloud.utils.Pair;
@ -68,6 +69,11 @@ public class ListClustersCmd extends BaseListCmd {
@Parameter(name = ApiConstants.SHOW_CAPACITIES, type = CommandType.BOOLEAN, description = "flag to display the capacity of the clusters")
private Boolean showCapacities;
@Parameter(name = ApiConstants.ARCH, type = CommandType.STRING,
description = "CPU arch of the clusters",
since = "4.20.1")
private String arch;
/////////////////////////////////////////////////////
/////////////////// Accessors ///////////////////////
/////////////////////////////////////////////////////
@ -112,6 +118,10 @@ public class ListClustersCmd extends BaseListCmd {
return showCapacities;
}
public CPU.CPUArch getArch() {
return StringUtils.isBlank(arch) ? null : CPU.CPUArch.fromType(arch);
}
/////////////////////////////////////////////////////
/////////////// API Implementation///////////////////
/////////////////////////////////////////////////////

View File

@ -21,7 +21,6 @@ import java.util.EnumSet;
import java.util.List;
import java.util.Map;
import org.apache.cloudstack.api.APICommand;
import org.apache.cloudstack.api.ApiCommandResourceType;
import org.apache.cloudstack.api.ApiConstants;
@ -34,7 +33,9 @@ import org.apache.cloudstack.api.response.ListResponse;
import org.apache.cloudstack.api.response.PodResponse;
import org.apache.cloudstack.api.response.UserVmResponse;
import org.apache.cloudstack.api.response.ZoneResponse;
import org.apache.commons.lang3.StringUtils;
import com.cloud.cpu.CPU;
import com.cloud.exception.InvalidParameterValueException;
import com.cloud.host.Host;
import com.cloud.hypervisor.Hypervisor.HypervisorType;
@ -105,6 +106,9 @@ public class ListHostsCmd extends BaseListCmd {
@Parameter(name = ApiConstants.HYPERVISOR, type = CommandType.STRING, description = "hypervisor type of host: XenServer,KVM,VMware,Hyperv,BareMetal,Simulator")
private String hypervisor;
@Parameter(name = ApiConstants.ARCH, type = CommandType.STRING, description = "CPU Arch of the host", since = "4.20.1")
private String arch;
/////////////////////////////////////////////////////
/////////////////// Accessors ///////////////////////
/////////////////////////////////////////////////////
@ -189,6 +193,10 @@ public class ListHostsCmd extends BaseListCmd {
return outOfBandManagementPowerState;
}
public CPU.CPUArch getArch() {
return StringUtils.isBlank(arch) ? null : CPU.CPUArch.fromType(arch);
}
/////////////////////////////////////////////////////
/////////////// API Implementation///////////////////
/////////////////////////////////////////////////////

View File

@ -16,8 +16,6 @@
// under the License.
package org.apache.cloudstack.api.command.admin.router;
import org.apache.commons.lang.BooleanUtils;
import org.apache.cloudstack.api.APICommand;
import org.apache.cloudstack.api.ApiCommandResourceType;
import org.apache.cloudstack.api.ApiConstants;
@ -32,7 +30,10 @@ import org.apache.cloudstack.api.response.PodResponse;
import org.apache.cloudstack.api.response.UserVmResponse;
import org.apache.cloudstack.api.response.VpcResponse;
import org.apache.cloudstack.api.response.ZoneResponse;
import org.apache.commons.lang.BooleanUtils;
import org.apache.commons.lang3.StringUtils;
import com.cloud.cpu.CPU;
import com.cloud.network.router.VirtualRouter.Role;
import com.cloud.vm.VirtualMachine;
@ -86,6 +87,11 @@ public class ListRoutersCmd extends BaseListProjectAndAccountResourcesCmd {
description = "if true is passed for this parameter, also fetch last executed health check results for the router. Default is false")
private Boolean fetchHealthCheckResults;
@Parameter(name = ApiConstants.ARCH, type = CommandType.STRING,
description = "CPU arch of the router",
since = "4.20.1")
private String arch;
/////////////////////////////////////////////////////
/////////////////// Accessors ///////////////////////
/////////////////////////////////////////////////////
@ -146,6 +152,10 @@ public class ListRoutersCmd extends BaseListProjectAndAccountResourcesCmd {
return BooleanUtils.isTrue(fetchHealthCheckResults);
}
public CPU.CPUArch getArch() {
return StringUtils.isBlank(arch) ? null : CPU.CPUArch.fromType(arch);
}
/////////////////////////////////////////////////////
/////////////// API Implementation///////////////////

View File

@ -19,7 +19,6 @@ package org.apache.cloudstack.api.command.admin.systemvm;
import java.util.ArrayList;
import java.util.List;
import org.apache.cloudstack.api.APICommand;
import org.apache.cloudstack.api.ApiCommandResourceType;
import org.apache.cloudstack.api.ApiConstants;
@ -31,7 +30,9 @@ import org.apache.cloudstack.api.response.PodResponse;
import org.apache.cloudstack.api.response.StoragePoolResponse;
import org.apache.cloudstack.api.response.SystemVmResponse;
import org.apache.cloudstack.api.response.ZoneResponse;
import org.apache.commons.lang3.StringUtils;
import com.cloud.cpu.CPU;
import com.cloud.utils.Pair;
import com.cloud.vm.VirtualMachine;
@ -74,6 +75,11 @@ public class ListSystemVMsCmd extends BaseListCmd {
since = "3.0.1")
private Long storageId;
@Parameter(name = ApiConstants.ARCH, type = CommandType.STRING,
description = "CPU arch of the system VM",
since = "4.20.1")
private String arch;
/////////////////////////////////////////////////////
/////////////////// Accessors ///////////////////////
/////////////////////////////////////////////////////
@ -110,6 +116,10 @@ public class ListSystemVMsCmd extends BaseListCmd {
return storageId;
}
public CPU.CPUArch getArch() {
return StringUtils.isBlank(arch) ? null : CPU.CPUArch.fromType(arch);
}
/////////////////////////////////////////////////////
/////////////// API Implementation///////////////////
/////////////////////////////////////////////////////

View File

@ -46,9 +46,11 @@ import org.apache.cloudstack.api.response.UserResponse;
import org.apache.cloudstack.api.response.UserVmResponse;
import org.apache.cloudstack.api.response.VpcResponse;
import org.apache.cloudstack.api.response.ZoneResponse;
import org.apache.commons.lang3.BooleanUtils;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.BooleanUtils;
import org.apache.commons.lang3.StringUtils;
import com.cloud.cpu.CPU;
import com.cloud.exception.InvalidParameterValueException;
import com.cloud.server.ResourceIcon;
import com.cloud.server.ResourceTag;
@ -153,6 +155,11 @@ public class ListVMsCmd extends BaseListRetrieveOnlyResourceCountCmd implements
@Parameter(name = ApiConstants.USER_DATA_ID, type = CommandType.UUID, entityType = UserDataResponse.class, required = false, description = "the instances by userdata", since = "4.20.1")
private Long userdataId;
@Parameter(name = ApiConstants.ARCH, type = CommandType.STRING,
description = "CPU arch of the VM",
since = "4.20.1")
private String arch;
/////////////////////////////////////////////////////
/////////////////// Accessors ///////////////////////
/////////////////////////////////////////////////////
@ -292,6 +299,10 @@ public class ListVMsCmd extends BaseListRetrieveOnlyResourceCountCmd implements
return isVnf;
}
public CPU.CPUArch getArch() {
return StringUtils.isBlank(arch) ? null : CPU.CPUArch.fromType(arch);
}
/////////////////////////////////////////////////////
/////////////// API Implementation///////////////////
/////////////////////////////////////////////////////

View File

@ -245,6 +245,10 @@ public class DomainRouterResponse extends BaseResponseWithAnnotations implements
@Param(description = "the version of the code / software in the router")
private String softwareVersion;
@SerializedName(ApiConstants.ARCH)
@Param(description = "CPU arch of the router", since = "4.20.1")
private String arch;
public DomainRouterResponse() {
nics = new LinkedHashSet<NicResponse>();
}
@ -518,4 +522,8 @@ public class DomainRouterResponse extends BaseResponseWithAnnotations implements
public void setSoftwareVersion(String softwareVersion) {
this.softwareVersion = softwareVersion;
}
public void setArch(String arch) {
this.arch = arch;
}
}

View File

@ -186,6 +186,10 @@ public class SystemVmResponse extends BaseResponseWithAnnotations {
@Param(description = "the name of the service offering of the system virtual machine.")
private String serviceOfferingName;
@SerializedName(ApiConstants.ARCH)
@Param(description = "CPU arch of the system VM", since = "4.20.1")
private String arch;
@Override
public String getObjectId() {
return this.getId();
@ -490,4 +494,8 @@ public class SystemVmResponse extends BaseResponseWithAnnotations {
public void setServiceOfferingName(String serviceOfferingName) {
this.serviceOfferingName = serviceOfferingName;
}
public void setArch(String arch) {
this.arch = arch;
}
}

View File

@ -31,13 +31,13 @@ import org.apache.cloudstack.affinity.AffinityGroupResponse;
import org.apache.cloudstack.api.ApiConstants;
import org.apache.cloudstack.api.BaseResponseWithTagInformation;
import org.apache.cloudstack.api.EntityReference;
import org.apache.commons.collections.CollectionUtils;
import com.cloud.network.router.VirtualRouter;
import com.cloud.serializer.Param;
import com.cloud.uservm.UserVm;
import com.cloud.vm.VirtualMachine;
import com.google.gson.annotations.SerializedName;
import org.apache.commons.collections.CollectionUtils;
@SuppressWarnings("unused")
@EntityReference(value = {VirtualMachine.class, UserVm.class, VirtualRouter.class})
@ -396,6 +396,10 @@ public class UserVmResponse extends BaseResponseWithTagInformation implements Co
@Param(description = "User VM type", since = "4.20.0")
private String vmType;
@SerializedName(ApiConstants.ARCH)
@Param(description = "CPU arch of the VM", since = "4.20.1")
private String arch;
public UserVmResponse() {
securityGroupList = new LinkedHashSet<>();
nics = new TreeSet<>(Comparator.comparingInt(x -> Integer.parseInt(x.getDeviceId())));
@ -1169,4 +1173,12 @@ public class UserVmResponse extends BaseResponseWithTagInformation implements Co
public void setIpAddress(String ipAddress) {
this.ipAddress = ipAddress;
}
public String getArch() {
return arch;
}
public void setArch(String arch) {
this.arch = arch;
}
}

View File

@ -0,0 +1,67 @@
// 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 com.cloud.cpu;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertThrows;
import static org.junit.Assert.assertTrue;
import org.junit.Test;
public class CPUTest {
@Test
public void testCPUArchGetType() {
assertEquals("i686", CPU.CPUArch.x86.getType());
assertEquals("x86_64", CPU.CPUArch.amd64.getType());
assertEquals("aarch64", CPU.CPUArch.arm64.getType());
}
@Test
public void testCPUArchGetBits() {
assertEquals(32, CPU.CPUArch.x86.getBits());
assertEquals(64, CPU.CPUArch.amd64.getBits());
assertEquals(64, CPU.CPUArch.arm64.getBits());
}
@Test
public void testCPUArchFromTypeWithValidValues() {
assertEquals(CPU.CPUArch.x86, CPU.CPUArch.fromType("i686"));
assertEquals(CPU.CPUArch.amd64, CPU.CPUArch.fromType("x86_64"));
assertEquals(CPU.CPUArch.arm64, CPU.CPUArch.fromType("aarch64"));
}
@Test
public void testCPUArchFromTypeWithDefaultForBlankOrNull() {
assertEquals(CPU.CPUArch.amd64, CPU.CPUArch.fromType(""));
assertEquals(CPU.CPUArch.amd64, CPU.CPUArch.fromType(" "));
assertEquals(CPU.CPUArch.amd64, CPU.CPUArch.fromType(null));
}
@Test
public void testCPUArchFromTypeWithInvalidValue() {
Exception exception = assertThrows(IllegalArgumentException.class, () -> {
CPU.CPUArch.fromType("unsupported");
});
assertTrue(exception.getMessage().contains("Unsupported arch type: unsupported"));
}
@Test
public void testCPUArchGetTypesAsCSV() {
String expectedCSV = "i686,x86_64,aarch64";
assertEquals(expectedCSV, CPU.CPUArch.getTypesAsCSV());
}
}

View File

@ -56,6 +56,8 @@ if [ "$1" = configure ]; then
chmod 0640 ${CONFDIR}/${DBPROPS}
chgrp cloud ${CONFDIR}/${DBPROPS}
chown -R cloud:cloud /var/log/cloudstack/management
chown -R cloud:cloud /usr/share/cloudstack-management/templates
find /usr/share/cloudstack-management/templates -type d -exec chmod 0770 {} \;
ln -sf ${CONFDIR}/log4j-cloud.xml ${CONFDIR}/log4j2.xml

View File

@ -309,4 +309,8 @@ public interface VirtualMachineManager extends Manager {
Map<Long, Boolean> getDiskOfferingSuitabilityForVm(long vmId, List<Long> diskOfferingIds);
void checkDeploymentPlan(VirtualMachine virtualMachine, VirtualMachineTemplate template,
ServiceOffering serviceOffering, Account systemAccount, DeploymentPlan plan)
throws InsufficientServerCapacityException;
}

View File

@ -28,6 +28,7 @@ import com.cloud.agent.api.StartupCommand;
import com.cloud.agent.api.StartupRoutingCommand;
import com.cloud.agent.api.VgpuTypesInfo;
import com.cloud.agent.api.to.GPUDeviceTO;
import com.cloud.cpu.CPU;
import com.cloud.dc.DataCenterVO;
import com.cloud.dc.HostPodVO;
import com.cloud.dc.PodCluster;
@ -61,6 +62,17 @@ public interface ResourceManager extends ResourceService, Configurable {
+ "To force-stop VMs, choose 'ForceStop' strategy",
true, ConfigKey.Scope.Global, null, null, null, null, null, ConfigKey.Kind.Select, "Error,Migration,ForceStop");
ConfigKey<String> SystemVmPreferredArchitecture = new ConfigKey<>(
String.class,
"system.vm.preferred.architecture",
"Advanced",
CPU.CPUArch.getDefault().getType(),
"Preferred architecture for the system VMs including virtual routers",
true,
ConfigKey.Scope.Zone, null, null, null, null, null,
ConfigKey.Kind.Select,
"," + CPU.CPUArch.getTypesAsCSV());
/**
* Register a listener for different types of resource life cycle events.
* There can only be one type of listener per type of host.

View File

@ -6083,4 +6083,18 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac
}
return result;
}
@Override
public void checkDeploymentPlan(VirtualMachine virtualMachine, VirtualMachineTemplate template,
ServiceOffering serviceOffering, Account systemAccount, DeploymentPlan plan)
throws InsufficientServerCapacityException {
final VirtualMachineProfileImpl vmProfile =
new VirtualMachineProfileImpl(virtualMachine, template, serviceOffering, systemAccount, null);
DeployDestination destination =
_dpMgr.planDeployment(vmProfile, plan, new DeploymentPlanner.ExcludeList(), null);
if (destination == null) {
throw new InsufficientServerCapacityException(String.format("Unable to create a deployment for %s",
vmProfile), DataCenter.class, plan.getDataCenterId(), areAffinityGroupsAssociated(vmProfile));
}
}
}

View File

@ -165,6 +165,15 @@
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<configuration>
<systemPropertyVariables>
<test.mode>true</test.mode>
</systemPropertyVariables>
</configuration>
</plugin>
</plugins>
</build>
<profiles>

View File

@ -18,11 +18,11 @@ package com.cloud.dc.dao;
import java.util.List;
import java.util.Map;
import java.util.Set;
import com.cloud.cpu.CPU;
import com.cloud.dc.ClusterVO;
import com.cloud.hypervisor.Hypervisor.HypervisorType;
import com.cloud.utils.Pair;
import com.cloud.utils.db.GenericDao;
public interface ClusterDao extends GenericDao<ClusterVO, Long> {
@ -30,13 +30,11 @@ public interface ClusterDao extends GenericDao<ClusterVO, Long> {
ClusterVO findBy(String name, long podId);
List<ClusterVO> listByHyTypeWithoutGuid(String hyType);
List<ClusterVO> listByZoneId(long zoneId);
List<HypervisorType> getAvailableHypervisorInZone(Long zoneId);
Set<HypervisorType> getDistinctAvailableHypervisorsAcrossClusters();
List<Pair<HypervisorType, CPU.CPUArch>> listDistinctHypervisorsArchAcrossClusters(Long zoneId);
List<ClusterVO> listByDcHyType(long dcId, String hyType);

View File

@ -21,10 +21,8 @@ import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
import javax.inject.Inject;
@ -39,6 +37,7 @@ import com.cloud.dc.HostPodVO;
import com.cloud.hypervisor.Hypervisor.HypervisorType;
import com.cloud.org.Grouping;
import com.cloud.org.Managed;
import com.cloud.utils.Pair;
import com.cloud.utils.db.GenericDaoBase;
import com.cloud.utils.db.GenericSearchBuilder;
import com.cloud.utils.db.JoinBuilder;
@ -148,14 +147,6 @@ public class ClusterDaoImpl extends GenericDaoBase<ClusterVO, Long> implements C
return findOneBy(sc);
}
@Override
public List<ClusterVO> listByHyTypeWithoutGuid(String hyType) {
SearchCriteria<ClusterVO> sc = HyTypeWithoutGuidSearch.create();
sc.setParameters("hypervisorType", hyType);
return listBy(sc);
}
@Override
public List<ClusterVO> listByDcHyType(long dcId, String hyType) {
SearchCriteria<ClusterVO> sc = ZoneHyTypeSearch.create();
@ -178,8 +169,19 @@ public class ClusterDaoImpl extends GenericDaoBase<ClusterVO, Long> implements C
}
@Override
public Set<HypervisorType> getDistinctAvailableHypervisorsAcrossClusters() {
return new HashSet<>(getAvailableHypervisorInZone(null));
public List<Pair<HypervisorType, CPU.CPUArch>> listDistinctHypervisorsArchAcrossClusters(Long zoneId) {
SearchBuilder<ClusterVO> sb = createSearchBuilder();
sb.select(null, Func.DISTINCT_PAIR, sb.entity().getHypervisorType(), sb.entity().getArch());
sb.and("zoneId", sb.entity().getDataCenterId(), SearchCriteria.Op.EQ);
sb.done();
SearchCriteria<ClusterVO> sc = sb.create();
if (zoneId != null) {
sc.setParameters("zoneId", zoneId);
}
final List<ClusterVO> clusters = search(sc, null);
return clusters.stream()
.map(c -> new Pair<>(c.getHypervisorType(), c.getArch()))
.collect(Collectors.toList());
}
@Override

View File

@ -19,6 +19,7 @@ package com.cloud.host.dao;
import java.util.Date;
import java.util.List;
import com.cloud.cpu.CPU;
import com.cloud.host.Host;
import com.cloud.host.Host.Type;
import com.cloud.host.HostVO;
@ -194,5 +195,9 @@ public interface HostDao extends GenericDao<HostVO, Long>, StateDao<Status, Stat
List<HypervisorType> listDistinctHypervisorTypes(final Long zoneId);
List<Pair<HypervisorType, CPU.CPUArch>> listDistinctHypervisorArchTypes(final Long zoneId);
List<CPU.CPUArch> listDistinctArchTypes(final Long clusterId);
List<HostVO> listByIds(final List<Long> ids);
}

View File

@ -42,6 +42,7 @@ import com.cloud.agent.api.VgpuTypesInfo;
import com.cloud.cluster.agentlb.HostTransferMapVO;
import com.cloud.cluster.agentlb.dao.HostTransferMapDao;
import com.cloud.configuration.ManagementServiceConfiguration;
import com.cloud.cpu.CPU;
import com.cloud.dc.ClusterVO;
import com.cloud.dc.dao.ClusterDao;
import com.cloud.gpu.dao.HostGpuGroupsDao;
@ -1743,17 +1744,52 @@ public class HostDaoImpl extends GenericDaoBase<HostVO, Long> implements HostDao
@Override
public List<HypervisorType> listDistinctHypervisorTypes(final Long zoneId) {
GenericSearchBuilder<HostVO, HypervisorType> sb = createSearchBuilder(HypervisorType.class);
GenericSearchBuilder<HostVO, String> sb = createSearchBuilder(String.class);
sb.and("zoneId", sb.entity().getDataCenterId(), SearchCriteria.Op.EQ);
sb.and("type", sb.entity().getType(), SearchCriteria.Op.EQ);
sb.select(null, Func.DISTINCT, sb.entity().getHypervisorType());
sb.done();
SearchCriteria<HypervisorType> sc = sb.create();
SearchCriteria<String> sc = sb.create();
if (zoneId != null) {
sc.setParameters("zoneId", zoneId);
}
sc.setParameters("type", Type.Routing);
return customSearch(sc, null);
List<String> hypervisorString = customSearch(sc, null);
return hypervisorString.stream().map(HypervisorType::getType).collect(Collectors.toList());
}
@Override
public List<Pair<HypervisorType, CPU.CPUArch>> listDistinctHypervisorArchTypes(final Long zoneId) {
SearchBuilder<HostVO> sb = createSearchBuilder();
sb.select(null, Func.DISTINCT_PAIR, sb.entity().getHypervisorType(), sb.entity().getArch());
sb.and("type", sb.entity().getType(), SearchCriteria.Op.EQ);
sb.and("zoneId", sb.entity().getDataCenterId(), SearchCriteria.Op.EQ);
sb.done();
SearchCriteria<HostVO> sc = sb.create();
sc.setParameters("type", Type.Routing);
if (zoneId != null) {
sc.setParameters("zoneId", zoneId);
}
final List<HostVO> hosts = search(sc, null);
return hosts.stream()
.map(h -> new Pair<>(h.getHypervisorType(), h.getArch()))
.collect(Collectors.toList());
}
@Override
public List<CPU.CPUArch> listDistinctArchTypes(final Long clusterId) {
GenericSearchBuilder<HostVO, String> sb = createSearchBuilder(String.class);
sb.and("clusterId", sb.entity().getClusterId(), SearchCriteria.Op.EQ);
sb.and("type", sb.entity().getType(), SearchCriteria.Op.EQ);
sb.select(null, Func.DISTINCT, sb.entity().getArch());
sb.done();
SearchCriteria<String> sc = sb.create();
if (clusterId != null) {
sc.setParameters("clusterId", clusterId);
}
sc.setParameters("type", Type.Routing);
List<String> archStrings = customSearch(sc, null);
return archStrings.stream().map(CPU.CPUArch::fromType).collect(Collectors.toList());
}
@Override

View File

@ -19,6 +19,7 @@ package com.cloud.storage.dao;
import java.util.List;
import java.util.Map;
import com.cloud.cpu.CPU;
import com.cloud.hypervisor.Hypervisor.HypervisorType;
import com.cloud.storage.Storage;
import com.cloud.storage.VMTemplateVO;
@ -73,9 +74,13 @@ public interface VMTemplateDao extends GenericDao<VMTemplateVO, Long>, StateDao<
VMTemplateVO findSystemVMReadyTemplate(long zoneId, HypervisorType hypervisorType);
List<VMTemplateVO> findSystemVMReadyTemplates(long zoneId, HypervisorType hypervisorType, String preferredArch);
VMTemplateVO findRoutingTemplate(HypervisorType type, String templateName);
VMTemplateVO findLatestTemplateByTypeAndHypervisor(HypervisorType hypervisorType, Storage.TemplateType type);
List<VMTemplateVO> findRoutingTemplates(HypervisorType type, String templateName, String preferredArch);
VMTemplateVO findLatestTemplateByTypeAndHypervisorAndArch(HypervisorType hypervisorType, CPU.CPUArch arch, Storage.TemplateType type);
public Long countTemplatesForAccount(long accountId);
@ -87,7 +92,7 @@ public interface VMTemplateDao extends GenericDao<VMTemplateVO, Long>, StateDao<
List<VMTemplateVO> listByParentTemplatetId(long parentTemplatetId);
VMTemplateVO findLatestTemplateByName(String name);
VMTemplateVO findLatestTemplateByName(String name, CPU.CPUArch arch);
List<VMTemplateVO> findTemplatesLinkedToUserdata(long userdataId);

View File

@ -18,7 +18,9 @@ package com.cloud.storage.dao;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@ -28,8 +30,10 @@ import javax.naming.ConfigurationException;
import org.apache.cloudstack.storage.datastore.db.TemplateDataStoreDao;
import org.apache.cloudstack.storage.datastore.db.TemplateDataStoreVO;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Component;
import com.cloud.cpu.CPU;
import com.cloud.dc.dao.DataCenterDao;
import com.cloud.domain.dao.DomainDao;
import com.cloud.host.Host;
@ -47,6 +51,7 @@ import com.cloud.storage.VMTemplateZoneVO;
import com.cloud.tags.ResourceTagVO;
import com.cloud.tags.dao.ResourceTagDao;
import com.cloud.template.VirtualMachineTemplate;
import com.cloud.utils.Pair;
import com.cloud.utils.db.DB;
import com.cloud.utils.db.Filter;
import com.cloud.utils.db.GenericDaoBase;
@ -111,6 +116,7 @@ public class VMTemplateDaoImpl extends GenericDaoBase<VMTemplateVO, Long> implem
LatestTemplateByHypervisorTypeSearch = createSearchBuilder();
LatestTemplateByHypervisorTypeSearch.and("hypervisorType", LatestTemplateByHypervisorTypeSearch.entity().getHypervisorType(), SearchCriteria.Op.EQ);
LatestTemplateByHypervisorTypeSearch.and("templateType", LatestTemplateByHypervisorTypeSearch.entity().getTemplateType(), SearchCriteria.Op.EQ);
LatestTemplateByHypervisorTypeSearch.and("arch", LatestTemplateByHypervisorTypeSearch.entity().getArch(), SearchCriteria.Op.EQ);
LatestTemplateByHypervisorTypeSearch.and("removed", LatestTemplateByHypervisorTypeSearch.entity().getRemoved(), SearchCriteria.Op.NULL);
}
@ -238,10 +244,16 @@ public class VMTemplateDaoImpl extends GenericDaoBase<VMTemplateVO, Long> implem
@Override
public VMTemplateVO findLatestTemplateByName(String name) {
SearchCriteria<VMTemplateVO> sc = createSearchCriteria();
sc.addAnd("name", SearchCriteria.Op.EQ, name);
sc.addAnd("removed", SearchCriteria.Op.NULL);
public VMTemplateVO findLatestTemplateByName(String name, CPU.CPUArch arch) {
SearchBuilder<VMTemplateVO> sb = createSearchBuilder();
sb.and("name", sb.entity().getName(), SearchCriteria.Op.EQ);
sb.and("arch", sb.entity().getArch(), SearchCriteria.Op.EQ);
sb.done();
SearchCriteria<VMTemplateVO> sc = sb.create();
sc.setParameters("name", name);
if (arch != null) {
sc.setParameters("arch", arch);
}
Filter filter = new Filter(VMTemplateVO.class, "id", false, null, 1L);
List<VMTemplateVO> templates = listBy(sc, filter);
if ((templates != null) && !templates.isEmpty()) {
@ -580,6 +592,59 @@ public class VMTemplateDaoImpl extends GenericDaoBase<VMTemplateVO, Long> implem
.orElse(null);
}
protected List<VMTemplateVO> getSortedTemplatesListWithPreferredArch(
Map<Pair<HypervisorType, CPU.CPUArch>, VMTemplateVO> uniqueTemplates, String preferredArch) {
List<VMTemplateVO> result = new ArrayList<>(uniqueTemplates.values());
if (StringUtils.isNotBlank(preferredArch)) {
result.sort((t1, t2) -> {
boolean t1Preferred = t1.getArch().getType().equalsIgnoreCase(preferredArch);
boolean t2Preferred = t2.getArch().getType().equalsIgnoreCase(preferredArch);
if (t1Preferred && !t2Preferred) {
return -1; // t1 comes before t2
} else if (!t1Preferred && t2Preferred) {
return 1; // t2 comes before t1
} else {
// Both are either preferred or not preferred; use template id as a secondary sorting key.
return Long.compare(t1.getId(), t2.getId());
}
});
} else {
result.sort(Comparator.comparing(VMTemplateVO::getId).reversed());
}
return result;
}
@Override
public List<VMTemplateVO> findSystemVMReadyTemplates(long zoneId, HypervisorType hypervisorType,
String preferredArch) {
List<Pair<HypervisorType, CPU.CPUArch>> availableHypervisors = _hostDao.listDistinctHypervisorArchTypes(zoneId);
if (CollectionUtils.isEmpty(availableHypervisors)) {
return Collections.emptyList();
}
SearchCriteria<VMTemplateVO> sc = readySystemTemplateSearch.create();
sc.setParameters("templateType", Storage.TemplateType.SYSTEM);
sc.setParameters("state", VirtualMachineTemplate.State.Active);
if (hypervisorType != null && !HypervisorType.Any.equals(hypervisorType)) {
sc.setParameters("hypervisorType", List.of(hypervisorType).toArray());
} else {
sc.setParameters("hypervisorType",
availableHypervisors.stream().map(Pair::first).distinct().toArray());
}
sc.setJoinParameters("vmTemplateJoinTemplateStoreRef", "downloadState",
List.of(VMTemplateStorageResourceAssoc.Status.DOWNLOADED,
VMTemplateStorageResourceAssoc.Status.BYPASSED).toArray());
// order by descending order of id
List<VMTemplateVO> templates = listBy(sc, new Filter(VMTemplateVO.class, "id", false, null, null));
Map<Pair<HypervisorType, CPU.CPUArch>, VMTemplateVO> uniqueTemplates = new HashMap<>();
for (VMTemplateVO template : templates) {
Pair<HypervisorType, CPU.CPUArch> key = new Pair<>(template.getHypervisorType(), template.getArch());
if (availableHypervisors.contains(key) && !uniqueTemplates.containsKey(key)) {
uniqueTemplates.put(key, template);
}
}
return getSortedTemplatesListWithPreferredArch(uniqueTemplates, preferredArch);
}
@Override
public VMTemplateVO findRoutingTemplate(HypervisorType hType, String templateName) {
SearchCriteria<VMTemplateVO> sc = tmpltTypeHyperSearch2.create();
@ -618,10 +683,43 @@ public class VMTemplateDaoImpl extends GenericDaoBase<VMTemplateVO, Long> implem
}
@Override
public VMTemplateVO findLatestTemplateByTypeAndHypervisor(HypervisorType hypervisorType, TemplateType type) {
public List<VMTemplateVO> findRoutingTemplates(HypervisorType hType, String templateName, String preferredArch) {
SearchCriteria<VMTemplateVO> sc = tmpltTypeHyperSearch2.create();
sc.setParameters("templateType", TemplateType.ROUTING);
sc.setParameters("hypervisorType", hType);
sc.setParameters("state", VirtualMachineTemplate.State.Active.toString());
if (templateName != null) {
sc.setParameters("templateName", templateName);
}
List<VMTemplateVO> templates = listBy(sc, new Filter(VMTemplateVO.class, "id", false, null, 1L));
if (CollectionUtils.isEmpty(templates)) {
sc = tmpltTypeHyperSearch2.create();
sc.setParameters("templateType", TemplateType.SYSTEM);
sc.setParameters("hypervisorType", hType);
sc.setParameters("state", VirtualMachineTemplate.State.Active.toString());
if (templateName != null) {
sc.setParameters("templateName", templateName);
}
templates = listBy(sc, new Filter(VMTemplateVO.class, "id", false, null, 1L));
}
Map<Pair<HypervisorType, CPU.CPUArch>, VMTemplateVO> uniqueTemplates = new HashMap<>();
for (VMTemplateVO template : templates) {
Pair<HypervisorType, CPU.CPUArch> key = new Pair<>(template.getHypervisorType(), template.getArch());
if (!uniqueTemplates.containsKey(key)) {
uniqueTemplates.put(key, template);
}
}
return getSortedTemplatesListWithPreferredArch(uniqueTemplates, preferredArch);
}
@Override
public VMTemplateVO findLatestTemplateByTypeAndHypervisorAndArch(HypervisorType hypervisorType, CPU.CPUArch arch, TemplateType type) {
SearchCriteria<VMTemplateVO> sc = LatestTemplateByHypervisorTypeSearch.create();
sc.setParameters("hypervisorType", hypervisorType);
sc.setParameters("templateType", type);
if (arch != null) {
sc.setParameters("arch", arch);
}
Filter filter = new Filter(VMTemplateVO.class, "id", false, null, 1L);
List<VMTemplateVO> templates = listBy(sc, filter);
if (templates != null && !templates.isEmpty()) {

View File

@ -16,6 +16,47 @@
// under the License.
package com.cloud.upgrade;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.net.URI;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.sql.Connection;
import java.sql.Date;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Objects;
import java.util.UUID;
import java.util.stream.Collectors;
import javax.inject.Inject;
import org.apache.cloudstack.engine.subsystem.api.storage.ObjectInDataStoreStateMachine;
import org.apache.cloudstack.framework.config.dao.ConfigurationDao;
import org.apache.cloudstack.framework.config.dao.ConfigurationDaoImpl;
import org.apache.cloudstack.framework.config.impl.ConfigurationVO;
import org.apache.cloudstack.storage.datastore.db.ImageStoreDao;
import org.apache.cloudstack.storage.datastore.db.ImageStoreDaoImpl;
import org.apache.cloudstack.storage.datastore.db.ImageStoreDetailsDao;
import org.apache.cloudstack.storage.datastore.db.ImageStoreDetailsDaoImpl;
import org.apache.cloudstack.storage.datastore.db.ImageStoreVO;
import org.apache.cloudstack.storage.datastore.db.TemplateDataStoreDao;
import org.apache.cloudstack.storage.datastore.db.TemplateDataStoreVO;
import org.apache.cloudstack.utils.security.DigestHelper;
import org.apache.commons.lang3.StringUtils;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.ini4j.Ini;
import com.cloud.cpu.CPU;
import com.cloud.dc.DataCenterVO;
import com.cloud.dc.dao.ClusterDao;
import com.cloud.dc.dao.ClusterDaoImpl;
@ -36,6 +77,7 @@ import com.cloud.template.VirtualMachineTemplate;
import com.cloud.upgrade.dao.BasicTemplateDataStoreDaoImpl;
import com.cloud.user.Account;
import com.cloud.utils.DateUtil;
import com.cloud.utils.HttpUtils;
import com.cloud.utils.Pair;
import com.cloud.utils.UriUtils;
import com.cloud.utils.db.GlobalLock;
@ -46,48 +88,10 @@ import com.cloud.utils.exception.CloudRuntimeException;
import com.cloud.utils.script.Script;
import com.cloud.vm.dao.VMInstanceDao;
import com.cloud.vm.dao.VMInstanceDaoImpl;
import org.apache.cloudstack.engine.subsystem.api.storage.ObjectInDataStoreStateMachine;
import org.apache.cloudstack.framework.config.dao.ConfigurationDao;
import org.apache.cloudstack.framework.config.dao.ConfigurationDaoImpl;
import org.apache.cloudstack.framework.config.impl.ConfigurationVO;
import org.apache.cloudstack.storage.datastore.db.ImageStoreDao;
import org.apache.cloudstack.storage.datastore.db.ImageStoreDaoImpl;
import org.apache.cloudstack.storage.datastore.db.ImageStoreDetailsDao;
import org.apache.cloudstack.storage.datastore.db.ImageStoreDetailsDaoImpl;
import org.apache.cloudstack.storage.datastore.db.ImageStoreVO;
import org.apache.cloudstack.storage.datastore.db.TemplateDataStoreDao;
import org.apache.cloudstack.storage.datastore.db.TemplateDataStoreVO;
import org.apache.cloudstack.utils.security.DigestHelper;
import org.apache.commons.lang3.StringUtils;
import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.LogManager;
import org.ini4j.Ini;
import javax.inject.Inject;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.net.URI;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.sql.Connection;
import java.sql.Date;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
import java.util.stream.Collectors;
public class SystemVmTemplateRegistration {
protected static Logger LOGGER = LogManager.getLogger(SystemVmTemplateRegistration.class);
private static final String MOUNT_COMMAND = "sudo mount -t nfs %s %s";
private static final String MOUNT_COMMAND_BASE = "sudo mount -t nfs";
private static final String UMOUNT_COMMAND = "sudo umount %s";
private static final String RELATIVE_TEMPLATE_PATH = "./engine/schema/dist/systemvm-templates/";
private static final String ABSOLUTE_TEMPLATE_PATH = "/usr/share/cloudstack-management/templates/systemvm/";
@ -102,6 +106,9 @@ public class SystemVmTemplateRegistration {
private static final Integer LINUX_7_ID = 183;
private static final Integer SCRIPT_TIMEOUT = 1800000;
private static final Integer LOCK_WAIT_TIMEOUT = 1200;
protected static final List<CPU.CPUArch> DOWNLOADABLE_TEMPLATE_ARCH_TYPES = Arrays.asList(
CPU.CPUArch.arm64
);
public static String CS_MAJOR_VERSION = null;
@ -128,6 +135,8 @@ public class SystemVmTemplateRegistration {
private String systemVmTemplateVersion;
private final File tempDownloadDir;
public SystemVmTemplateRegistration() {
dataCenterDao = new DataCenterDaoImpl();
vmTemplateDao = new VMTemplateDaoImpl();
@ -138,6 +147,7 @@ public class SystemVmTemplateRegistration {
imageStoreDetailsDao = new ImageStoreDetailsDaoImpl();
clusterDao = new ClusterDaoImpl();
configurationDao = new ConfigurationDaoImpl();
tempDownloadDir = new File(System.getProperty("java.io.tmpdir"));
}
/**
@ -149,7 +159,7 @@ public class SystemVmTemplateRegistration {
}
public static String getMountCommand(String nfsVersion, String device, String dir) {
String cmd = "sudo mount -t nfs";
String cmd = MOUNT_COMMAND_BASE;
if (StringUtils.isNotBlank(nfsVersion)) {
cmd = String.format("%s -o vers=%s", cmd, nfsVersion);
}
@ -163,6 +173,10 @@ public class SystemVmTemplateRegistration {
return systemVmTemplateVersion;
}
public File getTempDownloadDir() {
return tempDownloadDir;
}
private static class SystemVMTemplateDetails {
Long id;
String uuid;
@ -174,6 +188,7 @@ public class SystemVmTemplateRegistration {
ImageFormat format;
Integer guestOsId;
Hypervisor.HypervisorType hypervisorType;
CPU.CPUArch arch;
Long storeId;
Long size;
Long physicalSize;
@ -183,7 +198,7 @@ public class SystemVmTemplateRegistration {
SystemVMTemplateDetails(String uuid, String name, Date created, String url, String checksum,
ImageFormat format, Integer guestOsId, Hypervisor.HypervisorType hypervisorType,
Long storeId) {
CPU.CPUArch arch, Long storeId) {
this.uuid = uuid;
this.name = name;
this.created = created;
@ -192,6 +207,7 @@ public class SystemVmTemplateRegistration {
this.format = format;
this.guestOsId = guestOsId;
this.hypervisorType = hypervisorType;
this.arch = arch;
this.storeId = storeId;
}
@ -235,6 +251,10 @@ public class SystemVmTemplateRegistration {
return hypervisorType;
}
public CPU.CPUArch getArch() {
return arch;
}
public Long getStoreId() {
return storeId;
}
@ -288,18 +308,17 @@ public class SystemVmTemplateRegistration {
}
}
public static final List<Hypervisor.HypervisorType> hypervisorList = Arrays.asList(Hypervisor.HypervisorType.KVM,
Hypervisor.HypervisorType.VMware,
Hypervisor.HypervisorType.XenServer,
Hypervisor.HypervisorType.Hyperv,
Hypervisor.HypervisorType.LXC,
Hypervisor.HypervisorType.Ovm3
public static final List<Pair<Hypervisor.HypervisorType, CPU.CPUArch>> hypervisorList = Arrays.asList(
new Pair<>(Hypervisor.HypervisorType.KVM, CPU.CPUArch.amd64),
new Pair<>(Hypervisor.HypervisorType.KVM, CPU.CPUArch.arm64),
new Pair<>(Hypervisor.HypervisorType.VMware, null),
new Pair<>(Hypervisor.HypervisorType.XenServer, null),
new Pair<>(Hypervisor.HypervisorType.Hyperv, null),
new Pair<>(Hypervisor.HypervisorType.LXC, null),
new Pair<>(Hypervisor.HypervisorType.Ovm3, null)
);
public static final Map<Hypervisor.HypervisorType, String> NewTemplateNameList = new HashMap<Hypervisor.HypervisorType, String>();
public static final Map<Hypervisor.HypervisorType, String> FileNames = new HashMap<Hypervisor.HypervisorType, String>();
public static final Map<Hypervisor.HypervisorType, String> NewTemplateUrl = new HashMap<Hypervisor.HypervisorType, String>();
public static final Map<Hypervisor.HypervisorType, String> NewTemplateChecksum = new HashMap<Hypervisor.HypervisorType, String>();
public static final Map<String, MetadataTemplateDetails> NewTemplateMap = new HashMap<>();
public static final Map<Hypervisor.HypervisorType, String> RouterTemplateConfigurationNames = new HashMap<Hypervisor.HypervisorType, String>() {
{
@ -368,55 +387,73 @@ public class SystemVmTemplateRegistration {
}
}
public Long getRegisteredTemplateId(Pair<Hypervisor.HypervisorType, String> hypervisorAndTemplateName) {
VMTemplateVO vmTemplate = vmTemplateDao.findLatestTemplateByName(hypervisorAndTemplateName.second());
Long templateId = null;
if (vmTemplate != null) {
templateId = vmTemplate.getId();
private static String getHypervisorArchLog(Hypervisor.HypervisorType hypervisorType, CPU.CPUArch arch) {
StringBuilder sb = new StringBuilder("hypervisor: ").append(hypervisorType.name());
if (Hypervisor.HypervisorType.KVM.equals(hypervisorType)) {
sb.append(", arch: ").append(arch == null ? CPU.CPUArch.amd64.getType() : arch.getType());
}
return templateId;
return sb.toString();
}
private static String fetchTemplatesPath() {
String filePath = RELATIVE_TEMPLATE_PATH + METADATA_FILE_NAME;
LOGGER.debug(String.format("Looking for file [ %s ] in the classpath.", filePath));
File metaFile = new File(filePath);
String templatePath = null;
if (metaFile.exists()) {
templatePath = RELATIVE_TEMPLATE_PATH;
protected static String getHypervisorArchKey(Hypervisor.HypervisorType hypervisorType, CPU.CPUArch arch) {
if (Hypervisor.HypervisorType.KVM.equals(hypervisorType)) {
return String.format("%s-%s", hypervisorType.name().toLowerCase(),
arch == null ? CPU.CPUArch.amd64.getType() : arch.getType());
}
if (templatePath == null) {
filePath = ABSOLUTE_TEMPLATE_PATH + METADATA_FILE_NAME;
metaFile = new File(filePath);
templatePath = ABSOLUTE_TEMPLATE_PATH;
LOGGER.debug(String.format("Looking for file [ %s ] in the classpath.", filePath));
if (!metaFile.exists()) {
String errMsg = String.format("Unable to locate metadata file in your setup at %s", filePath.toString());
LOGGER.error(errMsg);
throw new CloudRuntimeException(errMsg);
return hypervisorType.name().toLowerCase();
}
protected static MetadataTemplateDetails getMetadataTemplateDetails(Hypervisor.HypervisorType hypervisorType,
CPU.CPUArch arch) {
return NewTemplateMap.get(getHypervisorArchKey(hypervisorType, arch));
}
public VMTemplateVO getRegisteredTemplate(String templateName, CPU.CPUArch arch) {
return vmTemplateDao.findLatestTemplateByName(templateName, arch);
}
private static boolean isRunningInTest() {
return "true".equalsIgnoreCase(System.getProperty("test.mode"));
}
/**
* Attempts to determine the templates directory path by locating the metadata file.
* <p>
* This method checks if the application is running in a test environment by invoking
* {@code isRunningInTest()}. If so, it immediately returns the {@code RELATIVE_TEMPLATE_PATH}.
* </p>
* <p>
* Otherwise, it creates a list of candidate paths (typically including both relative and absolute
* template paths) and iterates through them. For each candidate, it constructs the metadata file
* path by appending {@code METADATA_FILE_NAME} to {@code RELATIVE_TEMPLATE_PATH} (note: the candidate
* path is not used in the file path construction in this implementation) and checks if that file exists.
* If the metadata file exists, the candidate path is returned.
* </p>
* <p>
* If none of the candidate paths contain the metadata file, the method logs an error and throws a
* {@link CloudRuntimeException}.
* </p>
*
* @return the path to the templates directory if the metadata file is found, or {@code RELATIVE_TEMPLATE_PATH}
* when running in a test environment.
* @throws CloudRuntimeException if the metadata file cannot be located in any of the candidate paths.
*/
private static String fetchTemplatesPath() {
if (isRunningInTest()) {
return RELATIVE_TEMPLATE_PATH;
}
List<String> paths = Arrays.asList(RELATIVE_TEMPLATE_PATH, ABSOLUTE_TEMPLATE_PATH);
for (String path : paths) {
String filePath = path + METADATA_FILE_NAME;
LOGGER.debug("Looking for file [ {} ] in the classpath.", filePath);
File metaFile = new File(filePath);
if (metaFile.exists()) {
return path;
}
}
return templatePath;
}
private String getHypervisorName(String name) {
if (name.equals("xenserver")) {
return "xen";
}
if (name.equals("ovm3")) {
return "ovm";
}
return name;
}
private Hypervisor.HypervisorType getHypervisorType(String hypervisor) {
if (hypervisor.equalsIgnoreCase("xen")) {
hypervisor = "xenserver";
} else if (hypervisor.equalsIgnoreCase("ovm")) {
hypervisor = "ovm3";
}
return Hypervisor.HypervisorType.getType(hypervisor);
String errMsg = String.format("Unable to locate metadata file in your setup at %s", StringUtils.join(paths));
LOGGER.error(errMsg);
throw new CloudRuntimeException(errMsg);
}
private List<Long> getEligibleZoneIds() {
@ -430,28 +467,28 @@ public class SystemVmTemplateRegistration {
return zoneIds;
}
private Pair<String, Long> getNfsStoreInZone(Long zoneId) {
String url = null;
Long storeId = null;
protected Pair<String, Long> getNfsStoreInZone(Long zoneId) {
ImageStoreVO storeVO = imageStoreDao.findOneByZoneAndProtocol(zoneId, "nfs");
if (storeVO == null) {
String errMsg = String.format("Failed to fetch NFS store in zone = %s for SystemVM template registration", zoneId);
String errMsg = String.format("Failed to fetch NFS store in zone = %s for SystemVM template registration",
zoneId);
LOGGER.error(errMsg);
throw new CloudRuntimeException(errMsg);
}
url = storeVO.getUrl();
storeId = storeVO.getId();
String url = storeVO.getUrl();
Long storeId = storeVO.getId();
return new Pair<>(url, storeId);
}
public static void mountStore(String storeUrl, String path, String nfsVersion) {
try {
if (storeUrl != null) {
URI uri = new URI(UriUtils.encodeURIComponent(storeUrl));
String host = uri.getHost();
String mountPath = uri.getPath();
Script.runSimpleBashScript(getMountCommand(nfsVersion, host + ":" + mountPath, path));
if (storeUrl == null) {
return;
}
URI uri = new URI(UriUtils.encodeURIComponent(storeUrl));
String host = uri.getHost();
String mountPath = uri.getPath();
Script.runSimpleBashScript(getMountCommand(nfsVersion, host + ":" + mountPath, path));
} catch (Exception e) {
String msg = "NFS Store URL is not in the correct format";
LOGGER.error(msg, e);
@ -459,13 +496,6 @@ public class SystemVmTemplateRegistration {
}
}
private List<String> fetchAllHypervisors(Long zoneId) {
List<String> hypervisorList = new ArrayList<>();
List<Hypervisor.HypervisorType> hypervisorTypes = clusterDao.getAvailableHypervisorInZone(zoneId);
hypervisorList = hypervisorTypes.stream().distinct().map(Hypervisor.HypervisorType::name).collect(Collectors.toList());
return hypervisorList;
}
private VMTemplateVO createTemplateObjectInDB(SystemVMTemplateDetails details) {
Long templateId = vmTemplateDao.getNextInSequence(Long.class, "id");
VMTemplateVO template = new VMTemplateVO();
@ -486,6 +516,7 @@ public class SystemVmTemplateRegistration {
template.setGuestOSId(details.getGuestOsId());
template.setCrossZones(true);
template.setHypervisorType(details.getHypervisorType());
template.setArch(details.getArch());
template.setState(VirtualMachineTemplate.State.Inactive);
template.setDeployAsIs(false);
template = vmTemplateDao.persist(template);
@ -627,16 +658,20 @@ public class SystemVmTemplateRegistration {
}
}
private void setupTemplate(String templateName, Pair<Hypervisor.HypervisorType, String> hypervisorAndTemplateName,
String destTempFolder) throws CloudRuntimeException {
private void setupTemplate(String templateName, Hypervisor.HypervisorType hypervisor, CPU.CPUArch arch,
String destTempFolder) throws CloudRuntimeException {
String setupTmpltScript = Script.findScript(storageScriptsDir, "setup-sysvm-tmplt");
if (setupTmpltScript == null) {
throw new CloudRuntimeException("Unable to find the createtmplt.sh");
}
Script scr = new Script(setupTmpltScript, SCRIPT_TIMEOUT, LOGGER);
scr.add("-u", templateName);
scr.add("-f", TEMPLATES_PATH + FileNames.get(hypervisorAndTemplateName.first()));
scr.add("-h", hypervisorAndTemplateName.first().name().toLowerCase(Locale.ROOT));
MetadataTemplateDetails templateDetails = NewTemplateMap.get(getHypervisorArchKey(hypervisor, arch));
String filePath = StringUtils.isNotBlank(templateDetails.getDownloadedFilePath()) ?
templateDetails.getDownloadedFilePath() :
templateDetails.getDefaultFilePath();
scr.add("-f", filePath);
scr.add("-h", hypervisor.name().toLowerCase(Locale.ROOT));
scr.add("-d", destTempFolder);
String result = scr.execute();
if (result != null) {
@ -644,17 +679,15 @@ public class SystemVmTemplateRegistration {
LOGGER.error(errMsg);
throw new CloudRuntimeException(errMsg);
}
}
private Long performTemplateRegistrationOperations(Pair<Hypervisor.HypervisorType, String> hypervisorAndTemplateName,
String url, String checksum, ImageFormat format, long guestOsId,
Long storeId, Long templateId, String filePath, TemplateDataStoreVO templateDataStoreVO) {
Hypervisor.HypervisorType hypervisor = hypervisorAndTemplateName.first();
private Long performTemplateRegistrationOperations(Hypervisor.HypervisorType hypervisor,
String name, CPU.CPUArch arch, String url, String checksum, ImageFormat format, long guestOsId,
Long storeId, Long templateId, String filePath, TemplateDataStoreVO templateDataStoreVO) {
String templateName = UUID.randomUUID().toString();
Date created = new Date(DateUtil.currentGMTTime().getTime());
SystemVMTemplateDetails details = new SystemVMTemplateDetails(templateName, hypervisorAndTemplateName.second(), created,
url, checksum, format, (int) guestOsId, hypervisor, storeId);
SystemVMTemplateDetails details = new SystemVMTemplateDetails(templateName, name, created,
url, checksum, format, (int) guestOsId, hypervisor, arch, storeId);
if (templateId == null) {
VMTemplateVO template = createTemplateObjectInDB(details);
if (template == null) {
@ -671,23 +704,44 @@ public class SystemVmTemplateRegistration {
if (templateDataStoreVO == null) {
createTemplateStoreRefEntry(details);
}
setupTemplate(templateName, hypervisorAndTemplateName, destTempFolder);
setupTemplate(templateName, hypervisor, arch, destTempFolder);
readTemplateProperties(destTempFolder + "/template.properties", details);
details.setUpdated(new Date(DateUtil.currentGMTTime().getTime()));
updateTemplateDetails(details);
return templateId;
}
public void registerTemplate(Pair<Hypervisor.HypervisorType, String> hypervisorAndTemplateName,
Pair<String, Long> storeUrlAndId, VMTemplateVO templateVO,
TemplateDataStoreVO templateDataStoreVO, String filePath) {
public void registerTemplate(Hypervisor.HypervisorType hypervisor, String name, Long storeId,
VMTemplateVO templateVO, TemplateDataStoreVO templateDataStoreVO, String filePath) {
try {
performTemplateRegistrationOperations(hypervisor, name, templateVO.getArch(), templateVO.getUrl(),
templateVO.getChecksum(), templateVO.getFormat(), templateVO.getGuestOSId(), storeId,
templateVO.getId(), filePath, templateDataStoreVO);
} catch (Exception e) {
String errMsg = String.format("Failed to register template for hypervisor: %s", hypervisor);
LOGGER.error(errMsg, e);
updateTemplateTablesOnFailure(templateVO.getId());
cleanupStore(templateVO.getId(), filePath);
throw new CloudRuntimeException(errMsg, e);
}
}
public void registerTemplateForNonExistingEntries(Hypervisor.HypervisorType hypervisor, CPU.CPUArch arch,
String name, Pair<String, Long> storeUrlAndId, String filePath) {
Long templateId = null;
try {
templateId = templateVO.getId();
performTemplateRegistrationOperations(hypervisorAndTemplateName, templateVO.getUrl(), templateVO.getChecksum(),
templateVO.getFormat(), templateVO.getGuestOSId(), storeUrlAndId.second(), templateId, filePath, templateDataStoreVO);
MetadataTemplateDetails templateDetails = getMetadataTemplateDetails(hypervisor, arch);
templateId = performTemplateRegistrationOperations(hypervisor, name,
templateDetails.getArch(), templateDetails.getUrl(),
templateDetails.getChecksum(), hypervisorImageFormat.get(hypervisor),
hypervisorGuestOsMap.get(hypervisor), storeUrlAndId.second(), null, filePath, null);
Map<String, String> configParams = new HashMap<>();
configParams.put(RouterTemplateConfigurationNames.get(hypervisor), templateDetails.getName());
configParams.put("minreq.sysvmtemplate.version", getSystemVmTemplateVersion());
updateConfigurationParams(configParams);
updateSystemVMEntries(templateId, hypervisor);
} catch (Exception e) {
String errMsg = String.format("Failed to register template for hypervisor: %s", hypervisorAndTemplateName.first());
String errMsg = String.format("Failed to register template for hypervisor: %s", hypervisor);
LOGGER.error(errMsg, e);
if (templateId != null) {
updateTemplateTablesOnFailure(templateId);
@ -697,26 +751,31 @@ public class SystemVmTemplateRegistration {
}
}
public void registerTemplate(Pair<Hypervisor.HypervisorType, String> hypervisorAndTemplateName, Pair<String, Long> storeUrlAndId, String filePath) {
Long templateId = null;
try {
Hypervisor.HypervisorType hypervisor = hypervisorAndTemplateName.first();
templateId = performTemplateRegistrationOperations(hypervisorAndTemplateName, NewTemplateUrl.get(hypervisor), NewTemplateChecksum.get(hypervisor),
hypervisorImageFormat.get(hypervisor), hypervisorGuestOsMap.get(hypervisor), storeUrlAndId.second(), null, filePath, null);
Map<String, String> configParams = new HashMap<>();
configParams.put(RouterTemplateConfigurationNames.get(hypervisorAndTemplateName.first()), hypervisorAndTemplateName.second());
configParams.put("minreq.sysvmtemplate.version", getSystemVmTemplateVersion());
updateConfigurationParams(configParams);
updateSystemVMEntries(templateId, hypervisorAndTemplateName.first());
} catch (Exception e) {
String errMsg = String.format("Failed to register template for hypervisor: %s", hypervisorAndTemplateName.first());
LOGGER.error(errMsg, e);
if (templateId != null) {
updateTemplateTablesOnFailure(templateId);
cleanupStore(templateId, filePath);
}
throw new CloudRuntimeException(errMsg, e);
protected void validateTemplateFileForHypervisorAndArch(Hypervisor.HypervisorType hypervisor, CPU.CPUArch arch) {
MetadataTemplateDetails templateDetails = getMetadataTemplateDetails(hypervisor, arch);
File templateFile = getTemplateFile(templateDetails);
if (templateFile == null) {
throw new CloudRuntimeException("Failed to find local template file");
}
if (isTemplateFileChecksumDifferent(templateDetails, templateFile)) {
throw new CloudRuntimeException("Checksum failed for local template file");
}
}
public void validateAndRegisterTemplate(Hypervisor.HypervisorType hypervisor, String name, Long storeId,
VMTemplateVO templateVO, TemplateDataStoreVO templateDataStoreVO, String filePath) {
validateTemplateFileForHypervisorAndArch(hypervisor, templateVO.getArch());
registerTemplate(hypervisor, name, storeId, templateVO, templateDataStoreVO, filePath);
}
public void validateAndRegisterTemplateForNonExistingEntries(Hypervisor.HypervisorType hypervisor,
CPU.CPUArch arch, String name, Pair<String, Long> storeUrlAndId, String filePath) {
validateTemplateFileForHypervisorAndArch(hypervisor, arch);
registerTemplateForNonExistingEntries(hypervisor, arch, name, storeUrlAndId, filePath);
}
protected static String getMetadataFilePath() {
return METADATA_FILE;
}
/**
@ -726,26 +785,41 @@ public class SystemVmTemplateRegistration {
* exist a template corresponding to the current code version.
*/
public static String parseMetadataFile() {
try {
Ini ini = new Ini();
ini.load(new FileReader(METADATA_FILE));
for (Hypervisor.HypervisorType hypervisorType : hypervisorList) {
String hypervisor = hypervisorType.name().toLowerCase(Locale.ROOT);
Ini.Section section = ini.get(hypervisor);
NewTemplateNameList.put(hypervisorType, section.get("templatename"));
FileNames.put(hypervisorType, section.get("filename"));
NewTemplateChecksum.put(hypervisorType, section.get("checksum"));
NewTemplateUrl.put(hypervisorType, section.get("downloadurl"));
}
Ini.Section section = ini.get("default");
return section.get("version");
} catch (Exception e) {
String errMsg = String.format("Failed to parse systemVM template metadata file: %s", METADATA_FILE);
String metadataFilePath = getMetadataFilePath();
String errMsg = String.format("Failed to parse systemVM template metadata file: %s", metadataFilePath);
final Ini ini = new Ini();
try (FileReader reader = new FileReader(metadataFilePath)) {
ini.load(reader);
} catch (IOException e) {
LOGGER.error(errMsg, e);
throw new CloudRuntimeException(errMsg, e);
}
if (!ini.containsKey("default")) {
errMsg = String.format("%s as unable to default section", errMsg);
LOGGER.error(errMsg);
throw new CloudRuntimeException(errMsg);
}
for (Pair<Hypervisor.HypervisorType, CPU.CPUArch> hypervisorType : hypervisorList) {
String key = getHypervisorArchKey(hypervisorType.first(), hypervisorType.second());
Ini.Section section = ini.get(key);
if (section == null) {
LOGGER.error("Failed to find details for {} in template metadata file: {}",
key, metadataFilePath);
continue;
}
NewTemplateMap.put(key, new MetadataTemplateDetails(
hypervisorType.first(),
section.get("templatename"),
section.get("filename"),
section.get("downloadurl"),
section.get("checksum"),
hypervisorType.second()));
}
Ini.Section defaultSection = ini.get("default");
return defaultSection.get("version").trim();
}
private static void cleanupStore(Long templateId, String filePath) {
String destTempFolder = filePath + PARTIAL_TEMPLATE_FOLDER + String.valueOf(templateId);
try {
@ -755,31 +829,60 @@ public class SystemVmTemplateRegistration {
}
}
private void validateTemplates(Set<Hypervisor.HypervisorType> hypervisorsInUse) {
Set<String> hypervisors = hypervisorsInUse.stream().
map(Hypervisor.HypervisorType::name).map(name -> name.toLowerCase(Locale.ROOT)).map(this::getHypervisorName).collect(Collectors.toSet());
List<String> templates = new ArrayList<>();
for (Hypervisor.HypervisorType hypervisorType : hypervisorsInUse) {
templates.add(FileNames.get(hypervisorType));
protected File getTemplateFile(MetadataTemplateDetails templateDetails) {
File templateFile = new File(templateDetails.getDefaultFilePath());
if (templateFile.exists()) {
return templateFile;
}
LOGGER.debug("{} is not present", templateFile.getAbsolutePath());
if (DOWNLOADABLE_TEMPLATE_ARCH_TYPES.contains(templateDetails.getArch()) &&
StringUtils.isNotBlank(templateDetails.getUrl())) {
LOGGER.debug("Downloading the template file {} for {}",
templateDetails.getUrl(), templateDetails.getHypervisorArchLog());
Path path = Path.of(TEMPLATES_PATH);
if (!Files.isWritable(path)) {
templateFile = new File(tempDownloadDir, templateDetails.getFilename());
}
if (!templateFile.exists() &&
!HttpUtils.downloadFileWithProgress(templateDetails.getUrl(), templateFile.getAbsolutePath(),
LOGGER)) {
return null;
}
templateDetails.setDownloadedFilePath(templateFile.getAbsolutePath());
}
return templateFile;
}
protected boolean isTemplateFileChecksumDifferent(MetadataTemplateDetails templateDetails, File templateFile) {
String templateChecksum = DigestHelper.calculateChecksum(templateFile);
if (!templateChecksum.equals(templateDetails.getChecksum())) {
LOGGER.error("Checksum {} for file {} does not match checksum {} from metadata",
templateChecksum, templateFile, templateDetails.getChecksum());
return true;
}
return false;
}
protected void validateTemplates(List<Pair<Hypervisor.HypervisorType, CPU.CPUArch>> hypervisorsArchInUse) {
boolean templatesFound = true;
for (String hypervisor : hypervisors) {
String matchedTemplate = templates.stream().filter(x -> x.contains(hypervisor)).findAny().orElse(null);
for (Pair<Hypervisor.HypervisorType, CPU.CPUArch> hypervisorArch : hypervisorsArchInUse) {
MetadataTemplateDetails matchedTemplate = getMetadataTemplateDetails(hypervisorArch.first(),
hypervisorArch.second());
if (matchedTemplate == null) {
templatesFound = false;
break;
}
File tempFile = new File(TEMPLATES_PATH + matchedTemplate);
String templateChecksum = DigestHelper.calculateChecksum(tempFile);
if (!templateChecksum.equals(NewTemplateChecksum.get(getHypervisorType(hypervisor)))) {
LOGGER.error(String.format("Checksum mismatch: %s != %s ", templateChecksum, NewTemplateChecksum.get(getHypervisorType(hypervisor))));
File tempFile = getTemplateFile(matchedTemplate);
if (tempFile == null) {
LOGGER.warn("Failed to download template for {}, moving ahead",
matchedTemplate.getHypervisorArchLog());
continue;
}
if (isTemplateFileChecksumDifferent(matchedTemplate, tempFile)) {
templatesFound = false;
break;
}
}
if (!templatesFound) {
String errMsg = "SystemVm template not found. Cannot upgrade system Vms";
LOGGER.error(errMsg);
@ -787,7 +890,40 @@ public class SystemVmTemplateRegistration {
}
}
public void registerTemplates(Set<Hypervisor.HypervisorType> hypervisorsInUse) {
protected void registerTemplatesForZone(long zoneId, String filePath) {
Pair<String, Long> storeUrlAndId = getNfsStoreInZone(zoneId);
String nfsVersion = getNfsVersion(storeUrlAndId.second());
mountStore(storeUrlAndId.first(), filePath, nfsVersion);
List<Pair<Hypervisor.HypervisorType, CPU.CPUArch>> hypervisorArchList =
clusterDao.listDistinctHypervisorsArchAcrossClusters(zoneId);
for (Pair<Hypervisor.HypervisorType, CPU.CPUArch> hypervisorArch : hypervisorArchList) {
Hypervisor.HypervisorType hypervisorType = hypervisorArch.first();
MetadataTemplateDetails templateDetails = getMetadataTemplateDetails(hypervisorType,
hypervisorArch.second());
if (templateDetails == null) {
continue;
}
VMTemplateVO templateVO = getRegisteredTemplate(templateDetails.getName(), templateDetails.getArch());
if (templateVO != null) {
TemplateDataStoreVO templateDataStoreVO =
templateDataStoreDao.findByStoreTemplate(storeUrlAndId.second(), templateVO.getId());
if (templateDataStoreVO != null) {
String installPath = templateDataStoreVO.getInstallPath();
if (validateIfSeeded(templateDataStoreVO, storeUrlAndId.first(), installPath, nfsVersion)) {
continue;
}
}
registerTemplate(hypervisorType, templateDetails.getName(), storeUrlAndId.second(), templateVO,
templateDataStoreVO, filePath);
updateRegisteredTemplateDetails(templateVO.getId(), templateDetails);
continue;
}
registerTemplateForNonExistingEntries(hypervisorType, templateDetails.getArch(), templateDetails.getName(),
storeUrlAndId, filePath);
}
}
public void registerTemplates(List<Pair<Hypervisor.HypervisorType, CPU.CPUArch>> hypervisorsArchInUse) {
GlobalLock lock = GlobalLock.getInternLock("UpgradeDatabase-Lock");
try {
LOGGER.info("Grabbing lock to register templates.");
@ -795,7 +931,7 @@ public class SystemVmTemplateRegistration {
throw new CloudRuntimeException("Unable to acquire lock to register SystemVM template.");
}
try {
validateTemplates(hypervisorsInUse);
validateTemplates(hypervisorsArchInUse);
// Perform Registration if templates not already registered
Transaction.execute(new TransactionCallbackNoReturn() {
@Override
@ -808,32 +944,7 @@ public class SystemVmTemplateRegistration {
if (filePath == null) {
throw new CloudRuntimeException("Failed to create temporary file path to mount the store");
}
Pair<String, Long> storeUrlAndId = getNfsStoreInZone(zoneId);
String nfsVersion = getNfsVersion(storeUrlAndId.second());
mountStore(storeUrlAndId.first(), filePath, nfsVersion);
List<String> hypervisorList = fetchAllHypervisors(zoneId);
for (String hypervisor : hypervisorList) {
Hypervisor.HypervisorType name = Hypervisor.HypervisorType.getType(hypervisor);
String templateName = NewTemplateNameList.get(name);
Pair<Hypervisor.HypervisorType, String> hypervisorAndTemplateName = new Pair<Hypervisor.HypervisorType, String>(name, templateName);
Long templateId = getRegisteredTemplateId(hypervisorAndTemplateName);
if (templateId != null) {
VMTemplateVO templateVO = vmTemplateDao.findById(templateId);
TemplateDataStoreVO templateDataStoreVO = templateDataStoreDao.findByStoreTemplate(storeUrlAndId.second(), templateId);
if (templateDataStoreVO != null) {
String installPath = templateDataStoreVO.getInstallPath();
if (validateIfSeeded(templateDataStoreVO, storeUrlAndId.first(), installPath, nfsVersion)) {
continue;
}
}
if (templateVO != null) {
registerTemplate(hypervisorAndTemplateName, storeUrlAndId, templateVO, templateDataStoreVO, filePath);
updateRegisteredTemplateDetails(templateId, hypervisorAndTemplateName);
continue;
}
}
registerTemplate(hypervisorAndTemplateName, storeUrlAndId, filePath);
}
registerTemplatesForZone(zoneId, filePath);
unmountStore(filePath);
} catch (Exception e) {
unmountStore(filePath);
@ -851,12 +962,7 @@ public class SystemVmTemplateRegistration {
}
}
private void updateRegisteredTemplateDetails(Long templateId, Map.Entry<Hypervisor.HypervisorType, String> hypervisorAndTemplateName) {
Pair<Hypervisor.HypervisorType, String> entry = new Pair<>(hypervisorAndTemplateName.getKey(), hypervisorAndTemplateName.getValue());
updateRegisteredTemplateDetails(templateId, entry);
}
private void updateRegisteredTemplateDetails(Long templateId, Pair<Hypervisor.HypervisorType, String> hypervisorAndTemplateName) {
private void updateRegisteredTemplateDetails(Long templateId, MetadataTemplateDetails templateDetails) {
VMTemplateVO templateVO = vmTemplateDao.findById(templateId);
templateVO.setTemplateType(Storage.TemplateType.SYSTEM);
boolean updated = vmTemplateDao.update(templateVO.getId(), templateVO);
@ -865,68 +971,81 @@ public class SystemVmTemplateRegistration {
LOGGER.error(errMsg);
throw new CloudRuntimeException(errMsg);
}
updateSystemVMEntries(templateId, hypervisorAndTemplateName.first());
Hypervisor.HypervisorType hypervisorType = templateDetails.getHypervisorType();
updateSystemVMEntries(templateId, hypervisorType);
// Change value of global configuration parameter router.template.* for the corresponding hypervisor and minreq.sysvmtemplate.version for the ACS version
Map<String, String> configParams = new HashMap<>();
configParams.put(RouterTemplateConfigurationNames.get(hypervisorAndTemplateName.first()), hypervisorAndTemplateName.second());
configParams.put(RouterTemplateConfigurationNames.get(hypervisorType), templateDetails.getName());
configParams.put("minreq.sysvmtemplate.version", getSystemVmTemplateVersion());
updateConfigurationParams(configParams);
}
private void updateTemplateUrlAndChecksum(VMTemplateVO templateVO, Map.Entry<Hypervisor.HypervisorType, String> hypervisorAndTemplateName) {
templateVO.setUrl(NewTemplateUrl.get(hypervisorAndTemplateName.getKey()));
templateVO.setChecksum(NewTemplateChecksum.get(hypervisorAndTemplateName.getKey()));
private void updateTemplateUrlAndChecksum(VMTemplateVO templateVO, MetadataTemplateDetails templateDetails) {
templateVO.setUrl(templateDetails.getUrl());
templateVO.setChecksum(templateDetails.getChecksum());
boolean updated = vmTemplateDao.update(templateVO.getId(), templateVO);
if (!updated) {
String errMsg = String.format("updateSystemVmTemplates:Exception while updating 'url' and 'checksum' for hypervisor type %s", hypervisorAndTemplateName.getKey().name());
String errMsg = String.format("updateSystemVmTemplates:Exception while updating 'url' and 'checksum' for hypervisor type %s", templateDetails.getHypervisorType());
LOGGER.error(errMsg);
throw new CloudRuntimeException(errMsg);
}
}
protected boolean registerOrUpdateSystemVmTemplate(MetadataTemplateDetails templateDetails,
List<Pair<Hypervisor.HypervisorType, CPU.CPUArch>> hypervisorsInUse) {
LOGGER.debug("Updating System VM template for {}", templateDetails.getHypervisorArchLog());
VMTemplateVO registeredTemplate = getRegisteredTemplate(templateDetails.getName(), templateDetails.getArch());
// change template type to SYSTEM
if (registeredTemplate != null) {
updateRegisteredTemplateDetails(registeredTemplate.getId(), templateDetails);
} else {
boolean isHypervisorArchMatchMetadata = hypervisorsInUse.stream()
.anyMatch(p -> p.first().equals(templateDetails.getHypervisorType())
&& Objects.equals(p.second(), templateDetails.getArch()));
if (isHypervisorArchMatchMetadata) {
try {
registerTemplates(hypervisorsInUse);
return true;
} catch (final Exception e) {
throw new CloudRuntimeException(String.format("Failed to register %s templates for hypervisors: [%s]. " +
"Cannot upgrade system VMs",
getSystemVmTemplateVersion(),
StringUtils.join(hypervisorsInUse.stream()
.map(x -> getHypervisorArchKey(x.first(), x.second()))
.collect(Collectors.toList()), ",")), e);
}
} else {
LOGGER.warn("Cannot upgrade {} system VM template for {} as it is not used, not failing upgrade",
getSystemVmTemplateVersion(), templateDetails.getHypervisorArchLog());
VMTemplateVO templateVO = vmTemplateDao.findLatestTemplateByTypeAndHypervisorAndArch(
templateDetails.getHypervisorType(), templateDetails.getArch(), Storage.TemplateType.SYSTEM);
if (templateVO != null) {
updateTemplateUrlAndChecksum(templateVO, templateDetails);
}
}
}
return false;
}
public void updateSystemVmTemplates(final Connection conn) {
LOGGER.debug("Updating System Vm template IDs");
Transaction.execute(new TransactionCallbackNoReturn() {
@Override
public void doInTransactionWithoutResult(final TransactionStatus status) {
Set<Hypervisor.HypervisorType> hypervisorsListInUse = new HashSet<Hypervisor.HypervisorType>();
List<Pair<Hypervisor.HypervisorType, CPU.CPUArch>> hypervisorsInUse;
try {
hypervisorsListInUse = clusterDao.getDistinctAvailableHypervisorsAcrossClusters();
hypervisorsInUse = clusterDao.listDistinctHypervisorsArchAcrossClusters(null);
} catch (final Exception e) {
LOGGER.error("updateSystemVmTemplates: Exception caught while getting hypervisor types from clusters: " + e.getMessage());
throw new CloudRuntimeException("updateSystemVmTemplates:Exception while getting hypervisor types from clusters", e);
throw new CloudRuntimeException("Exception while getting hypervisor types from clusters", e);
}
for (final Map.Entry<Hypervisor.HypervisorType, String> hypervisorAndTemplateName : NewTemplateNameList.entrySet()) {
LOGGER.debug("Updating " + hypervisorAndTemplateName.getKey() + " System Vms");
Long templateId = getRegisteredTemplateId(new Pair<>(hypervisorAndTemplateName.getKey(), hypervisorAndTemplateName.getValue()));
Collection<MetadataTemplateDetails> templateEntries = NewTemplateMap.values();
for (MetadataTemplateDetails templateDetails : templateEntries) {
try {
// change template type to SYSTEM
if (templateId != null) {
updateRegisteredTemplateDetails(templateId, hypervisorAndTemplateName);
} else {
if (hypervisorsListInUse.contains(hypervisorAndTemplateName.getKey())) {
try {
registerTemplates(hypervisorsListInUse);
break;
} catch (final Exception e) {
throw new CloudRuntimeException(String.format("%s %s SystemVm template not found. Cannot upgrade system Vms", getSystemVmTemplateVersion(), hypervisorAndTemplateName.getKey()));
}
} else {
LOGGER.warn(String.format("%s %s SystemVm template not found. Cannot upgrade system Vms hypervisor is not used, so not failing upgrade",
getSystemVmTemplateVersion(), hypervisorAndTemplateName.getKey()));
// Update the latest template URLs for corresponding hypervisor
VMTemplateVO templateVO = vmTemplateDao.findLatestTemplateByTypeAndHypervisor(hypervisorAndTemplateName.getKey(), Storage.TemplateType.SYSTEM);
if (templateVO != null) {
updateTemplateUrlAndChecksum(templateVO, hypervisorAndTemplateName);
}
}
if (registerOrUpdateSystemVmTemplate(templateDetails, hypervisorsInUse)) {
break;
}
} catch (final Exception e) {
String errMsg = "updateSystemVmTemplates:Exception while getting ids of templates";
String errMsg = "Exception while registering/updating system VM templates for hypervisors in metadata";
LOGGER.error(errMsg, e);
throw new CloudRuntimeException(errMsg, e);
}
@ -948,4 +1067,64 @@ public class SystemVmTemplateRegistration {
}
return null;
}
protected static class MetadataTemplateDetails {
private final Hypervisor.HypervisorType hypervisorType;
private final String name;
private final String filename;
private final String url;
private final String checksum;
private final CPU.CPUArch arch;
private String downloadedFilePath;
MetadataTemplateDetails(Hypervisor.HypervisorType hypervisorType, String name, String filename, String url,
String checksum, CPU.CPUArch arch) {
this.hypervisorType = hypervisorType;
this.name = name;
this.filename = filename;
this.url = url;
this.checksum = checksum;
this.arch = arch;
}
public Hypervisor.HypervisorType getHypervisorType() {
return hypervisorType;
}
public String getName() {
return name;
}
public String getFilename() {
return filename;
}
public String getUrl() {
return url;
}
public String getChecksum() {
return checksum;
}
public CPU.CPUArch getArch() {
return arch;
}
public String getDownloadedFilePath() {
return downloadedFilePath;
}
public void setDownloadedFilePath(String downloadedFilePath) {
this.downloadedFilePath = downloadedFilePath;
}
public String getDefaultFilePath() {
return TEMPLATES_PATH + filename;
}
public String getHypervisorArchLog() {
return SystemVmTemplateRegistration.getHypervisorArchLog(hypervisorType, arch);
}
}
}

View File

@ -78,7 +78,7 @@ public class Upgrade42000to42010 extends DbUpgradeAbstractImpl implements DbUpgr
try {
systemVmTemplateRegistration.updateSystemVmTemplates(conn);
} catch (Exception e) {
throw new CloudRuntimeException("Failed to find / register SystemVM template(s)");
throw new CloudRuntimeException("Failed to find / register SystemVM template(s)", e);
}
}

View File

@ -44,7 +44,7 @@ public interface ImageStoreDao extends GenericDao<ImageStoreVO, Long> {
List<ImageStoreVO> listStoresByZoneId(long zoneId);
List<ImageStoreVO> listAllStoresInZone(Long zoneId, String provider, DataStoreRole role);
List<ImageStoreVO> listAllStoresInZoneExceptId(Long zoneId, String provider, DataStoreRole role, long storeId);
List<ImageStoreVO> findByProtocol(String protocol);

View File

@ -41,7 +41,7 @@ public class ImageStoreDaoImpl extends GenericDaoBase<ImageStoreVO, Long> implem
private SearchBuilder<ImageStoreVO> nameSearch;
private SearchBuilder<ImageStoreVO> providerSearch;
private SearchBuilder<ImageStoreVO> regionSearch;
private SearchBuilder<ImageStoreVO> storeSearch;
private SearchBuilder<ImageStoreVO> storesExceptIdSearch;
private SearchBuilder<ImageStoreVO> protocolSearch;
private SearchBuilder<ImageStoreVO> zoneProtocolSearch;
@ -88,11 +88,12 @@ public class ImageStoreDaoImpl extends GenericDaoBase<ImageStoreVO, Long> implem
regionSearch.and("role", regionSearch.entity().getRole(), SearchCriteria.Op.EQ);
regionSearch.done();
storeSearch = createSearchBuilder();
storeSearch.and("providerName", storeSearch.entity().getProviderName(), SearchCriteria.Op.EQ);
storeSearch.and("role", storeSearch.entity().getRole(), SearchCriteria.Op.EQ);
storeSearch.and("dataCenterId", storeSearch.entity().getDcId(), SearchCriteria.Op.EQ);
storeSearch.done();
storesExceptIdSearch = createSearchBuilder();
storesExceptIdSearch.and("providerName", storesExceptIdSearch.entity().getProviderName(), SearchCriteria.Op.EQ);
storesExceptIdSearch.and("role", storesExceptIdSearch.entity().getRole(), SearchCriteria.Op.EQ);
storesExceptIdSearch.and("dataCenterId", storesExceptIdSearch.entity().getDcId(), SearchCriteria.Op.EQ);
storesExceptIdSearch.and("id", storesExceptIdSearch.entity().getId(), SearchCriteria.Op.NEQ);
storesExceptIdSearch.done();
return true;
}
@ -113,11 +114,12 @@ public class ImageStoreDaoImpl extends GenericDaoBase<ImageStoreVO, Long> implem
}
@Override
public List<ImageStoreVO> listAllStoresInZone(Long zoneId, String provider, DataStoreRole role) {
SearchCriteria<ImageStoreVO> sc = storeSearch.create();
public List<ImageStoreVO> listAllStoresInZoneExceptId(Long zoneId, String provider, DataStoreRole role, long id) {
SearchCriteria<ImageStoreVO> sc = storesExceptIdSearch.create();
sc.setParameters("providerName", provider);
sc.setParameters("role", role);
sc.setParameters("dataCenterId", zoneId);
sc.setParameters("id", id);
return listBy(sc);
}

View File

@ -21,7 +21,7 @@ import com.cloud.cpu.CPU;
import javax.persistence.AttributeConverter;
import javax.persistence.Converter;
@Converter
@Converter(autoApply = true)
public class CPUArchConverter implements AttributeConverter<CPU.CPUArch, String> {
@Override

View File

@ -58,6 +58,7 @@ select
host.resource_state host_resource_state,
vm_template.id template_id,
vm_template.uuid template_uuid,
vm_template.arch arch,
service_offering.id service_offering_id,
service_offering.uuid service_offering_uuid,
service_offering.name service_offering_name,

View File

@ -83,6 +83,7 @@ SELECT
`iso`.`uuid` AS `iso_uuid`,
`iso`.`name` AS `iso_name`,
`iso`.`display_text` AS `iso_display_text`,
`vm_template`.`arch` AS `arch`,
`service_offering`.`id` AS `service_offering_id`,
`service_offering`.`uuid` AS `service_offering_uuid`,
`disk_offering`.`uuid` AS `disk_offering_uuid`,

View File

@ -17,6 +17,7 @@
package com.cloud.dc.dao;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.any;
import static org.mockito.Mockito.doReturn;
@ -36,9 +37,13 @@ import org.mockito.InjectMocks;
import org.mockito.Spy;
import org.mockito.junit.MockitoJUnitRunner;
import com.cloud.cpu.CPU;
import com.cloud.dc.ClusterVO;
import com.cloud.hypervisor.Hypervisor;
import com.cloud.utils.Pair;
import com.cloud.utils.db.GenericSearchBuilder;
import com.cloud.utils.db.SearchBuilder;
import com.cloud.utils.db.SearchCriteria;
@RunWith(MockitoJUnitRunner.class)
public class ClusterDaoImplTest {
@ -75,4 +80,39 @@ public class ClusterDaoImplTest {
verify(clusterDao).customSearch(genericSearchBuilder.create(), null);
assertTrue(result.isEmpty());
}
@Test
public void listDistinctHypervisorsArchAcrossClusters_WithZone() {
Long zoneId = 123L;
ClusterVO cluster1 = mock(ClusterVO.class);
when(cluster1.getHypervisorType()).thenReturn(Hypervisor.HypervisorType.XenServer);
when(cluster1.getArch()).thenReturn(CPU.CPUArch.amd64);
ClusterVO cluster2 = mock(ClusterVO.class);
when(cluster2.getHypervisorType()).thenReturn(Hypervisor.HypervisorType.KVM);
when(cluster2.getArch()).thenReturn(CPU.CPUArch.arm64);
List<ClusterVO> dummyHosts = Arrays.asList(cluster1, cluster2);
doReturn(dummyHosts).when(clusterDao).search(any(SearchCriteria.class), isNull());
List<Pair<Hypervisor.HypervisorType, CPU.CPUArch>> result = clusterDao.listDistinctHypervisorsArchAcrossClusters(zoneId);
assertNotNull(result);
assertEquals(2, result.size());
assertEquals(Hypervisor.HypervisorType.XenServer, result.get(0).first());
assertEquals(CPU.CPUArch.amd64, result.get(0).second());
assertEquals(Hypervisor.HypervisorType.KVM, result.get(1).first());
assertEquals(CPU.CPUArch.arm64, result.get(1).second());
}
@Test
public void listDistinctHypervisorsArchAcrossClusters_WithoutZone() {
Long zoneId = null;
ClusterVO cluster = mock(ClusterVO.class);
when(cluster.getHypervisorType()).thenReturn(Hypervisor.HypervisorType.VMware);
when(cluster.getArch()).thenReturn(CPU.CPUArch.amd64);
List<ClusterVO> dummyHosts = Collections.singletonList(cluster);
doReturn(dummyHosts).when(clusterDao).search(any(SearchCriteria.class), isNull());
List<Pair<Hypervisor.HypervisorType, CPU.CPUArch>> result = clusterDao.listDistinctHypervisorsArchAcrossClusters(zoneId);
assertNotNull(result);
assertEquals(1, result.size());
assertEquals(Hypervisor.HypervisorType.VMware, result.get(0).first());
assertEquals(CPU.CPUArch.amd64, result.get(0).second());
}
}

View File

@ -16,7 +16,18 @@
// under the License.
package com.cloud.host.dao;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.isNull;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;
import org.junit.Assert;
import org.junit.Test;
@ -26,6 +37,7 @@ import org.mockito.Mockito;
import org.mockito.Spy;
import org.mockito.junit.MockitoJUnitRunner;
import com.cloud.cpu.CPU;
import com.cloud.host.Host;
import com.cloud.host.HostVO;
import com.cloud.host.Status;
@ -52,10 +64,10 @@ public class HostDaoImplTest {
public void testCountUpAndEnabledHostsInZone() {
long testZoneId = 100L;
hostDao.HostTypeCountSearch = mockSearchBuilder;
Mockito.when(mockSearchBuilder.create()).thenReturn(mockSearchCriteria);
Mockito.doNothing().when(mockSearchCriteria).setParameters(Mockito.anyString(), Mockito.any());
when(mockSearchBuilder.create()).thenReturn(mockSearchCriteria);
Mockito.doNothing().when(mockSearchCriteria).setParameters(Mockito.anyString(), any());
int expected = 5;
Mockito.doReturn(expected).when(hostDao).getCount(mockSearchCriteria);
doReturn(expected).when(hostDao).getCount(mockSearchCriteria);
Integer count = hostDao.countUpAndEnabledHostsInZone(testZoneId);
Assert.assertSame(expected, count);
Mockito.verify(mockSearchCriteria).setParameters("type", Host.Type.Routing);
@ -70,16 +82,16 @@ public class HostDaoImplTest {
GenericDaoBase.SumCount mockSumCount = new GenericDaoBase.SumCount();
mockSumCount.count = 10;
mockSumCount.sum = 20;
HostVO host = Mockito.mock(HostVO.class);
GenericSearchBuilder<HostVO, GenericDaoBase.SumCount> sb = Mockito.mock(GenericSearchBuilder.class);
Mockito.when(sb.entity()).thenReturn(host);
Mockito.doReturn(sb).when(hostDao).createSearchBuilder(GenericDaoBase.SumCount.class);
SearchCriteria<GenericDaoBase.SumCount> sc = Mockito.mock(SearchCriteria.class);
Mockito.when(sb.create()).thenReturn(sc);
Mockito.doReturn(List.of(mockSumCount)).when(hostDao).customSearch(Mockito.any(SearchCriteria.class), Mockito.any());
HostVO host = mock(HostVO.class);
GenericSearchBuilder<HostVO, GenericDaoBase.SumCount> sb = mock(GenericSearchBuilder.class);
when(sb.entity()).thenReturn(host);
doReturn(sb).when(hostDao).createSearchBuilder(GenericDaoBase.SumCount.class);
SearchCriteria<GenericDaoBase.SumCount> sc = mock(SearchCriteria.class);
when(sb.create()).thenReturn(sc);
doReturn(List.of(mockSumCount)).when(hostDao).customSearch(any(SearchCriteria.class), any());
Pair<Integer, Integer> result = hostDao.countAllHostsAndCPUSocketsByType(type);
Assert.assertEquals(10, result.first().intValue());
Assert.assertEquals(20, result.second().intValue());
assertEquals(10, result.first().intValue());
assertEquals(20, result.second().intValue());
Mockito.verify(sc).setParameters("type", type);
}
@ -87,13 +99,13 @@ public class HostDaoImplTest {
public void testIsHostUp() {
long testHostId = 101L;
List<Status> statuses = List.of(Status.Up);
HostVO host = Mockito.mock(HostVO.class);
GenericSearchBuilder<HostVO, Status> sb = Mockito.mock(GenericSearchBuilder.class);
Mockito.when(sb.entity()).thenReturn(host);
SearchCriteria<Status> sc = Mockito.mock(SearchCriteria.class);
Mockito.when(sb.create()).thenReturn(sc);
Mockito.doReturn(sb).when(hostDao).createSearchBuilder(Status.class);
Mockito.doReturn(statuses).when(hostDao).customSearch(Mockito.any(SearchCriteria.class), Mockito.any());
HostVO host = mock(HostVO.class);
GenericSearchBuilder<HostVO, Status> sb = mock(GenericSearchBuilder.class);
when(sb.entity()).thenReturn(host);
SearchCriteria<Status> sc = mock(SearchCriteria.class);
when(sb.create()).thenReturn(sc);
doReturn(sb).when(hostDao).createSearchBuilder(Status.class);
doReturn(statuses).when(hostDao).customSearch(any(SearchCriteria.class), any());
boolean result = hostDao.isHostUp(testHostId);
Assert.assertTrue("Host should be up", result);
Mockito.verify(sc).setParameters("id", testHostId);
@ -108,17 +120,17 @@ public class HostDaoImplTest {
List<Host.Type> types = List.of(Host.Type.Routing);
List<Hypervisor.HypervisorType> hypervisorTypes = List.of(Hypervisor.HypervisorType.KVM);
List<Long> mockResults = List.of(1001L, 1002L); // Mocked result
HostVO host = Mockito.mock(HostVO.class);
GenericSearchBuilder<HostVO, Long> sb = Mockito.mock(GenericSearchBuilder.class);
Mockito.when(sb.entity()).thenReturn(host);
SearchCriteria<Long> sc = Mockito.mock(SearchCriteria.class);
Mockito.when(sb.create()).thenReturn(sc);
Mockito.when(sb.and()).thenReturn(sb);
Mockito.doReturn(sb).when(hostDao).createSearchBuilder(Long.class);
Mockito.doReturn(mockResults).when(hostDao).customSearch(Mockito.any(SearchCriteria.class), Mockito.any());
HostVO host = mock(HostVO.class);
GenericSearchBuilder<HostVO, Long> sb = mock(GenericSearchBuilder.class);
when(sb.entity()).thenReturn(host);
SearchCriteria<Long> sc = mock(SearchCriteria.class);
when(sb.create()).thenReturn(sc);
when(sb.and()).thenReturn(sb);
doReturn(sb).when(hostDao).createSearchBuilder(Long.class);
doReturn(mockResults).when(hostDao).customSearch(any(SearchCriteria.class), any());
List<Long> hostIds = hostDao.findHostIdsByZoneClusterResourceStateTypeAndHypervisorType(
zoneId, clusterId, resourceStates, types, hypervisorTypes);
Assert.assertEquals(mockResults, hostIds);
assertEquals(mockResults, hostIds);
Mockito.verify(sc).setParameters("zoneId", zoneId);
Mockito.verify(sc).setParameters("clusterId", clusterId);
Mockito.verify(sc).setParameters("resourceState", resourceStates.toArray());
@ -130,15 +142,16 @@ public class HostDaoImplTest {
public void testListDistinctHypervisorTypes() {
Long zoneId = 1L;
List<Hypervisor.HypervisorType> mockResults = List.of(Hypervisor.HypervisorType.KVM, Hypervisor.HypervisorType.XenServer);
HostVO host = Mockito.mock(HostVO.class);
GenericSearchBuilder<HostVO, Hypervisor.HypervisorType> sb = Mockito.mock(GenericSearchBuilder.class);
Mockito.when(sb.entity()).thenReturn(host);
SearchCriteria<Hypervisor.HypervisorType> sc = Mockito.mock(SearchCriteria.class);
Mockito.when(sb.create()).thenReturn(sc);
Mockito.doReturn(sb).when(hostDao).createSearchBuilder(Hypervisor.HypervisorType.class);
Mockito.doReturn(mockResults).when(hostDao).customSearch(Mockito.any(SearchCriteria.class), Mockito.any());
HostVO host = mock(HostVO.class);
GenericSearchBuilder<HostVO, String> sb = mock(GenericSearchBuilder.class);
when(sb.entity()).thenReturn(host);
SearchCriteria<String> sc = mock(SearchCriteria.class);
when(sb.create()).thenReturn(sc);
doReturn(sb).when(hostDao).createSearchBuilder(String.class);
doReturn(mockResults.stream().map(h -> h.name()).collect(Collectors.toList())).when(hostDao)
.customSearch(any(SearchCriteria.class), any());
List<Hypervisor.HypervisorType> hypervisorTypes = hostDao.listDistinctHypervisorTypes(zoneId);
Assert.assertEquals(mockResults, hypervisorTypes);
assertEquals(mockResults, hypervisorTypes);
Mockito.verify(sc).setParameters("zoneId", zoneId);
Mockito.verify(sc).setParameters("type", Host.Type.Routing);
}
@ -146,12 +159,12 @@ public class HostDaoImplTest {
@Test
public void testListByIds() {
List<Long> ids = List.of(101L, 102L);
List<HostVO> mockResults = List.of(Mockito.mock(HostVO.class), Mockito.mock(HostVO.class));
List<HostVO> mockResults = List.of(mock(HostVO.class), mock(HostVO.class));
hostDao.IdsSearch = mockSearchBuilder;
Mockito.when(mockSearchBuilder.create()).thenReturn(mockSearchCriteria);
Mockito.doReturn(mockResults).when(hostDao).search(Mockito.any(SearchCriteria.class), Mockito.any());
when(mockSearchBuilder.create()).thenReturn(mockSearchCriteria);
doReturn(mockResults).when(hostDao).search(any(SearchCriteria.class), any());
List<HostVO> hosts = hostDao.listByIds(ids);
Assert.assertEquals(mockResults, hosts);
assertEquals(mockResults, hosts);
Mockito.verify(mockSearchCriteria).setParameters("id", ids.toArray());
Mockito.verify(hostDao).search(mockSearchCriteria, null);
}
@ -164,15 +177,15 @@ public class HostDaoImplTest {
Hypervisor.HypervisorType hypervisorType = Hypervisor.HypervisorType.KVM;
Long zoneId = 1L, podId = 2L, clusterId = 3L;
List<Long> mockResults = List.of(1001L, 1002L);
HostVO host = Mockito.mock(HostVO.class);
GenericSearchBuilder<HostVO, Long> sb = Mockito.mock(GenericSearchBuilder.class);
Mockito.when(sb.entity()).thenReturn(host);
SearchCriteria<Long> sc = Mockito.mock(SearchCriteria.class);
Mockito.when(sb.create()).thenReturn(sc);
Mockito.doReturn(sb).when(hostDao).createSearchBuilder(Long.class);
Mockito.doReturn(mockResults).when(hostDao).customSearch(Mockito.any(SearchCriteria.class), Mockito.any());
HostVO host = mock(HostVO.class);
GenericSearchBuilder<HostVO, Long> sb = mock(GenericSearchBuilder.class);
when(sb.entity()).thenReturn(host);
SearchCriteria<Long> sc = mock(SearchCriteria.class);
when(sb.create()).thenReturn(sc);
doReturn(sb).when(hostDao).createSearchBuilder(Long.class);
doReturn(mockResults).when(hostDao).customSearch(any(SearchCriteria.class), any());
List<Long> hostIds = hostDao.listIdsBy(type, status, resourceState, hypervisorType, zoneId, podId, clusterId);
Assert.assertEquals(mockResults, hostIds);
assertEquals(mockResults, hostIds);
Mockito.verify(sc).setParameters("type", type);
Mockito.verify(sc).setParameters("status", status);
Mockito.verify(sc).setParameters("resourceState", resourceState);
@ -181,4 +194,57 @@ public class HostDaoImplTest {
Mockito.verify(sc).setParameters("podId", podId);
Mockito.verify(sc).setParameters("clusterId", clusterId);
}
@Test
public void testListDistinctHypervisorArchTypes_WithZone() {
Long zoneId = 123L;
HostVO host1 = mock(HostVO.class);
when(host1.getHypervisorType()).thenReturn(Hypervisor.HypervisorType.XenServer);
when(host1.getArch()).thenReturn(CPU.CPUArch.amd64);
HostVO host2 = mock(HostVO.class);
when(host2.getHypervisorType()).thenReturn(Hypervisor.HypervisorType.KVM);
when(host2.getArch()).thenReturn(CPU.CPUArch.arm64);
List<HostVO> dummyHosts = Arrays.asList(host1, host2);
doReturn(dummyHosts).when(hostDao).search(any(SearchCriteria.class), isNull());
List<Pair<Hypervisor.HypervisorType, CPU.CPUArch>> result = hostDao.listDistinctHypervisorArchTypes(zoneId);
assertNotNull(result);
assertEquals(2, result.size());
assertEquals(Hypervisor.HypervisorType.XenServer, result.get(0).first());
assertEquals(CPU.CPUArch.amd64, result.get(0).second());
assertEquals(Hypervisor.HypervisorType.KVM, result.get(1).first());
assertEquals(CPU.CPUArch.arm64, result.get(1).second());
}
@Test
public void testListDistinctHypervisorArchTypes_WithoutZone() {
Long zoneId = null;
HostVO host1 = mock(HostVO.class);
when(host1.getHypervisorType()).thenReturn(Hypervisor.HypervisorType.VMware);
when(host1.getArch()).thenReturn(CPU.CPUArch.amd64);
List<HostVO> dummyHosts = Collections.singletonList(host1);
doReturn(dummyHosts).when(hostDao).search(any(SearchCriteria.class), isNull());
List<Pair<Hypervisor.HypervisorType, CPU.CPUArch>> result = hostDao.listDistinctHypervisorArchTypes(zoneId);
assertNotNull(result);
assertEquals(1, result.size());
assertEquals(Hypervisor.HypervisorType.VMware, result.get(0).first());
assertEquals(CPU.CPUArch.amd64, result.get(0).second());
}
@Test
public void testListDistinctArchTypes() {
Long clusterId = 1L;
List<CPU.CPUArch> mockResults = List.of(CPU.CPUArch.amd64, CPU.CPUArch.arm64);
HostVO host = mock(HostVO.class);
GenericSearchBuilder<HostVO, String> sb = mock(GenericSearchBuilder.class);
when(sb.entity()).thenReturn(host);
SearchCriteria<String> sc = mock(SearchCriteria.class);
when(sb.create()).thenReturn(sc);
doReturn(sb).when(hostDao).createSearchBuilder(String.class);
doReturn(mockResults.stream().map(h -> h.getType()).collect(Collectors.toList())).when(hostDao)
.customSearch(any(SearchCriteria.class), any());
List<CPU.CPUArch> hypervisorTypes = hostDao.listDistinctArchTypes(clusterId);
assertEquals(mockResults, hypervisorTypes);
Mockito.verify(sc).setParameters("clusterId", clusterId);
Mockito.verify(sc).setParameters("type", Host.Type.Routing);
}
}

View File

@ -0,0 +1,189 @@
// 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 com.cloud.storage.dao;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
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 com.cloud.cpu.CPU;
import com.cloud.host.dao.HostDao;
import com.cloud.hypervisor.Hypervisor;
import com.cloud.storage.Storage;
import com.cloud.storage.VMTemplateVO;
import com.cloud.utils.Pair;
import com.cloud.utils.db.Filter;
import com.cloud.utils.db.SearchBuilder;
import com.cloud.utils.db.SearchCriteria;
@RunWith(MockitoJUnitRunner.class)
public class VMTemplateDaoImplTest {
@Mock
HostDao hostDao;
@Spy
@InjectMocks
VMTemplateDaoImpl templateDao = new VMTemplateDaoImpl();
@Test
public void testFindLatestTemplateByName_ReturnsTemplate() {
VMTemplateVO expectedTemplate = new VMTemplateVO();
List<VMTemplateVO> returnedList = Collections.singletonList(expectedTemplate);
doReturn(returnedList).when(templateDao).listBy(any(SearchCriteria.class), any(Filter.class));
VMTemplateVO result = templateDao.findLatestTemplateByName("test", CPU.CPUArch.getDefault());
assertNotNull("Expected a non-null template", result);
assertEquals("Expected the returned template to be the first element", expectedTemplate, result);
}
@Test
public void testFindLatestTemplateByName_ReturnsNullWhenNoTemplateFound() {
List<VMTemplateVO> emptyList = Collections.emptyList();
doReturn(emptyList).when(templateDao).listBy(any(SearchCriteria.class), any(Filter.class));
VMTemplateVO result = templateDao.findLatestTemplateByName("test", CPU.CPUArch.getDefault());
assertNull("Expected null when no templates are found", result);
}
@Test
public void testFindLatestTemplateByName_NullArch() {
VMTemplateVO expectedTemplate = new VMTemplateVO();
List<VMTemplateVO> returnedList = Collections.singletonList(expectedTemplate);
doReturn(returnedList).when(templateDao).listBy(any(SearchCriteria.class), any(Filter.class));
VMTemplateVO result = templateDao.findLatestTemplateByName("test", null);
assertNotNull("Expected a non-null template even if arch is null", result);
assertEquals("Expected the returned template to be the first element", expectedTemplate, result);
}
@Test
public void testGetSortedTemplatesListWithPreferredArch_PreferredProvided() {
VMTemplateVO templatePreferred = Mockito.mock(VMTemplateVO.class);
when(templatePreferred.getArch()).thenReturn(CPU.CPUArch.amd64);
VMTemplateVO templateOther = Mockito.mock(VMTemplateVO.class);
when(templateOther.getArch()).thenReturn(CPU.CPUArch.arm64);
Map<Pair<Hypervisor.HypervisorType, CPU.CPUArch>, VMTemplateVO> uniqueTemplates = new HashMap<>();
uniqueTemplates.put(new Pair<>(Hypervisor.HypervisorType.KVM, CPU.CPUArch.amd64), templatePreferred);
uniqueTemplates.put(new Pair<>(Hypervisor.HypervisorType.KVM, CPU.CPUArch.arm64), templateOther);
List<VMTemplateVO> sortedList = templateDao.getSortedTemplatesListWithPreferredArch(uniqueTemplates,
CPU.CPUArch.amd64.getType());
assertEquals(2, sortedList.size());
assertEquals(templatePreferred, sortedList.get(0));
assertEquals(templateOther, sortedList.get(1));
}
@Test
public void testGetSortedTemplatesListWithPreferredArch_NoPreferred() {
VMTemplateVO template1 = Mockito.mock(VMTemplateVO.class);
when(template1.getId()).thenReturn(1L);
VMTemplateVO template2 = Mockito.mock(VMTemplateVO.class);
when(template2.getId()).thenReturn(2L);
Map<Pair<Hypervisor.HypervisorType, CPU.CPUArch>, VMTemplateVO> uniqueTemplates = new HashMap<>();
uniqueTemplates.put(new Pair<>(Hypervisor.HypervisorType.KVM, CPU.CPUArch.amd64), template1);
uniqueTemplates.put(new Pair<>(Hypervisor.HypervisorType.KVM, CPU.CPUArch.arm64), template2);
List<VMTemplateVO> sortedList = templateDao.getSortedTemplatesListWithPreferredArch(uniqueTemplates, "");
assertEquals(2, sortedList.size());
assertEquals(template2, sortedList.get(0));
assertEquals(template1, sortedList.get(1));
}
@Test
public void testFindSystemVMReadyTemplates() {
long zoneId = 1L;
Hypervisor.HypervisorType hypervisorType = Hypervisor.HypervisorType.KVM;
String preferredArch = CPU.CPUArch.arm64.getType();
List<Pair<Hypervisor.HypervisorType, CPU.CPUArch>> availableHypervisors = new ArrayList<>();
availableHypervisors.add(new Pair<>(Hypervisor.HypervisorType.KVM, CPU.CPUArch.amd64));
availableHypervisors.add(new Pair<>(Hypervisor.HypervisorType.KVM, CPU.CPUArch.arm64));
doReturn(availableHypervisors).when(hostDao).listDistinctHypervisorArchTypes(zoneId);
VMTemplateVO template1 = Mockito.mock(VMTemplateVO.class);
when(template1.getHypervisorType()).thenReturn(Hypervisor.HypervisorType.KVM);
when(template1.getArch()).thenReturn(CPU.CPUArch.amd64);
VMTemplateVO template2 = Mockito.mock(VMTemplateVO.class);
when(template2.getHypervisorType()).thenReturn(Hypervisor.HypervisorType.KVM);
when(template2.getArch()).thenReturn(CPU.CPUArch.arm64);
List<VMTemplateVO> templatesFromDb = Arrays.asList(template1, template2);
doReturn(templatesFromDb).when(templateDao).listBy(any(), any());
SearchBuilder<VMTemplateVO> sb = mock(SearchBuilder.class);
templateDao.readySystemTemplateSearch = sb;
when(sb.create()).thenReturn(mock(SearchCriteria.class));
List<VMTemplateVO> result = templateDao.findSystemVMReadyTemplates(zoneId, hypervisorType, preferredArch);
assertNotNull(result);
assertEquals(2, result.size());
assertEquals(template2, result.get(0));
assertEquals(template1, result.get(1));
}
@Test
public void testFindRoutingTemplates() {
Hypervisor.HypervisorType hType = Hypervisor.HypervisorType.KVM;
String templateName = "TestRouting";
String preferredArch = CPU.CPUArch.amd64.getType();
VMTemplateVO template = Mockito.mock(VMTemplateVO.class);
when(template.getArch()).thenReturn(CPU.CPUArch.amd64);
List<VMTemplateVO> templatesFromDb = Collections.singletonList(template);
doReturn(templatesFromDb).when(templateDao).listBy(any(), any());
SearchBuilder<VMTemplateVO> sb = mock(SearchBuilder.class);
when(sb.create()).thenReturn(mock(SearchCriteria.class));
templateDao.tmpltTypeHyperSearch2 = sb;
List<VMTemplateVO> result = templateDao.findRoutingTemplates(hType, templateName, preferredArch);
assertNotNull(result);
assertEquals(1, result.size());
assertEquals(template, result.get(0));
}
@Test
public void testFindLatestTemplateByTypeAndHypervisorAndArch_Found() {
Hypervisor.HypervisorType hypervisorType = Hypervisor.HypervisorType.KVM;
CPU.CPUArch arch = CPU.CPUArch.amd64;
Storage.TemplateType type = Storage.TemplateType.SYSTEM;
VMTemplateVO template = Mockito.mock(VMTemplateVO.class);
List<VMTemplateVO> templatesFromDb = Collections.singletonList(template);
doReturn(templatesFromDb).when(templateDao).listBy(any(), any());
VMTemplateVO result = templateDao.findLatestTemplateByTypeAndHypervisorAndArch(hypervisorType, arch, type);
assertNotNull(result);
assertEquals(template, result);
}
@Test
public void testFindLatestTemplateByTypeAndHypervisorAndArch_NotFound() {
Hypervisor.HypervisorType hypervisorType = Hypervisor.HypervisorType.KVM;
CPU.CPUArch arch = CPU.CPUArch.x86;
Storage.TemplateType type = Storage.TemplateType.SYSTEM;
doReturn(Collections.emptyList()).when(templateDao).listBy(any(), any());
VMTemplateVO result = templateDao.findLatestTemplateByTypeAndHypervisorAndArch(hypervisorType, arch, type);
assertNull(result);
}
}

View File

@ -0,0 +1,427 @@
// 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 com.cloud.upgrade;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertThrows;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import static org.mockito.ArgumentMatchers.any;
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.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import java.io.File;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.List;
import org.apache.cloudstack.storage.datastore.db.TemplateDataStoreVO;
import org.apache.cloudstack.utils.security.DigestHelper;
import org.apache.commons.lang3.StringUtils;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.MockedStatic;
import org.mockito.Mockito;
import org.mockito.Spy;
import org.mockito.junit.MockitoJUnitRunner;
import com.cloud.cpu.CPU;
import com.cloud.dc.dao.ClusterDao;
import com.cloud.hypervisor.Hypervisor;
import com.cloud.storage.VMTemplateVO;
import com.cloud.storage.dao.VMTemplateDao;
import com.cloud.utils.HttpUtils;
import com.cloud.utils.Pair;
import com.cloud.utils.UriUtils;
import com.cloud.utils.exception.CloudRuntimeException;
import com.cloud.utils.script.Script;
@RunWith(MockitoJUnitRunner.class)
public class SystemVmTemplateRegistrationTest {
@Mock
ClusterDao clusterDao;
@Mock
VMTemplateDao vmTemplateDao;
@Spy
@InjectMocks
SystemVmTemplateRegistration systemVmTemplateRegistration = new SystemVmTemplateRegistration();
private void setupMetadataFile(MockedStatic<SystemVmTemplateRegistration> mockedStatic, String content) {
try {
String location = "metadata.ini";
if (StringUtils.isNotBlank(content)) {
File tempFile = File.createTempFile("metadata", ".ini");
location = tempFile.getAbsolutePath();
Files.write(Paths.get(location), content.getBytes());
tempFile.deleteOnExit();
}
mockedStatic.when(SystemVmTemplateRegistration::getMetadataFilePath).thenReturn(location);
} catch (Exception e) {
fail(e.getMessage());
}
}
@Test
public void test_parseMetadataFile_noFile() {
try (MockedStatic<SystemVmTemplateRegistration> mockedStatic =
Mockito.mockStatic(SystemVmTemplateRegistration.class, Mockito.CALLS_REAL_METHODS)) {
setupMetadataFile(mockedStatic, null);
CloudRuntimeException exception = assertThrows(CloudRuntimeException.class,
SystemVmTemplateRegistration::parseMetadataFile);
assertTrue(exception.getMessage().contains("Failed to parse systemVM template metadata file"));
}
}
@Test
public void test_parseMetadataFile_invalidContent() {
try (MockedStatic<SystemVmTemplateRegistration> mockedStatic =
Mockito.mockStatic(SystemVmTemplateRegistration.class, Mockito.CALLS_REAL_METHODS)) {
setupMetadataFile(mockedStatic, "abc");
CloudRuntimeException exception = assertThrows(CloudRuntimeException.class,
SystemVmTemplateRegistration::parseMetadataFile);
assertTrue(exception.getMessage().contains("Failed to parse systemVM template metadata file"));
}
}
@Test
public void test_parseMetadataFile_success() {
String metadataFileContent = "[default]\n" +
"version = x.y.z.0\n" +
"\n" +
"[kvm-x86_64]\n" +
"templatename = systemvm-kvm-x.y.z\n" +
"checksum = abc1\n" +
"downloadurl = https://download.cloudstack.org/systemvm/x.y/systemvmtemplate-x.y.z-kvm.qcow2.bz2\n" +
"filename = systemvmtemplate-x.y.z-kvm.qcow2.bz2\n" +
"\n" +
"[kvm-aarch64]\n" +
"templatename = systemvm-kvm-x.y.z\n" +
"checksum = abc2\n" +
"downloadurl = https://download.cloudstack.org/systemvm/x.y/systemvmtemplate-x.y.z-kvm.qcow2.bz2\n" +
"filename = systemvmtemplate-x.y.z-kvm.qcow2.bz2\n" +
"\n" +
"[vmware]\n" +
"templatename = systemvm-vmware-x.y.z\n" +
"checksum = abc3\n" +
"downloadurl = https://download.cloudstack.org/systemvm/x.y/systemvmtemplate-x.y.z-vmware.ova\n" +
"filename = systemvmtemplate-x.y.z-vmware.ova\n";
try (MockedStatic<SystemVmTemplateRegistration> mockedStatic =
Mockito.mockStatic(SystemVmTemplateRegistration.class, Mockito.CALLS_REAL_METHODS)) {
setupMetadataFile(mockedStatic, metadataFileContent);
String version = SystemVmTemplateRegistration.parseMetadataFile();
assertEquals("x.y.z.0", version);
}
assertNull(SystemVmTemplateRegistration.NewTemplateMap.get("xenserver"));
SystemVmTemplateRegistration.MetadataTemplateDetails templateDetails =
SystemVmTemplateRegistration.NewTemplateMap.get("kvm-x86_64");
assertNotNull(templateDetails);
assertEquals(CPU.CPUArch.amd64, templateDetails.getArch());
assertEquals(Hypervisor.HypervisorType.KVM, templateDetails.getHypervisorType());
templateDetails =
SystemVmTemplateRegistration.NewTemplateMap.get("kvm-aarch64");
assertNotNull(templateDetails);
assertEquals(CPU.CPUArch.arm64, templateDetails.getArch());
assertEquals(Hypervisor.HypervisorType.KVM, templateDetails.getHypervisorType());
templateDetails =
SystemVmTemplateRegistration.NewTemplateMap.get("vmware");
assertNotNull(templateDetails);
assertNull(templateDetails.getArch());
assertEquals(Hypervisor.HypervisorType.VMware, templateDetails.getHypervisorType());
}
@Test
public void testMountStore_nullStoreUrl() throws Exception {
try (MockedStatic<Script> scriptMock = Mockito.mockStatic(Script.class)) {
SystemVmTemplateRegistration.mountStore(null, "/mnt/nfs", "nfs3");
scriptMock.verifyNoInteractions();
}
}
@Test
public void testMountStore_validStoreUrl() throws Exception {
String storeUrl = "nfs://192.168.1.100/export";
String path = "/mnt/nfs";
String nfsVersion = "nfs3";
String expectedMountCommand = "expectedMountCommand";
try (MockedStatic<UriUtils> uriUtilsMock = Mockito.mockStatic(UriUtils.class);
MockedStatic<SystemVmTemplateRegistration> sysVmMock =
Mockito.mockStatic(SystemVmTemplateRegistration.class, Mockito.CALLS_REAL_METHODS);
MockedStatic<Script> scriptMock = Mockito.mockStatic(Script.class)) {
uriUtilsMock.when(() -> UriUtils.encodeURIComponent(storeUrl)).thenReturn(storeUrl);
sysVmMock.when(() -> SystemVmTemplateRegistration.getMountCommand(
eq(nfsVersion),
eq("192.168.1.100:/export"),
eq(path)
)).thenReturn(expectedMountCommand);
SystemVmTemplateRegistration.mountStore(storeUrl, path, nfsVersion);
scriptMock.verify(() -> Script.runSimpleBashScript(expectedMountCommand), times(1));
}
}
@Test
public void testValidateTemplateFile_fileNotFound() {
SystemVmTemplateRegistration.MetadataTemplateDetails details =
new SystemVmTemplateRegistration.MetadataTemplateDetails(Hypervisor.HypervisorType.KVM,
"name", "file", "url", "checksum", CPU.CPUArch.amd64);
SystemVmTemplateRegistration.NewTemplateMap.put(SystemVmTemplateRegistration.getHypervisorArchKey(
details.getHypervisorType(), details.getArch()), details);
doReturn(null).when(systemVmTemplateRegistration).getTemplateFile(details);
try {
systemVmTemplateRegistration.validateTemplateFileForHypervisorAndArch(details.getHypervisorType(),
details.getArch());
fail("Expected CloudRuntimeException due to missing template file");
} catch (CloudRuntimeException e) {
assertEquals("Failed to find local template file", e.getMessage());
}
}
@Test
public void testValidateTemplateFile_checksumMismatch() {
SystemVmTemplateRegistration.MetadataTemplateDetails details =
new SystemVmTemplateRegistration.MetadataTemplateDetails(Hypervisor.HypervisorType.KVM,
"name", "file", "url", "checksum", CPU.CPUArch.amd64);
File dummyFile = new File("dummy.txt");
SystemVmTemplateRegistration.NewTemplateMap.put(SystemVmTemplateRegistration.getHypervisorArchKey(
details.getHypervisorType(), details.getArch()), details);
doReturn(dummyFile).when(systemVmTemplateRegistration).getTemplateFile(details);
doReturn(true).when(systemVmTemplateRegistration).isTemplateFileChecksumDifferent(details, dummyFile);
try {
systemVmTemplateRegistration.validateTemplateFileForHypervisorAndArch(details.getHypervisorType(),
details.getArch());
fail("Expected CloudRuntimeException due to checksum failure");
} catch (CloudRuntimeException e) {
assertEquals("Checksum failed for local template file", e.getMessage());
}
}
@Test
public void testValidateTemplateFile_success() {
SystemVmTemplateRegistration.MetadataTemplateDetails details =
new SystemVmTemplateRegistration.MetadataTemplateDetails(Hypervisor.HypervisorType.KVM,
"name", "file", "url", "checksum", CPU.CPUArch.amd64);
File dummyFile = new File("dummy.txt");
SystemVmTemplateRegistration.NewTemplateMap.put(SystemVmTemplateRegistration.getHypervisorArchKey(
details.getHypervisorType(), details.getArch()), details);
doReturn(dummyFile).when(systemVmTemplateRegistration).getTemplateFile(details);
doReturn(false).when(systemVmTemplateRegistration).isTemplateFileChecksumDifferent(details, dummyFile);
systemVmTemplateRegistration.validateTemplateFileForHypervisorAndArch(details.getHypervisorType(),
details.getArch());
}
@Test
public void testValidateAndRegisterTemplate() {
Hypervisor.HypervisorType hypervisor = Hypervisor.HypervisorType.KVM;
String name = "TestTemplate";
Long storeId = 123L;
VMTemplateVO templateVO = new VMTemplateVO();
templateVO.setArch(CPU.CPUArch.x86);
TemplateDataStoreVO templateDataStoreVO = new TemplateDataStoreVO();
String filePath = "/dummy/path";
doNothing().when(systemVmTemplateRegistration).validateTemplateFileForHypervisorAndArch(hypervisor, templateVO.getArch());
doNothing().when(systemVmTemplateRegistration).registerTemplate(hypervisor, name, storeId, templateVO, templateDataStoreVO, filePath);
systemVmTemplateRegistration.validateAndRegisterTemplate(hypervisor, name, storeId, templateVO, templateDataStoreVO, filePath);
verify(systemVmTemplateRegistration).validateTemplateFileForHypervisorAndArch(eq(hypervisor), eq(templateVO.getArch()));
verify(systemVmTemplateRegistration).registerTemplate(eq(hypervisor), eq(name), eq(storeId), eq(templateVO), eq(templateDataStoreVO), eq(filePath));
}
@Test
public void testValidateAndRegisterTemplateForNonExistingEntries() {
Hypervisor.HypervisorType hypervisor = Hypervisor.HypervisorType.KVM;
CPU.CPUArch arch = CPU.CPUArch.amd64;
String name = "TestTemplateNonExisting";
Pair<String, Long> storeUrlAndId = new Pair<>("nfs://dummy", 456L);
String filePath = "/dummy/path/nonexisting";
doNothing().when(systemVmTemplateRegistration).validateTemplateFileForHypervisorAndArch(hypervisor, arch);
doNothing().when(systemVmTemplateRegistration).registerTemplateForNonExistingEntries(hypervisor, arch, name, storeUrlAndId, filePath);
systemVmTemplateRegistration.validateAndRegisterTemplateForNonExistingEntries(hypervisor, arch, name, storeUrlAndId, filePath);
verify(systemVmTemplateRegistration).validateTemplateFileForHypervisorAndArch(eq(hypervisor), eq(arch));
verify(systemVmTemplateRegistration).registerTemplateForNonExistingEntries(eq(hypervisor), eq(arch), eq(name), eq(storeUrlAndId), eq(filePath));
}
@Test
public void testGetTemplateFile_fileExists() throws Exception {
File tempFile = File.createTempFile("template", ".qcow2");
tempFile.deleteOnExit();
SystemVmTemplateRegistration.MetadataTemplateDetails details =
Mockito.mock(SystemVmTemplateRegistration.MetadataTemplateDetails.class);
when(details.getDefaultFilePath()).thenReturn(tempFile.getAbsolutePath());
File result = systemVmTemplateRegistration.getTemplateFile(details);
assertNotNull(result);
assertEquals(tempFile.getAbsolutePath(), result.getAbsolutePath());
}
@Test
public void testGetTemplateFile_fileDoesNotExist_downloadFails() {
SystemVmTemplateRegistration.MetadataTemplateDetails details =
new SystemVmTemplateRegistration.MetadataTemplateDetails(Hypervisor.HypervisorType.KVM,
"name", "nonexistent.qcow2", "http://example.com/file.qcow2",
"", CPU.CPUArch.arm64);
try (MockedStatic<Files> filesMock = Mockito.mockStatic(Files.class);
MockedStatic<HttpUtils> httpMock = Mockito.mockStatic(HttpUtils.class)) {
filesMock.when(() -> Files.isWritable(any(Path.class))).thenReturn(true);
httpMock.when(() -> HttpUtils.downloadFileWithProgress(eq(details.getUrl()), anyString(), any()))
.thenReturn(false);
File result = systemVmTemplateRegistration.getTemplateFile(details);
assertNull(result);
}
}
@Test
public void testGetTemplateFile_fileDoesNotExist_downloadSucceeds() {
SystemVmTemplateRegistration.MetadataTemplateDetails details =
new SystemVmTemplateRegistration.MetadataTemplateDetails(Hypervisor.HypervisorType.KVM,
"name", "file.qcow2", "http://example.com/file.qcow2",
"", CPU.CPUArch.arm64);
try (MockedStatic<Files> filesMock = Mockito.mockStatic(Files.class);
MockedStatic<HttpUtils> httpMock = Mockito.mockStatic(HttpUtils.class)) {
filesMock.when(() -> Files.isWritable(any(Path.class))).thenReturn(false);
File expectedFile = new File(systemVmTemplateRegistration.getTempDownloadDir(), details.getFilename());
httpMock.when(() -> HttpUtils.downloadFileWithProgress(eq(details.getUrl()), eq(expectedFile.getAbsolutePath()), any()))
.thenReturn(true);
File result = systemVmTemplateRegistration.getTemplateFile(details);
assertNotNull(result);
assertEquals(expectedFile.getAbsolutePath(), result.getAbsolutePath());
assertEquals(expectedFile.getAbsolutePath(), details.getDownloadedFilePath());
}
}
@Test
public void testIsTemplateFileChecksumDifferent_noMismatch() {
SystemVmTemplateRegistration.MetadataTemplateDetails details =
Mockito.mock(SystemVmTemplateRegistration.MetadataTemplateDetails.class);
when(details.getChecksum()).thenReturn("dummyChecksum");
File file = new File("dummy.txt");
try (MockedStatic<DigestHelper> digestMock = Mockito.mockStatic(DigestHelper.class)) {
digestMock.when(() -> DigestHelper.calculateChecksum(file)).thenReturn("dummyChecksum");
boolean result = systemVmTemplateRegistration.isTemplateFileChecksumDifferent(details, file);
assertFalse(result);
}
}
@Test
public void testIsTemplateFileChecksumDifferent_mismatch() {
SystemVmTemplateRegistration.MetadataTemplateDetails details =
Mockito.mock(SystemVmTemplateRegistration.MetadataTemplateDetails.class);
when(details.getChecksum()).thenReturn("expectedChecksum");
File file = new File("dummy.txt");
try (MockedStatic<DigestHelper> digestMock = Mockito.mockStatic(DigestHelper.class)) {
digestMock.when(() -> DigestHelper.calculateChecksum(file)).thenReturn("actualChecksum");
boolean result = systemVmTemplateRegistration.isTemplateFileChecksumDifferent(details, file);
assertTrue(result);
}
}
@Test(expected = CloudRuntimeException.class)
public void testValidateTemplates_metadataTemplateFailure() {
List<Pair<Hypervisor.HypervisorType, CPU.CPUArch>> list = new ArrayList<>();
list.add(new Pair<>(Hypervisor.HypervisorType.KVM, CPU.CPUArch.amd64));
systemVmTemplateRegistration.validateTemplates(list);
}
@Test(expected = CloudRuntimeException.class)
public void testValidateTemplates_fileFailure() {
List<Pair<Hypervisor.HypervisorType, CPU.CPUArch>> list = new ArrayList<>();
list.add(new Pair<>(Hypervisor.HypervisorType.KVM, CPU.CPUArch.amd64));
SystemVmTemplateRegistration.MetadataTemplateDetails details =
Mockito.mock(SystemVmTemplateRegistration.MetadataTemplateDetails.class);
SystemVmTemplateRegistration.NewTemplateMap.put(SystemVmTemplateRegistration.getHypervisorArchKey(
Hypervisor.HypervisorType.KVM, CPU.CPUArch.amd64), details);
File mockFile = Mockito.mock(File.class);
doReturn(mockFile).when(systemVmTemplateRegistration).getTemplateFile(details);
doReturn(true).when(systemVmTemplateRegistration).isTemplateFileChecksumDifferent(details, mockFile);
systemVmTemplateRegistration.validateTemplates(list);
}
public void testValidateTemplates_downloadableFileNotFound() {
CPU.CPUArch arch = SystemVmTemplateRegistration.DOWNLOADABLE_TEMPLATE_ARCH_TYPES.get(0);
List<Pair<Hypervisor.HypervisorType, CPU.CPUArch>> list = new ArrayList<>();
list.add(new Pair<>(Hypervisor.HypervisorType.KVM, arch));
SystemVmTemplateRegistration.MetadataTemplateDetails details =
Mockito.mock(SystemVmTemplateRegistration.MetadataTemplateDetails.class);
SystemVmTemplateRegistration.NewTemplateMap.put(SystemVmTemplateRegistration.getHypervisorArchKey(
Hypervisor.HypervisorType.KVM, arch), details);
doReturn(null).when(systemVmTemplateRegistration).getTemplateFile(details);
systemVmTemplateRegistration.validateTemplates(list);
}
@Test
public void testValidateTemplates_success() {
List<Pair<Hypervisor.HypervisorType, CPU.CPUArch>> list = new ArrayList<>();
list.add(new Pair<>(Hypervisor.HypervisorType.KVM, CPU.CPUArch.amd64));
SystemVmTemplateRegistration.MetadataTemplateDetails details =
Mockito.mock(SystemVmTemplateRegistration.MetadataTemplateDetails.class);
SystemVmTemplateRegistration.NewTemplateMap.put(SystemVmTemplateRegistration.getHypervisorArchKey(
Hypervisor.HypervisorType.KVM, CPU.CPUArch.amd64), details);
File mockFile = Mockito.mock(File.class);
doReturn(mockFile).when(systemVmTemplateRegistration).getTemplateFile(details);
doReturn(false).when(systemVmTemplateRegistration).isTemplateFileChecksumDifferent(details, mockFile);
systemVmTemplateRegistration.validateTemplates(list);
}
@Test
public void testRegisterTemplatesForZone() {
long zoneId = 1L;
String filePath = "dummyFilePath";
String nfsVersion = "nfs3";
Pair<String, Long> storeUrlAndId = new Pair<>("nfs://dummy", 100L);
doReturn(storeUrlAndId).when(systemVmTemplateRegistration).getNfsStoreInZone(zoneId);
doReturn(nfsVersion).when(systemVmTemplateRegistration).getNfsVersion(storeUrlAndId.second());
try (MockedStatic<SystemVmTemplateRegistration> mockedStatic = Mockito.mockStatic(
SystemVmTemplateRegistration.class)) {
List<Pair<Hypervisor.HypervisorType, CPU.CPUArch>> hypervisorArchList = new ArrayList<>();
Hypervisor.HypervisorType hypervisorType = Hypervisor.HypervisorType.KVM;
CPU.CPUArch arch = CPU.CPUArch.getDefault();
hypervisorArchList.add(new Pair<>(hypervisorType, arch));
doReturn(hypervisorArchList).when(clusterDao).listDistinctHypervisorsArchAcrossClusters(zoneId);
SystemVmTemplateRegistration.MetadataTemplateDetails details =
Mockito.mock(SystemVmTemplateRegistration.MetadataTemplateDetails.class);
String name = "existing";
Mockito.when(details.getArch()).thenReturn(CPU.CPUArch.getDefault());
Mockito.when(details.getName()).thenReturn(name);
mockedStatic.when(() -> SystemVmTemplateRegistration.getMetadataTemplateDetails(Mockito.any(),
Mockito.any())).thenReturn(details);
when(systemVmTemplateRegistration.getRegisteredTemplate(name, arch))
.thenReturn(null);
doNothing().when(systemVmTemplateRegistration).registerTemplateForNonExistingEntries(
hypervisorType, arch,
name, storeUrlAndId, filePath);
systemVmTemplateRegistration.registerTemplatesForZone(zoneId, filePath);
mockedStatic.verify(() -> SystemVmTemplateRegistration.mountStore(storeUrlAndId.first(), filePath,
nfsVersion));
verify(systemVmTemplateRegistration).registerTemplateForNonExistingEntries(hypervisorType,
arch, name, storeUrlAndId, filePath);
}
}
}

View File

@ -58,11 +58,12 @@ function createMetadataFile() {
for template in "${templates[@]}"
do
section="${template%%:*}"
hvName=$(getGenericName $section)
sectionHv="${section%%-*}"
hvName=$(getGenericName $sectionHv)
downloadurl="${template#*:}"
arch=$(echo ${downloadurl#*"/systemvmtemplate-$VERSION-"} | cut -d'-' -f 1)
templatename="systemvm-${section%.*}-${VERSION}-${arch}"
templatename="systemvm-${sectionHv%.*}-${VERSION}-${arch}"
checksum=$(getChecksum "$fileData" "$VERSION-${arch}-$hvName")
filename=$(echo ${downloadurl##*'/'})
echo -e "["$section"]\ntemplatename = $templatename\nchecksum = $checksum\ndownloadurl = $downloadurl\nfilename = $filename\narch = $arch\n" >> $METADATAFILE
@ -71,7 +72,8 @@ function createMetadataFile() {
declare -a templates
getTemplateVersion $1
templates=( "kvm:https://download.cloudstack.org/systemvm/${CS_VERSION}/systemvmtemplate-$VERSION-x86_64-kvm.qcow2.bz2"
templates=( "kvm-x86_64:https://download.cloudstack.org/systemvm/${CS_VERSION}/systemvmtemplate-$VERSION-x86_64-kvm.qcow2.bz2"
"kvm-aarch64:https://download.cloudstack.org/systemvm/${CS_VERSION}/systemvmtemplate-$VERSION-aarch64-kvm.qcow2.bz2"
"vmware:https://download.cloudstack.org/systemvm/${CS_VERSION}/systemvmtemplate-$VERSION-x86_64-vmware.ova"
"xenserver:https://download.cloudstack.org/systemvm/$CS_VERSION/systemvmtemplate-$VERSION-x86_64-xen.vhd.bz2"
"hyperv:https://download.cloudstack.org/systemvm/$CS_VERSION/systemvmtemplate-$VERSION-x86_64-hyperv.vhd.zip"

View File

@ -573,6 +573,9 @@ public abstract class GenericDaoBase<T, ID extends Serializable> extends Compone
} else {
field.set(entity, rs.getLong(index));
}
} else if (field.getDeclaredAnnotation(Convert.class) != null) {
Object val = _conversionSupport.convertToEntityAttribute(field, rs.getObject(index));
field.set(entity, val);
} else if (type.isEnum()) {
final Enumerated enumerated = field.getAnnotation(Enumerated.class);
final EnumType enumType = (enumerated == null) ? EnumType.STRING : enumerated.value();
@ -677,9 +680,6 @@ public abstract class GenericDaoBase<T, ID extends Serializable> extends Compone
}
} else if (type == byte[].class) {
field.set(entity, rs.getBytes(index));
} else if (field.getDeclaredAnnotation(Convert.class) != null) {
Object val = _conversionSupport.convertToEntityAttribute(field, rs.getObject(index));
field.set(entity, val);
} else {
field.set(entity, rs.getObject(index));
}
@ -949,7 +949,7 @@ public abstract class GenericDaoBase<T, ID extends Serializable> extends Compone
}
@DB()
protected List<T> listBy(SearchCriteria<T> sc, final Filter filter) {
public List<T> listBy(SearchCriteria<T> sc, final Filter filter) {
sc = checkAndSetRemovedIsNull(sc);
return listIncludingRemovedBy(sc, filter);
}

View File

@ -36,6 +36,8 @@ import com.cloud.utils.exception.CloudRuntimeException;
import net.sf.cglib.proxy.Factory;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
/**
@ -122,7 +124,7 @@ public abstract class SearchBase<J extends SearchBase<?, T, K>, T, K> {
if (_entity == null) {
throw new RuntimeException("SearchBuilder cannot be modified once it has been setup");
}
if (_specifiedAttrs.size() > 1) {
if (func.getCount() <= 1 && _specifiedAttrs.size() > 1) {
throw new RuntimeException("You can't specify more than one field to search on");
}
if (func.getCount() != -1 && (func.getCount() != (params.length + 1))) {
@ -150,7 +152,7 @@ public abstract class SearchBase<J extends SearchBase<?, T, K>, T, K> {
}
}
final Select select = new Select(func, _specifiedAttrs.size() == 0 ? null : _specifiedAttrs.get(0), declaredField, params);
final Select select = new Select(func, _specifiedAttrs, declaredField, params);
_selects.add(select);
_specifiedAttrs.clear();
@ -185,7 +187,7 @@ public abstract class SearchBase<J extends SearchBase<?, T, K>, T, K> {
} catch (final SecurityException e) {
} catch (final NoSuchFieldException e) {
}
_selects.add(new Select(Func.NATIVE, attr, field, null));
_selects.add(new Select(Func.NATIVE, List.of(attr), field, null));
}
_specifiedAttrs.clear();
@ -528,16 +530,18 @@ public abstract class SearchBase<J extends SearchBase<?, T, K>, T, K> {
protected static class Select {
public Func func;
public Attribute attr;
public List<Attribute> attributes = new ArrayList<>();
public Object[] params;
public Field field;
protected Select() {
}
public Select(final Func func, final Attribute attr, final Field field, final Object[] params) {
public Select(final Func func, final List<Attribute> attributes, final Field field, final Object[] params) {
this.func = func;
this.attr = attr;
if (CollectionUtils.isNotEmpty(attributes)) {
this.attributes.addAll(attributes);
}
this.params = params;
this.field = field;
}

View File

@ -23,6 +23,7 @@ import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.ArrayUtils;
import com.cloud.utils.Pair;
@ -59,7 +60,7 @@ public class SearchCriteria<K> {
}
public enum Func {
NATIVE("@", 1), MAX("MAX(@)", 1), MIN("MIN(@)", 1), FIRST("FIRST(@)", 1), LAST("LAST(@)", 1), SUM("SUM(@)", 1), COUNT("COUNT(@)", 1), DISTINCT("DISTINCT(@)", 1);
NATIVE("@", 1), MAX("MAX(@)", 1), MIN("MIN(@)", 1), FIRST("FIRST(@)", 1), LAST("LAST(@)", 1), SUM("SUM(@)", 1), COUNT("COUNT(@)", 1), DISTINCT("DISTINCT(@)", 1), DISTINCT_PAIR("DISTINCT @, @", 2);
private String func;
private int count;
@ -135,10 +136,12 @@ public class SearchCriteria<K> {
for (Select select : _selects) {
String func = select.func.toString() + ",";
if (select.attr == null) {
if (CollectionUtils.isEmpty(select.attributes)) {
func = func.replace("@", "*");
} else {
func = func.replace("@", select.attr.table + "." + select.attr.columnName);
for (Attribute attribute : select.attributes) {
func = func.replaceFirst("@", attribute.table + "." + attribute.columnName);
}
}
str.insert(insertAt, func);
insertAt += func.length();

View File

@ -470,6 +470,8 @@ if [ -f %{_sysconfdir}/sysconfig/%{name}-management ] ; then
fi
chown -R cloud:cloud /var/log/cloudstack/management
chown -R cloud:cloud /usr/share/cloudstack-management/templates
find /usr/share/cloudstack-management/templates -type d -exec chmod 0770 {} \;
systemctl daemon-reload
@ -610,6 +612,9 @@ pip3 install --upgrade /usr/share/cloudstack-marvin/Marvin-*.tar.gz
%{_datadir}/%{name}-management/setup/*.sh
%{_datadir}/%{name}-management/setup/server-setup.xml
%{_datadir}/%{name}-management/webapp/*
%dir %attr(0770, cloud, cloud) %{_datadir}/%{name}-management/templates
%dir %attr(0770, cloud, cloud) %{_datadir}/%{name}-management/templates/systemvm
%attr(0644, cloud, cloud) %{_datadir}/%{name}-management/templates/systemvm/*
%attr(0755,root,root) %{_bindir}/%{name}-external-ipallocator.py
%attr(0755,root,root) %{_initrddir}/%{name}-ipallocator
%dir %attr(0770,root,root) %{_localstatedir}/log/%{name}/ipallocator

View File

@ -39,6 +39,7 @@ public interface MockAgentManager extends Manager {
public static final long DEFAULT_HOST_MEM_SIZE = 8 * 1024 * 1024 * 1024L; // 8G, unit of Mbytes
public static final int DEFAULT_HOST_CPU_CORES = 4; // 2 dual core CPUs (2 x 2)
public static final int DEFAULT_HOST_SPEED_MHZ = 8000; // 1 GHz CPUs
public static final String DEFAULT_HOST_ARCH = "x86_64";
@Override
boolean configure(String name, Map<String, Object> params) throws ConfigurationException;

View File

@ -153,6 +153,7 @@ public class MockAgentManagerImpl extends ManagerBase implements MockAgentManage
long cpuSpeed = Long.parseLong((String)params.get("cpuspeed"));
long memory = Long.parseLong((String)params.get("memory"));
long localStorageSize = Long.parseLong((String)params.get("localstorage"));
String arch = (String)params.get("arch");
synchronized (this) {
long dataCenterId = Long.parseLong((String)params.get("zone"));
long podId = Long.parseLong((String)params.get("pod"));
@ -170,6 +171,7 @@ public class MockAgentManagerImpl extends ManagerBase implements MockAgentManage
mockHost.setCpuCount(cpuCore);
mockHost.setCpuSpeed(cpuSpeed);
mockHost.setMemorySize(memory);
mockHost.setArch(arch);
String guid = UUID.randomUUID().toString();
mockHost.setGuid(guid);
mockHost.setName("SimulatedAgent." + guid);

View File

@ -177,6 +177,7 @@ public class AgentRoutingResource extends AgentStorageResource {
StartupRoutingCommand cmd =
new StartupRoutingCommand((Integer)info.get(0), (Long)info.get(1), (Long)info.get(2), (Long)info.get(4), (String)info.get(3), HypervisorType.Simulator,
RouterPrivateIpStrategy.HostLocal);
cmd.setCpuArch((String)info.get(5));
Map<String, String> hostDetails = new HashMap<String, String>();
hostDetails.put(RouterPrivateIpStrategy.class.getCanonicalName(), RouterPrivateIpStrategy.DcGlobal.toString());
@ -274,12 +275,14 @@ public class AgentRoutingResource extends AgentStorageResource {
long cpus = agentHost.getCpuCount();
long ram = agentHost.getMemorySize();
long dom0Ram = agentHost.getMemorySize() / 10;
String arch = agentHost.getArch();
info.add((int)cpus);
info.add(speed);
info.add(ram);
info.add(agentHost.getCapabilities());
info.add(dom0Ram);
info.add(arch);
return info;
}

View File

@ -87,6 +87,7 @@ public class SimulatorDiscoverer extends DiscovererBase implements Discoverer, L
long cpuCores = MockAgentManager.DEFAULT_HOST_CPU_CORES;
long memory = MockAgentManager.DEFAULT_HOST_MEM_SIZE;
long localstorageSize = MockStorageManager.DEFAULT_HOST_STORAGE_SIZE;
String arch = MockAgentManager.DEFAULT_HOST_ARCH;
if (scheme.equals("http")) {
if (host == null || !host.startsWith("sim")) {
String msg = "uri is not of simulator type so we're not taking care of the discovery for this: " + uri;
@ -111,6 +112,8 @@ public class SimulatorDiscoverer extends DiscovererBase implements Discoverer, L
memory = Long.parseLong(parameter[1]);
} else if (parameter[0].equalsIgnoreCase("localstorage") && parameter[1] != null) {
localstorageSize = Long.parseLong(parameter[1]);
} else if (parameter[0].equalsIgnoreCase("arch") && parameter[1] != null) {
arch = parameter[1];
}
}
}
@ -168,6 +171,7 @@ public class SimulatorDiscoverer extends DiscovererBase implements Discoverer, L
params.put("cpucore", Long.toString(cpuCores));
params.put("memory", Long.toString(memory));
params.put("localstorage", Long.toString(localstorageSize));
params.put("arch", arch);
resources = createAgentResources(params);
return resources;

View File

@ -23,6 +23,8 @@ public interface MockHost {
public long getMemorySize();
String getArch();
public String getCapabilities();
public long getId();

View File

@ -87,6 +87,9 @@ public class MockHostVO implements MockHost, InternalIdentity {
@Column(name = "ram")
private long memorySize;
@Column(name = "arch")
private String arch;
@Column(name = "capabilities")
private String capabilities;
@ -143,6 +146,14 @@ public class MockHostVO implements MockHost, InternalIdentity {
this.memorySize = memorySize;
}
public String getArch() {
return arch;
}
public void setArch(String arch) {
this.arch = arch;
}
@Override
public String getCapabilities() {
return this.capabilities;

View File

@ -22,7 +22,6 @@ import java.util.List;
import javax.inject.Inject;
import com.cloud.cpu.CPU;
import org.apache.cloudstack.api.ApiCommandResourceType;
import org.apache.cloudstack.api.ApiConstants;
import org.apache.cloudstack.api.command.admin.kubernetes.version.AddKubernetesSupportedVersionCmd;
@ -38,6 +37,7 @@ import org.apache.commons.lang3.StringUtils;
import com.cloud.api.query.dao.TemplateJoinDao;
import com.cloud.api.query.vo.TemplateJoinVO;
import com.cloud.cpu.CPU;
import com.cloud.dc.DataCenter;
import com.cloud.dc.DataCenterVO;
import com.cloud.dc.dao.DataCenterDao;
@ -58,6 +58,7 @@ import com.cloud.utils.Pair;
import com.cloud.utils.component.ComponentContext;
import com.cloud.utils.component.ManagerBase;
import com.cloud.utils.db.Filter;
import com.cloud.utils.db.JoinBuilder;
import com.cloud.utils.db.SearchBuilder;
import com.cloud.utils.db.SearchCriteria;
import com.cloud.utils.exception.CloudRuntimeException;
@ -94,6 +95,7 @@ public class KubernetesVersionManagerImpl extends ManagerBase implements Kuberne
if (template.getState() != null) {
response.setIsoState(template.getState().toString());
}
response.setIsoArch(template.getArch().getType());
response.setDirectDownload(template.isDirectDownload());
}
@ -267,6 +269,7 @@ public class KubernetesVersionManagerImpl extends ManagerBase implements Kuberne
final Long zoneId = cmd.getZoneId();
String minimumSemanticVersion = cmd.getMinimumSemanticVersion();
final Long minimumKubernetesVersionId = cmd.getMinimumKubernetesVersionId();
final String arch = cmd.getArch();
if (StringUtils.isNotEmpty(minimumSemanticVersion) && minimumKubernetesVersionId != null) {
throw new CloudRuntimeException(String.format("Both parameters %s and %s can not be passed together", ApiConstants.MIN_SEMANTIC_VERSION, ApiConstants.MIN_KUBERNETES_VERSION_ID));
}
@ -281,6 +284,13 @@ public class KubernetesVersionManagerImpl extends ManagerBase implements Kuberne
SearchBuilder<KubernetesSupportedVersionVO> sb = kubernetesSupportedVersionDao.createSearchBuilder();
sb.and("id", sb.entity().getId(), SearchCriteria.Op.EQ);
sb.and("keyword", sb.entity().getName(), SearchCriteria.Op.LIKE);
if (StringUtils.isNotBlank(arch)) {
SearchBuilder<VMTemplateVO> isoSearch = templateDao.createSearchBuilder();
isoSearch.and("arch", isoSearch.entity().getArch(), SearchCriteria.Op.EQ);
sb.join("isoSearch", isoSearch, isoSearch.entity().getId(), sb.entity().getIsoId(), JoinBuilder.JoinType.INNER);
isoSearch.done();
}
sb.done();
SearchCriteria<KubernetesSupportedVersionVO> sc = sb.create();
String keyword = cmd.getKeyword();
if (versionId != null) {
@ -295,6 +305,9 @@ public class KubernetesVersionManagerImpl extends ManagerBase implements Kuberne
if(keyword != null){
sc.setParameters("keyword", "%" + keyword + "%");
}
if (StringUtils.isNotBlank(arch)) {
sc.setJoinParameters("isoSearch", "arch", arch);
}
Pair<List<KubernetesSupportedVersionVO>, Integer> versionsAndCount =
kubernetesSupportedVersionDao.searchAndCount(sc, searchFilter);
List<KubernetesSupportedVersionVO> versions =

View File

@ -66,6 +66,11 @@ public class ListKubernetesSupportedVersionsCmd extends BaseListCmd {
description = "the ID of the minimum Kubernetes supported version")
private Long minimumKubernetesVersionId;
@Parameter(name = ApiConstants.ARCH, type = CommandType.STRING,
description = "the CPU arch of the binaries ISO. Valid options are: x86_64, aarch64",
since = "4.20")
private String arch;
/////////////////////////////////////////////////////
/////////////////// Accessors ///////////////////////
/////////////////////////////////////////////////////
@ -85,6 +90,10 @@ public class ListKubernetesSupportedVersionsCmd extends BaseListCmd {
return minimumSemanticVersion;
}
public String getArch() {
return arch;
}
public Long getMinimumKubernetesVersionId() {
return minimumKubernetesVersionId;
}

View File

@ -54,6 +54,10 @@ public class KubernetesSupportedVersionResponse extends BaseResponse {
@Param(description = "the state of the binaries ISO for Kubernetes supported version")
private String isoState;
@SerializedName(ApiConstants.ARCH)
@Param(description = "the arch of the binaries ISO for Kubernetes supported version", since = "4.20.1")
private String isoArch;
@SerializedName(ApiConstants.ZONE_ID)
@Param(description = "the id of the zone in which Kubernetes supported version is available")
private String zoneId;
@ -138,6 +142,14 @@ public class KubernetesSupportedVersionResponse extends BaseResponse {
this.isoState = isoState;
}
public String getIsoArch() {
return isoArch;
}
public void setIsoArch(String isoArch) {
this.isoArch = isoArch;
}
public String getZoneId() {
return zoneId;
}

View File

@ -31,6 +31,7 @@ import org.springframework.test.util.ReflectionTestUtils;
import com.cloud.api.query.dao.TemplateJoinDao;
import com.cloud.api.query.vo.TemplateJoinVO;
import com.cloud.cpu.CPU;
@RunWith(MockitoJUnitRunner.class)
public class KubernetesVersionManagerImplTest {
@ -57,6 +58,7 @@ public class KubernetesVersionManagerImplTest {
Mockito.when(kubernetesSupportedVersion.getIsoId()).thenReturn(1L);
KubernetesSupportedVersionResponse response = new KubernetesSupportedVersionResponse();
TemplateJoinVO templateJoinVO = Mockito.mock(TemplateJoinVO.class);
Mockito.when(templateJoinVO.getArch()).thenReturn(CPU.CPUArch.getDefault());
String uuid = UUID.randomUUID().toString();
Mockito.when(templateJoinVO.getUuid()).thenReturn(uuid);
Mockito.when(templateJoinDao.findById(1L)).thenReturn(templateJoinVO);

View File

@ -125,6 +125,7 @@ public class KubernetesVersionServiceTest {
TemplateJoinVO templateJoinVO = Mockito.mock(TemplateJoinVO.class);
when(templateJoinVO.getState()).thenReturn(ObjectInDataStoreStateMachine.State.Ready);
when(templateJoinVO.getArch()).thenReturn(CPU.CPUArch.getDefault());
when(templateJoinDao.findById(Mockito.anyLong())).thenReturn(templateJoinVO);
KubernetesSupportedVersionVO versionVO = Mockito.mock(KubernetesSupportedVersionVO.class);

View File

@ -16,6 +16,12 @@
// under the License.
package org.apache.cloudstack.network.lb;
import static com.cloud.hypervisor.Hypervisor.HypervisorType.Hyperv;
import static com.cloud.hypervisor.Hypervisor.HypervisorType.KVM;
import static com.cloud.hypervisor.Hypervisor.HypervisorType.LXC;
import static com.cloud.hypervisor.Hypervisor.HypervisorType.VMware;
import static com.cloud.hypervisor.Hypervisor.HypervisorType.XenServer;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
@ -31,6 +37,7 @@ import org.apache.cloudstack.engine.orchestration.service.NetworkOrchestrationSe
import org.apache.cloudstack.framework.config.dao.ConfigurationDao;
import org.apache.cloudstack.lb.ApplicationLoadBalancerRuleVO;
import org.apache.cloudstack.lb.dao.ApplicationLoadBalancerRuleDao;
import org.apache.commons.collections.CollectionUtils;
import com.cloud.agent.AgentManager;
import com.cloud.agent.api.Answer;
@ -118,12 +125,6 @@ import com.cloud.vm.VirtualMachineProfile.Param;
import com.cloud.vm.dao.DomainRouterDao;
import com.cloud.vm.dao.NicDao;
import static com.cloud.hypervisor.Hypervisor.HypervisorType.Hyperv;
import static com.cloud.hypervisor.Hypervisor.HypervisorType.KVM;
import static com.cloud.hypervisor.Hypervisor.HypervisorType.LXC;
import static com.cloud.hypervisor.Hypervisor.HypervisorType.VMware;
import static com.cloud.hypervisor.Hypervisor.HypervisorType.XenServer;
public class InternalLoadBalancerVMManagerImpl extends ManagerBase implements InternalLoadBalancerVMManager, InternalLoadBalancerVMService, VirtualMachineGuru {
static final private String InternalLbVmNamePrefix = "b";
@ -732,6 +733,70 @@ public class InternalLoadBalancerVMManagerImpl extends ManagerBase implements In
return internalLbVms;
}
protected String getRouterTemplateForHypervisor(HypervisorType hypervisorType, long dataCenterId) {
String templateName = null;
if (XenServer.equals(hypervisorType)) {
templateName = VirtualNetworkApplianceManager.RouterTemplateXen.valueIn(dataCenterId);
} else if (KVM.equals(hypervisorType)) {
templateName = VirtualNetworkApplianceManager.RouterTemplateKvm.valueIn(dataCenterId);
} else if (VMware.equals(hypervisorType)) {
templateName = VirtualNetworkApplianceManager.RouterTemplateVmware.valueIn(dataCenterId);
} else if (Hyperv.equals(hypervisorType)) {
templateName = VirtualNetworkApplianceManager.RouterTemplateHyperV.valueIn(dataCenterId);
} else if (LXC.equals(hypervisorType)) {
templateName = VirtualNetworkApplianceManager.RouterTemplateLxc.valueIn(dataCenterId);
}
return templateName;
}
protected DomainRouterVO createOrUpdateInternalLb(DomainRouterVO internalLbVm, final long id,
final long internalLbProviderId, final Account owner, final long userId, final Long vpcId,
final ServiceOffering routerOffering, final VMTemplateVO template) {
if (internalLbVm == null) {
internalLbVm = new DomainRouterVO(id, routerOffering.getId(), internalLbProviderId,
VirtualMachineName.getSystemVmName(id, _instance, InternalLbVmNamePrefix),
template.getId(), template.getHypervisorType(), template.getGuestOSId(),
owner.getDomainId(), owner.getId(), userId, false,
RedundantState.UNKNOWN, false, false,
VirtualMachine.Type.InternalLoadBalancerVm, vpcId);
internalLbVm.setRole(Role.INTERNAL_LB_VM);
internalLbVm.setLimitCpuUse(routerOffering.getLimitCpuUse());
internalLbVm.setDynamicallyScalable(template.isDynamicallyScalable());
return _internalLbVmDao.persist(internalLbVm);
}
internalLbVm.setTemplateId(template.getId());
internalLbVm.setDynamicallyScalable(template.isDynamicallyScalable());
_internalLbVmDao.update(internalLbVm.getId(), internalLbVm);
return internalLbVm;
}
protected DomainRouterVO deployInternalLbVmWithTemplates(DomainRouterVO internalLbVm, final long id,
final DeploymentPlan plan, final long internalLbProviderId, final Account owner, final long userId,
final Long vpcId, final ServiceOffering routerOffering,
final LinkedHashMap<Network, List<? extends NicProfile>> networks, final List<VMTemplateVO> templates)
throws InsufficientCapacityException {
for (final Iterator<VMTemplateVO> templatesIterator = templates.iterator(); templatesIterator.hasNext();) {
final VMTemplateVO template = templatesIterator.next();
try {
internalLbVm = createOrUpdateInternalLb(internalLbVm, id, internalLbProviderId, owner, userId, vpcId,
routerOffering, template);
_itMgr.allocate(internalLbVm.getInstanceName(), template, routerOffering, networks, plan, null);
internalLbVm = _internalLbVmDao.findById(internalLbVm.getId());
if (templatesIterator.hasNext()) {
_itMgr.checkDeploymentPlan(internalLbVm, template, routerOffering, owner, plan);
}
return internalLbVm;
} catch (InsufficientCapacityException ex) {
if (templatesIterator.hasNext()) {
logger.debug("Failed to allocate the VR with hypervisor {} and {}, retrying with another template", template.getHypervisorType(), template);
} else {
throw ex;
}
}
}
return null;
}
protected DomainRouterVO deployInternalLbVm(final Account owner, final DeployDestination dest, final DeploymentPlan plan, final Map<Param, Object> params, final long internalLbProviderId,
final long svcOffId, final Long vpcId, final LinkedHashMap<Network, List<? extends NicProfile>> networks, final boolean startVm) throws ConcurrentOperationException,
InsufficientAddressCapacityException, InsufficientServerCapacityException, InsufficientCapacityException, StorageUnavailableException,
@ -743,6 +808,14 @@ public class InternalLoadBalancerVMManagerImpl extends ManagerBase implements In
// Try to allocate the internal lb twice using diff hypervisors, and when failed both times, throw the exception up
final List<HypervisorType> hypervisors = getHypervisors(dest, plan, null);
long userId = CallContext.current().getCallingUserId();
if (CallContext.current().getCallingAccount().getId() != owner.getId()) {
List<UserVO> userVOs = _userDao.listByAccount(owner.getAccountId());
if (!userVOs.isEmpty()) {
userId = userVOs.get(0).getId();
}
}
int allocateRetry = 0;
int startRetry = 0;
DomainRouterVO internalLbVm = null;
@ -751,45 +824,23 @@ public class InternalLoadBalancerVMManagerImpl extends ManagerBase implements In
try {
final long id = _internalLbVmDao.getNextInSequence(Long.class, "id");
if (logger.isDebugEnabled()) {
logger.debug("Creating the internal lb vm " + id + " in datacenter " + dest.getDataCenter() + " with hypervisor type " + hType);
logger.debug("Creating the internal lb vm {} in datacenter {} with hypervisor type {}",
id, dest.getDataCenter(), hType);
}
String templateName = null;
if (hType.equals(XenServer)) {
templateName = VirtualNetworkApplianceManager.RouterTemplateXen.valueIn(dest.getDataCenter().getId());
} else if (hType.equals(KVM)) {
templateName = VirtualNetworkApplianceManager.RouterTemplateKvm.valueIn(dest.getDataCenter().getId());
} else if (hType.equals(VMware)) {
templateName = VirtualNetworkApplianceManager.RouterTemplateVmware.valueIn(dest.getDataCenter().getId());
} else if (hType.equals(Hyperv)) {
templateName = VirtualNetworkApplianceManager.RouterTemplateHyperV.valueIn(dest.getDataCenter().getId());
} else if (hType.equals(LXC)) {
templateName = VirtualNetworkApplianceManager.RouterTemplateLxc.valueIn(dest.getDataCenter().getId());
}
final VMTemplateVO template = _templateDao.findRoutingTemplate(hType, templateName);
if (template == null) {
logger.debug(hType + " won't support system vm, skip it");
final long zoneId = dest.getDataCenter().getId();
final String templateName = getRouterTemplateForHypervisor(hType, zoneId);
final String preferredArch = ResourceManager.SystemVmPreferredArchitecture.valueIn(zoneId);
final List<VMTemplateVO> templates = _templateDao.findRoutingTemplates(hType, templateName,
preferredArch);
if (CollectionUtils.isEmpty(templates)) {
logger.debug("{} won't support system vm, skip it", hType);
continue;
}
long userId = CallContext.current().getCallingUserId();
if (CallContext.current().getCallingAccount().getId() != owner.getId()) {
List<UserVO> userVOs = _userDao.listByAccount(owner.getAccountId());
if (!userVOs.isEmpty()) {
userId = userVOs.get(0).getId();
}
}
internalLbVm =
new DomainRouterVO(id, routerOffering.getId(), internalLbProviderId, VirtualMachineName.getSystemVmName(id, _instance, InternalLbVmNamePrefix),
template.getId(), template.getHypervisorType(), template.getGuestOSId(), owner.getDomainId(), owner.getId(), userId, false, RedundantState.UNKNOWN, false, false, VirtualMachine.Type.InternalLoadBalancerVm, vpcId);
internalLbVm.setRole(Role.INTERNAL_LB_VM);
internalLbVm = _internalLbVmDao.persist(internalLbVm);
_itMgr.allocate(internalLbVm.getInstanceName(), template, routerOffering, networks, plan, null);
internalLbVm = _internalLbVmDao.findById(internalLbVm.getId());
internalLbVm = deployInternalLbVmWithTemplates(internalLbVm, id, plan, internalLbProviderId, owner,
userId, vpcId, routerOffering, networks, templates);
} catch (final InsufficientCapacityException ex) {
if (allocateRetry < 2 && iter.hasNext()) {
logger.debug("Failed to allocate the Internal lb vm with hypervisor type " + hType + ", retrying one more time");
logger.debug("Failed to allocate the Internal lb vm with hypervisor type {}, retrying one more time", hType);
continue;
} else {
throw ex;
@ -804,8 +855,7 @@ public class InternalLoadBalancerVMManagerImpl extends ManagerBase implements In
break;
} catch (final InsufficientCapacityException ex) {
if (startRetry < 2 && iter.hasNext()) {
logger.debug("Failed to start the Internal lb vm " + internalLbVm + " with hypervisor type " + hType + ", " +
"destroying it and recreating one more time");
logger.debug("Failed to start the Internal lb vm {} with hypervisor type {}, destroying it and recreating one more time", internalLbVm, hType);
// destroy the internal lb vm
destroyInternalLbVm(internalLbVm.getId(), _accountMgr.getSystemAccount(), User.UID_SYSTEM);
continue;

View File

@ -0,0 +1,172 @@
// 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.network.lb;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
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.ArgumentMatchers.isNull;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import java.util.Arrays;
import java.util.LinkedHashMap;
import java.util.List;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnitRunner;
import org.mockito.stubbing.Answer;
import com.cloud.deploy.DataCenterDeployment;
import com.cloud.deploy.DeploymentPlan;
import com.cloud.exception.InsufficientCapacityException;
import com.cloud.exception.InsufficientServerCapacityException;
import com.cloud.network.Network;
import com.cloud.network.router.VirtualRouter;
import com.cloud.offering.ServiceOffering;
import com.cloud.storage.VMTemplateVO;
import com.cloud.user.Account;
import com.cloud.vm.DomainRouterVO;
import com.cloud.vm.NicProfile;
import com.cloud.vm.VirtualMachineManager;
import com.cloud.vm.dao.DomainRouterDao;
@RunWith(MockitoJUnitRunner.class)
public class InternalLoadBalancerVMManagerImplTest {
@Mock
private DomainRouterDao internalLbVmDao;
@Mock
private VirtualMachineManager virtualMachineManager;
@InjectMocks
private InternalLoadBalancerVMManagerImpl service;
// Dummy objects for testing.
private Account account;
private ServiceOffering serviceOffering;
private VMTemplateVO template1;
private VMTemplateVO template2;
private DeploymentPlan plan;
@Before
public void setUp() {
account = mock(Account.class);
serviceOffering = mock(ServiceOffering.class);
template1 = mock(VMTemplateVO.class);
template2 = mock(VMTemplateVO.class);
plan = new DataCenterDeployment(1L);
}
@Test
public void testCreateOrUpdateInternalLb_New() {
long id = 1L;
long internalLbProviderId = 2L;
long userId = 100L;
Long vpcId = 200L;
when(template1.isDynamicallyScalable()).thenReturn(true);
when(serviceOffering.getLimitCpuUse()).thenReturn(true);
when(internalLbVmDao.persist(any(DomainRouterVO.class))).thenAnswer(invocation -> invocation.getArgument(0));
DomainRouterVO result = service.createOrUpdateInternalLb(null, id, internalLbProviderId, account,
userId, vpcId, serviceOffering, template1);
verify(internalLbVmDao).persist(any(DomainRouterVO.class));
assertEquals(template1.getId(), result.getTemplateId());
assertTrue(result.isDynamicallyScalable());
assertEquals(VirtualRouter.Role.INTERNAL_LB_VM, result.getRole());
assertTrue(result.limitCpuUse());
}
@Test
public void testCreateOrUpdateInternalLb_Update() {
long id = 1L;
long internalLbProviderId = 2L;
long userId = 100L;
Long vpcId = 200L;
DomainRouterVO existing = mock(DomainRouterVO.class);
when(existing.getId()).thenReturn(id);
when(template1.isDynamicallyScalable()).thenReturn(true);
final boolean[] dsResult = {false};
doAnswer((Answer<Void>) invocation -> {
dsResult[0] = invocation.getArgument(0);
return null;
}).when(existing).setDynamicallyScalable(anyBoolean());
DomainRouterVO result = service.createOrUpdateInternalLb(existing, id, internalLbProviderId, account, userId, vpcId, serviceOffering, template1);
verify(internalLbVmDao).update(existing.getId(), existing);
assertEquals(template1.getId(), result.getTemplateId());
assertTrue(dsResult[0]);
}
@Test
public void testDeployInternalLbVmWithTemplates_SuccessfulFirstTemplate() throws Exception {
long id = 1L;
long internalLbProviderId = 2L;
long userId = 100L;
Long vpcId = 200L;
List<VMTemplateVO> templates = Arrays.asList(template1);
LinkedHashMap<Network, List<? extends NicProfile>> networks = new LinkedHashMap<>();
when(internalLbVmDao.persist(any(DomainRouterVO.class))).thenAnswer(invocation -> invocation.getArgument(0));
when(internalLbVmDao.findById(anyLong())).thenReturn(mock(DomainRouterVO.class));
DomainRouterVO result = service.deployInternalLbVmWithTemplates(null, id, plan, internalLbProviderId, account, userId, vpcId, serviceOffering, networks, templates);
assertNotNull(result);
verify(virtualMachineManager).allocate(anyString(), eq(template1), eq(serviceOffering), eq(networks), eq(plan), isNull());
}
@Test
public void testDeployInternalLbVmWithTemplates_RetryOnInsufficientCapacity() throws Exception {
long id = 1L;
long internalLbProviderId = 2L;
long userId = 100L;
Long vpcId = 200L;
List<VMTemplateVO> templates = Arrays.asList(template1, template2);
LinkedHashMap<Network, List<? extends NicProfile>> networks = new LinkedHashMap<>();
when(internalLbVmDao.persist(any(DomainRouterVO.class))).thenAnswer(invocation -> invocation.getArgument(0));
when(internalLbVmDao.findById(anyLong())).thenReturn(mock(DomainRouterVO.class));
doThrow(new InsufficientServerCapacityException("Not enough capacity", id))
.when(virtualMachineManager).allocate(anyString(), eq(template1), eq(serviceOffering), eq(networks), eq(plan), isNull());
DomainRouterVO result = service.deployInternalLbVmWithTemplates(null, id, plan, internalLbProviderId, account, userId, vpcId, serviceOffering, networks, templates);
assertNotNull(result);
verify(virtualMachineManager).allocate(anyString(), eq(template1), eq(serviceOffering), eq(networks), eq(plan), isNull());
verify(virtualMachineManager).allocate(anyString(), eq(template2), eq(serviceOffering), eq(networks), eq(plan), isNull());
}
@Test(expected = InsufficientCapacityException.class)
public void testDeployInternalLbVmWithTemplates_AllTemplatesFail() throws Exception {
long id = 1L;
long internalLbProviderId = 2L;
long userId = 100L;
Long vpcId = 200L;
List<VMTemplateVO> templates = Arrays.asList(template1, template2);
LinkedHashMap<Network, List<? extends NicProfile>> networks = new LinkedHashMap<>();
when(internalLbVmDao.persist(any(DomainRouterVO.class))).thenAnswer(invocation -> invocation.getArgument(0));
doThrow(new InsufficientServerCapacityException("Insufficient capacity", id))
.when(virtualMachineManager).allocate(anyString(), any(VMTemplateVO.class), eq(serviceOffering), eq(networks), eq(plan), isNull());
service.deployInternalLbVmWithTemplates(null, id, plan, internalLbProviderId, account, userId, vpcId, serviceOffering, networks, templates);
}
}

View File

@ -1718,6 +1718,7 @@ public class ApiResponseHelper implements ResponseGenerator {
if (template != null) {
vmResponse.setTemplateId(template.getUuid());
vmResponse.setTemplateName(template.getName());
vmResponse.setArch(template.getArch().getType());
}
vmResponse.setCreated(vm.getCreated());
vmResponse.setHypervisor(vm.getHypervisorType().getHypervisorDisplayName());

View File

@ -1298,6 +1298,7 @@ public class QueryManagerImpl extends MutualExclusiveIdsManagerBase implements Q
Long userId = cmd.getUserId();
Long userdataId = cmd.getUserdataId();
Map<String, String> tags = cmd.getTags();
final CPU.CPUArch arch = cmd.getArch();
boolean isAdmin = false;
boolean isRootAdmin = false;
@ -1525,8 +1526,10 @@ public class QueryManagerImpl extends MutualExclusiveIdsManagerBase implements Q
}
Boolean isVnf = cmd.getVnf();
if (isVnf != null) {
boolean templateJoinNeeded = isVnf != null || arch != null;
if (templateJoinNeeded) {
SearchBuilder<VMTemplateVO> templateSearch = _templateDao.createSearchBuilder();
templateSearch.and("templateArch", templateSearch.entity().getArch(), Op.EQ);
templateSearch.and("templateTypeEQ", templateSearch.entity().getTemplateType(), Op.EQ);
templateSearch.and("templateTypeNEQ", templateSearch.entity().getTemplateType(), Op.NEQ);
@ -1655,6 +1658,9 @@ public class QueryManagerImpl extends MutualExclusiveIdsManagerBase implements Q
userVmSearchCriteria.setJoinParameters("vmTemplate", "templateTypeNEQ", TemplateType.VNF);
}
}
if (arch != null) {
userVmSearchCriteria.setJoinParameters("vmTemplate", "templateArch", arch);
}
if (isRootAdmin) {
if (podId != null) {
@ -2355,6 +2361,7 @@ public class QueryManagerImpl extends MutualExclusiveIdsManagerBase implements Q
Long startIndex = cmd.getStartIndex();
Long pageSize = cmd.getPageSizeVal();
Hypervisor.HypervisorType hypervisorType = cmd.getHypervisor();
final CPU.CPUArch arch = cmd.getArch();
Filter searchFilter = new Filter(HostVO.class, "id", Boolean.TRUE, startIndex, pageSize);
@ -2370,6 +2377,7 @@ public class QueryManagerImpl extends MutualExclusiveIdsManagerBase implements Q
hostSearchBuilder.and("clusterId", hostSearchBuilder.entity().getClusterId(), SearchCriteria.Op.EQ);
hostSearchBuilder.and("resourceState", hostSearchBuilder.entity().getResourceState(), SearchCriteria.Op.EQ);
hostSearchBuilder.and("hypervisor_type", hostSearchBuilder.entity().getHypervisorType(), SearchCriteria.Op.EQ);
hostSearchBuilder.and("arch", hostSearchBuilder.entity().getArch(), SearchCriteria.Op.EQ);
if (keyword != null) {
hostSearchBuilder.and().op("keywordName", hostSearchBuilder.entity().getName(), SearchCriteria.Op.LIKE);
@ -2450,6 +2458,10 @@ public class QueryManagerImpl extends MutualExclusiveIdsManagerBase implements Q
sc.setParameters("hypervisor_type", hypervisorType);
}
if (arch != null) {
sc.setParameters("arch", arch);
}
Pair<List<HostVO>, Integer> uniqueHostPair = hostDao.searchAndCount(sc, searchFilter);
Integer count = uniqueHostPair.second();
List<Long> hostIds = uniqueHostPair.first().stream().map(HostVO::getId).collect(Collectors.toList());

View File

@ -88,6 +88,7 @@ public class DomainRouterJoinDaoImpl extends GenericDaoBase<DomainRouterJoinVO,
routerResponse.setZoneId(router.getDataCenterUuid());
routerResponse.setName(router.getName());
routerResponse.setTemplateId(router.getTemplateUuid());
routerResponse.setArch(router.getArch().getType());
VMTemplateVO template = ApiDBUtils.findTemplateById(router.getTemplateId());
if (template != null) {
routerResponse.setTemplateName(template.getName());

View File

@ -188,6 +188,7 @@ public class UserVmJoinDaoImpl extends GenericDaoBaseWithTagInformation<UserVmJo
userVmResponse.setInstanceName(userVm.getInstanceName());
userVmResponse.setHostId(userVm.getHostUuid());
userVmResponse.setHostName(userVm.getHostName());
userVmResponse.setArch(userVm.getArch());
}
if (userVm.getHostStatus() != null) {
userVmResponse.setHostControlState(ControlState.getControlState(userVm.getHostStatus(), userVm.getHostResourceState()).toString());

View File

@ -27,6 +27,7 @@ import javax.persistence.Enumerated;
import javax.persistence.Id;
import javax.persistence.Table;
import com.cloud.cpu.CPU;
import com.cloud.host.Status;
import com.cloud.hypervisor.Hypervisor;
import com.cloud.network.Network.GuestType;
@ -38,6 +39,8 @@ import com.cloud.user.Account;
import com.cloud.utils.db.GenericDao;
import com.cloud.vm.VirtualMachine;
import com.cloud.vm.VirtualMachine.State;
import org.apache.cloudstack.util.CPUArchConverter;
import org.apache.cloudstack.util.HypervisorTypeConverter;
@Entity
@ -143,6 +146,10 @@ public class DomainRouterJoinVO extends BaseViewVO implements ControlledViewEnti
@Convert(converter = HypervisorTypeConverter.class)
private Hypervisor.HypervisorType hypervisorType;
@Column(name="arch")
@Convert(converter = CPUArchConverter.class)
private CPU.CPUArch arch;
@Column(name = "template_id", updatable = true, nullable = true, length = 17)
private long templateId;
@ -376,6 +383,10 @@ public class DomainRouterJoinVO extends BaseViewVO implements ControlledViewEnti
return hypervisorType;
}
public CPU.CPUArch getArch() {
return arch;
}
public Long getClusterId() {
return clusterId;
}

View File

@ -439,6 +439,9 @@ public class UserVmJoinVO extends BaseViewWithTagInformationVO implements Contro
@Column(name = "delete_protection")
protected Boolean deleteProtection;
@Column(name = "arch")
protected String arch;
public UserVmJoinVO() {
// Empty constructor
@ -977,4 +980,8 @@ public class UserVmJoinVO extends BaseViewWithTagInformationVO implements Contro
public String getUserDataDetails() {
return userDataDetails;
}
public String getArch() {
return arch;
}
}

View File

@ -48,7 +48,6 @@ import org.apache.cloudstack.framework.security.keystore.KeystoreVO;
import org.apache.cloudstack.storage.datastore.db.PrimaryDataStoreDao;
import org.apache.cloudstack.storage.datastore.db.StoragePoolVO;
import org.apache.cloudstack.storage.datastore.db.TemplateDataStoreDao;
import org.apache.cloudstack.storage.datastore.db.TemplateDataStoreVO;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.BooleanUtils;
@ -82,9 +81,9 @@ import com.cloud.exception.InsufficientAddressCapacityException;
import com.cloud.exception.InsufficientCapacityException;
import com.cloud.exception.OperationTimedoutException;
import com.cloud.exception.ResourceUnavailableException;
import com.cloud.host.Host;
import com.cloud.host.Host.Type;
import com.cloud.host.HostVO;
import com.cloud.host.Status;
import com.cloud.host.dao.HostDao;
import com.cloud.hypervisor.Hypervisor.HypervisorType;
import com.cloud.info.ConsoleProxyConnectionInfo;
@ -111,10 +110,8 @@ import com.cloud.resource.ServerResource;
import com.cloud.resource.UnableDeleteHostException;
import com.cloud.service.ServiceOfferingVO;
import com.cloud.service.dao.ServiceOfferingDao;
import com.cloud.storage.DataStoreRole;
import com.cloud.storage.Storage;
import com.cloud.storage.StoragePoolStatus;
import com.cloud.storage.VMTemplateStorageResourceAssoc.Status;
import com.cloud.storage.VMTemplateVO;
import com.cloud.storage.dao.VMTemplateDao;
import com.cloud.user.Account;
@ -281,9 +278,9 @@ public class ConsoleProxyManagerImpl extends ManagerBase implements ConsoleProxy
}
@Override
public void onAgentDisconnect(long agentId, com.cloud.host.Status state) {
public void onAgentDisconnect(long agentId, Status state) {
if (state == com.cloud.host.Status.Alert || state == com.cloud.host.Status.Disconnected) {
if (state == Status.Alert || state == Status.Disconnected) {
HostVO host = _hostDao.findById(agentId);
if (host.getType() == Type.ConsoleProxy) {
String name = host.getName();
@ -465,7 +462,7 @@ public class ConsoleProxyManagerImpl extends ManagerBase implements ConsoleProxy
public ConsoleProxyVO startProxy(long proxyVmId, boolean ignoreRestartSetting) {
try {
ConsoleProxyVO proxy = consoleProxyDao.findById(proxyVmId);
if (proxy.getState() == VirtualMachine.State.Running) {
if (proxy.getState() == State.Running) {
return proxy;
}
@ -474,7 +471,7 @@ public class ConsoleProxyManagerImpl extends ManagerBase implements ConsoleProxy
return null;
}
if (proxy.getState() == VirtualMachine.State.Stopped) {
if (proxy.getState() == State.Stopped) {
virtualMachineManager.advanceStart(proxy.getUuid(), null, null);
proxy = consoleProxyDao.findById(proxy.getId());
return proxy;
@ -575,12 +572,13 @@ public class ConsoleProxyManagerImpl extends ManagerBase implements ConsoleProxy
}
HypervisorType availableHypervisor = resourceManager.getAvailableHypervisor(dataCenterId);
VMTemplateVO template = vmTemplateDao.findSystemVMReadyTemplate(dataCenterId, availableHypervisor);
if (template == null) {
List<VMTemplateVO> templates = vmTemplateDao.findSystemVMReadyTemplates(dataCenterId, availableHypervisor,
ResourceManager.SystemVmPreferredArchitecture.valueIn(dataCenterId));
if (CollectionUtils.isEmpty(templates)) {
throw new CloudRuntimeException("Not able to find the System templates or not downloaded in zone " + dataCenterId);
}
Map<String, Object> context = createProxyInstance(dataCenterId, template);
Map<String, Object> context = createProxyInstance(dataCenterId, templates);
long proxyVmId = (Long)context.get("proxyVmId");
if (proxyVmId == 0) {
@ -673,7 +671,26 @@ public class ConsoleProxyManagerImpl extends ManagerBase implements ConsoleProxy
return defaultNetworks.get(0);
}
protected Map<String, Object> createProxyInstance(long dataCenterId, VMTemplateVO template) throws ConcurrentOperationException {
protected ConsoleProxyVO createOrUpdateConsoleProxy(ConsoleProxyVO proxy, long dataCenterId, long id, String name,
ServiceOffering serviceOffering, VMTemplateVO template, Account systemAccount) {
if (proxy == null) {
proxy = new ConsoleProxyVO(id, serviceOffering.getId(), name, template.getId(),
template.getHypervisorType(), template.getGuestOSId(), dataCenterId, systemAccount.getDomainId(),
systemAccount.getId(), accountManager.getSystemUser().getId(), 0,
serviceOffering.isOfferHA());
proxy.setDynamicallyScalable(template.isDynamicallyScalable());
proxy.setLimitCpuUse(serviceOffering.getLimitCpuUse());
return consoleProxyDao.persist(proxy);
}
proxy.setTemplateId(template.getId());
proxy.setHypervisorType(template.getHypervisorType());
proxy.setGuestOSId(template.getGuestOSId());
proxy.setDynamicallyScalable(template.isDynamicallyScalable());
consoleProxyDao.update(proxy.getId(), proxy);
return proxy;
}
protected Map<String, Object> createProxyInstance(long dataCenterId, List<VMTemplateVO> templates) throws ConcurrentOperationException {
long id = consoleProxyDao.getNextInSequence(Long.class, "id");
String name = VirtualMachineName.getConsoleProxyName(id, instance);
@ -702,18 +719,23 @@ public class ConsoleProxyManagerImpl extends ManagerBase implements ConsoleProxy
if (serviceOffering == null) {
serviceOffering = serviceOfferingDao.findDefaultSystemOffering(ServiceOffering.consoleProxyDefaultOffUniqueName, ConfigurationManagerImpl.SystemVMUseLocalStorage.valueIn(dataCenterId));
}
ConsoleProxyVO proxy =
new ConsoleProxyVO(id, serviceOffering.getId(), name, template.getId(), template.getHypervisorType(), template.getGuestOSId(), dataCenterId,
systemAcct.getDomainId(), systemAcct.getId(), accountManager.getSystemUser().getId(), 0, serviceOffering.isOfferHA());
proxy.setDynamicallyScalable(template.isDynamicallyScalable());
proxy.setLimitCpuUse(serviceOffering.getLimitCpuUse());
proxy = consoleProxyDao.persist(proxy);
try {
virtualMachineManager.allocate(name, template, serviceOffering, networks, plan, null);
} catch (InsufficientCapacityException e) {
String message = String.format("Unable to allocate proxy [%s] on zone [%s] due to [%s].", proxy.toString(), dataCenterId, e.getMessage());
logger.warn(message, e);
throw new CloudRuntimeException(message, e);
ConsoleProxyVO proxy = null;
for (final Iterator<VMTemplateVO> templateIterator = templates.iterator(); templateIterator.hasNext();) {
VMTemplateVO template = templateIterator.next();
proxy = createOrUpdateConsoleProxy(proxy, dataCenterId, id, name, serviceOffering, template, systemAcct);
try {
virtualMachineManager.allocate(name, template, serviceOffering, networks, plan,
template.getHypervisorType());
proxy = consoleProxyDao.findById(proxy.getId());
virtualMachineManager.checkDeploymentPlan(proxy, template, serviceOffering, systemAcct, plan);
break;
} catch (InsufficientCapacityException e) {
if (templateIterator.hasNext()) {
logger.debug("Unable to allocate proxy {} with {} in {} due to [{}]. Retrying with another template", proxy, template, dc, e.getMessage(), e);
continue;
}
throw new CloudRuntimeException("Failed to allocate proxy [%s] in zone [%s] with available templates", e);
}
}
Map<String, Object> context = new HashMap<>();
@ -737,8 +759,8 @@ public class ConsoleProxyManagerImpl extends ManagerBase implements ConsoleProxy
updateConsoleProxyStatus(answer.getDetails(), answer.getProxyVmId());
}
public void handleAgentDisconnect(long agentId, com.cloud.host.Status state) {
if (state == com.cloud.host.Status.Alert || state == com.cloud.host.Status.Disconnected) {
public void handleAgentDisconnect(long agentId, Status state) {
if (state == Status.Alert || state == Status.Disconnected) {
HostVO host = hostDao.findById(agentId);
if (host.getType() == Type.ConsoleProxy) {
String name = host.getName();
@ -789,8 +811,8 @@ public class ConsoleProxyManagerImpl extends ManagerBase implements ConsoleProxy
return false;
}
List<ConsoleProxyVO> l =
consoleProxyDao.getProxyListInStates(dcId, VirtualMachine.State.Starting, VirtualMachine.State.Running, VirtualMachine.State.Stopping,
VirtualMachine.State.Stopped, VirtualMachine.State.Migrating, VirtualMachine.State.Shutdown, VirtualMachine.State.Unknown);
consoleProxyDao.getProxyListInStates(dcId, State.Starting, State.Running, State.Stopping,
State.Stopped, State.Migrating, State.Shutdown, State.Unknown);
String value = configurationDao.getValue(Config.ConsoleProxyLaunchMax.key());
int launchLimit = NumbersUtil.parseInt(value, 10);
@ -876,33 +898,21 @@ public class ConsoleProxyManagerImpl extends ManagerBase implements ConsoleProxy
}
ZoneHostInfo zoneHostInfo = zoneHostInfoMap.get(dataCenter.getId());
if (zoneHostInfo != null && isZoneHostReady(zoneHostInfo)) {
VMTemplateVO template = vmTemplateDao.findSystemVMReadyTemplate(dataCenter.getId(), HypervisorType.Any);
if (template == null) {
List<VMTemplateVO> templates = vmTemplateDao.findSystemVMReadyTemplates(dataCenter.getId(),
HypervisorType.Any, null);
if (CollectionUtils.isEmpty(templates)) {
if (logger.isDebugEnabled()) {
logger.debug("System vm template is not ready at data center {}, wait until it is ready to launch console proxy vm", dataCenter);
}
return false;
}
TemplateDataStoreVO templateHostRef;
if (template.isDirectDownload()) {
templateHostRef = templateDataStoreDao.findByTemplate(template.getId(), DataStoreRole.Image);
} else {
templateHostRef = templateDataStoreDao.findByTemplateZoneDownloadStatus(template.getId(), dataCenter.getId(), Status.DOWNLOADED);
}
if (templateHostRef != null) {
Boolean useLocalStorage = BooleanUtils.toBoolean(ConfigurationManagerImpl.SystemVMUseLocalStorage.valueIn(dataCenter.getId()));
boolean hasDatacenterStoragePoolHostInfo = consoleProxyDao.hasDatacenterStoragePoolHostInfo(dataCenter.getId(), !useLocalStorage);
if (hasDatacenterStoragePoolHostInfo) {
return true;
} else {
if (logger.isDebugEnabled()) {
logger.debug("Primary storage is not ready, wait until it is ready to launch console proxy");
}
}
boolean useLocalStorage = BooleanUtils.toBoolean(ConfigurationManagerImpl.SystemVMUseLocalStorage.valueIn(dataCenter.getId()));
boolean hasDatacenterStoragePoolHostInfo = consoleProxyDao.hasDatacenterStoragePoolHostInfo(dataCenter.getId(), !useLocalStorage);
if (hasDatacenterStoragePoolHostInfo) {
return true;
} else {
if (logger.isDebugEnabled()) {
logger.debug("Zone [{}] is ready, but console proxy template [{}] is not ready on secondary storage.", dataCenter, template);
logger.debug("Primary storage is not ready, wait until it is ready to launch console proxy");
}
}
}
@ -912,9 +922,9 @@ public class ConsoleProxyManagerImpl extends ManagerBase implements ConsoleProxy
private boolean isZoneHostReady(ZoneHostInfo zoneHostInfo) {
int expectedFlags;
if (useStorageVm) {
expectedFlags = RunningHostInfoAgregator.ZoneHostInfo.ROUTING_HOST_MASK;
expectedFlags = ZoneHostInfo.ROUTING_HOST_MASK;
} else {
expectedFlags = RunningHostInfoAgregator.ZoneHostInfo.ALL_HOST_MASK;
expectedFlags = ZoneHostInfo.ALL_HOST_MASK;
}
return (zoneHostInfo.getFlags() & expectedFlags) == expectedFlags;
@ -1093,7 +1103,7 @@ public class ConsoleProxyManagerImpl extends ManagerBase implements ConsoleProxy
proxy.setPrivateIpAddress(null);
consoleProxyDao.update(proxy.getId(), proxy);
consoleProxyDao.remove(vmId);
HostVO host = hostDao.findByTypeNameAndZoneId(proxy.getDataCenterId(), proxy.getHostName(), Host.Type.ConsoleProxy);
HostVO host = hostDao.findByTypeNameAndZoneId(proxy.getDataCenterId(), proxy.getHostName(), Type.ConsoleProxy);
if (host != null) {
logger.debug("Removing host [{}] entry for proxy [{}].", host, proxy);
return hostDao.remove(host.getId());
@ -1511,7 +1521,7 @@ public class ConsoleProxyManagerImpl extends ManagerBase implements ConsoleProxy
return false;
}
List<ConsoleProxyVO> l = consoleProxyDao.getProxyListInStates(VirtualMachine.State.Starting, VirtualMachine.State.Stopping);
List<ConsoleProxyVO> l = consoleProxyDao.getProxyListInStates(State.Starting, State.Stopping);
if (l.size() > 0) {
if (logger.isDebugEnabled()) {
logger.debug("Zone {} has {} console proxy VM(s) in transition state", zone, l.size());
@ -1568,7 +1578,7 @@ public class ConsoleProxyManagerImpl extends ManagerBase implements ConsoleProxy
return null;
}
host.setType(com.cloud.host.Host.Type.ConsoleProxy);
host.setType(Type.ConsoleProxy);
return host;
}
@ -1584,7 +1594,7 @@ public class ConsoleProxyManagerImpl extends ManagerBase implements ConsoleProxy
protected HostVO findConsoleProxyHostByName(String name) {
QueryBuilder<HostVO> sc = QueryBuilder.create(HostVO.class);
sc.and(sc.entity().getType(), Op.EQ, Host.Type.ConsoleProxy);
sc.and(sc.entity().getType(), Op.EQ, Type.ConsoleProxy);
sc.and(sc.entity().getName(), Op.EQ, name);
return sc.find();
}

View File

@ -28,8 +28,6 @@ import java.util.Map;
import javax.annotation.PostConstruct;
import javax.inject.Inject;
import com.cloud.network.vpc.dao.VpcDao;
import com.cloud.utils.validation.ChecksumUtil;
import org.apache.cloudstack.api.ApiConstants;
import org.apache.cloudstack.context.CallContext;
import org.apache.cloudstack.engine.orchestration.service.NetworkOrchestrationService;
@ -38,8 +36,8 @@ import org.apache.cloudstack.framework.config.dao.ConfigurationDao;
import org.apache.cloudstack.network.router.deployment.RouterDeploymentDefinition;
import org.apache.cloudstack.utils.CloudStackVersion;
import org.apache.commons.collections.CollectionUtils;
import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import com.cloud.agent.AgentManager;
import com.cloud.agent.api.Answer;
@ -86,6 +84,7 @@ import com.cloud.network.lb.LoadBalancingRule;
import com.cloud.network.router.VirtualRouter.RedundantState;
import com.cloud.network.router.VirtualRouter.Role;
import com.cloud.network.rules.LbStickinessMethod;
import com.cloud.network.vpc.dao.VpcDao;
import com.cloud.network.vpn.Site2SiteVpnManager;
import com.cloud.offering.NetworkOffering;
import com.cloud.resource.ResourceManager;
@ -104,6 +103,7 @@ import com.cloud.user.dao.UserDao;
import com.cloud.utils.Pair;
import com.cloud.utils.exception.CloudRuntimeException;
import com.cloud.utils.net.NetUtils;
import com.cloud.utils.validation.ChecksumUtil;
import com.cloud.vm.DomainRouterVO;
import com.cloud.vm.Nic;
import com.cloud.vm.NicProfile;
@ -498,18 +498,77 @@ public class NetworkHelperImpl implements NetworkHelper {
return templateName;
}
protected DomainRouterVO createOrUpdateDomainRouter(DomainRouterVO router, final long id,
final RouterDeploymentDefinition routerDeploymentDefinition, final Account owner, final long userId,
final ServiceOfferingVO routerOffering, final boolean offerHA, final Long vpcId,
final VMTemplateVO template) {
if (router == null) {
router = new DomainRouterVO(id, routerOffering.getId(),
routerDeploymentDefinition.getVirtualProvider().getId(),
VirtualMachineName.getRouterName(id, s_vmInstanceName), template.getId(),
template.getHypervisorType(), template.getGuestOSId(), owner.getDomainId(), owner.getId(),
userId, routerDeploymentDefinition.isRedundant(), RedundantState.UNKNOWN, offerHA, false,
vpcId);
router.setDynamicallyScalable(template.isDynamicallyScalable());
router.setRole(Role.VIRTUAL_ROUTER);
router.setLimitCpuUse(routerOffering.getLimitCpuUse());
return _routerDao.persist(router);
}
router.setTemplateId(template.getId());
router.setDynamicallyScalable(template.isDynamicallyScalable());
_routerDao.update(router.getId(), router);
return router;
}
protected DomainRouterVO deployRouterWithTemplates(DomainRouterVO router, final long id,
final RouterDeploymentDefinition routerDeploymentDefinition, final Account owner, final long userId,
final ServiceOfferingVO routerOffering, final boolean offerHA, final Long vpcId,
final List<VMTemplateVO> templates) throws InsufficientCapacityException {
for (final Iterator<VMTemplateVO> templatesIterator = templates.iterator(); templatesIterator.hasNext();) {
final VMTemplateVO template = templatesIterator.next();
try {
router = createOrUpdateDomainRouter(router, id, routerDeploymentDefinition, owner, userId,
routerOffering, offerHA, vpcId, template);
reallocateRouterNetworks(routerDeploymentDefinition, router, template, null);
router = _routerDao.findById(router.getId());
if (templatesIterator.hasNext()) {
_itMgr.checkDeploymentPlan(router, template, routerOffering, owner,
routerDeploymentDefinition.getPlan());
}
return router;
} catch (InsufficientCapacityException ex) {
if (templatesIterator.hasNext()) {
logger.debug("Failed to allocate the VR with hypervisor {} and {}, retrying with another template", template.getHypervisorType(), template);
} else {
throw ex;
}
}
}
return null;
}
@Override
public DomainRouterVO deployRouter(final RouterDeploymentDefinition routerDeploymentDefinition, final boolean startRouter)
throws InsufficientAddressCapacityException, InsufficientServerCapacityException, InsufficientCapacityException, StorageUnavailableException, ResourceUnavailableException {
final ServiceOfferingVO routerOffering = _serviceOfferingDao.findById(routerDeploymentDefinition.getServiceOfferingId());
final boolean offerHA = routerOffering.isOfferHA();
final Account owner = routerDeploymentDefinition.getOwner();
final Long vpcId = routerDeploymentDefinition.getVpc() != null ? routerDeploymentDefinition.getVpc().getId() : null;
// Router is the network element, we don't know the hypervisor type yet.
// Try to allocate the domR twice using diff hypervisors, and when
// failed both times, throw the exception up
final List<HypervisorType> hypervisors = getHypervisors(routerDeploymentDefinition);
long userId = CallContext.current().getCallingUserId();
if (CallContext.current().getCallingAccount().getId() != owner.getId()) {
final List<UserVO> userVOs = _userDao.listByAccount(owner.getAccountId());
if (!userVOs.isEmpty()) {
userId = userVOs.get(0).getId();
}
}
DomainRouterVO router = null;
for (final Iterator<HypervisorType> iter = hypervisors.iterator(); iter.hasNext();) {
final HypervisorType hType = iter.next();
@ -521,43 +580,20 @@ public class NetworkHelperImpl implements NetworkHelper {
logger.debug(String.format("Allocating the VR with id=%s in datacenter %s with the hypervisor type %s", id, routerDeploymentDefinition.getDest()
.getDataCenter(), hType));
}
final String templateName = retrieveTemplateName(hType, routerDeploymentDefinition.getDest().getDataCenter().getId());
final VMTemplateVO template = _templateDao.findRoutingTemplate(hType, templateName);
if (template == null) {
logger.debug(hType + " won't support system vm, skip it");
final long zoneId = routerDeploymentDefinition.getDest().getDataCenter().getId();
final String templateName = retrieveTemplateName(hType, zoneId);
final String preferredArch = ResourceManager.SystemVmPreferredArchitecture.valueIn(zoneId);
final List<VMTemplateVO> templates = _templateDao.findRoutingTemplates(hType, templateName,
preferredArch);
if (CollectionUtils.isEmpty(templates)) {
logger.debug("{} won't support system vm, skip it", hType);
continue;
}
final boolean offerHA = routerOffering.isOfferHA();
// routerDeploymentDefinition.getVpc().getId() ==> do not use
// VPC because it is not a VPC offering.
final Long vpcId = routerDeploymentDefinition.getVpc() != null ? routerDeploymentDefinition.getVpc().getId() : null;
long userId = CallContext.current().getCallingUserId();
if (CallContext.current().getCallingAccount().getId() != owner.getId()) {
final List<UserVO> userVOs = _userDao.listByAccount(owner.getAccountId());
if (!userVOs.isEmpty()) {
userId = userVOs.get(0).getId();
}
}
router = new DomainRouterVO(id, routerOffering.getId(), routerDeploymentDefinition.getVirtualProvider().getId(), VirtualMachineName.getRouterName(id,
s_vmInstanceName), template.getId(), template.getHypervisorType(), template.getGuestOSId(), owner.getDomainId(), owner.getId(),
userId, routerDeploymentDefinition.isRedundant(), RedundantState.UNKNOWN, offerHA, false, vpcId);
router.setDynamicallyScalable(template.isDynamicallyScalable());
router.setRole(Role.VIRTUAL_ROUTER);
router.setLimitCpuUse(routerOffering.getLimitCpuUse());
router = _routerDao.persist(router);
reallocateRouterNetworks(routerDeploymentDefinition, router, template, null);
router = _routerDao.findById(router.getId());
router = deployRouterWithTemplates(router, id, routerDeploymentDefinition, owner, userId,
routerOffering, offerHA, vpcId, templates);
} catch (final InsufficientCapacityException ex) {
if (iter.hasNext()) {
logger.debug("Failed to allocate the VR with hypervisor type " + hType + ", retrying one more time");
logger.debug("Failed to allocate the VR with {}, retrying with another hypervisor", hType);
continue;
} else {
throw ex;

View File

@ -1201,6 +1201,12 @@ public class ResourceManagerImpl extends ManagerBase implements ResourceManager,
}
if (arch != null) {
List<CPU.CPUArch> architectureTypes = _hostDao.listDistinctArchTypes(cluster.getId());
if (architectureTypes.stream().anyMatch(a -> !a.equals(arch))) {
throw new InvalidParameterValueException(String.format(
"Cluster has host(s) present with arch type(s): %s",
StringUtils.join(architectureTypes.stream().map(CPU.CPUArch::getType).toArray())));
}
cluster.setArch(arch.getType());
doUpdate = true;
}
@ -3537,6 +3543,10 @@ public class ResourceManagerImpl extends ManagerBase implements ResourceManager,
@Override
public ConfigKey<?>[] getConfigKeys() {
return new ConfigKey<?>[] {KvmSshToAgentEnabled, HOST_MAINTENANCE_LOCAL_STRATEGY};
return new ConfigKey<?>[] {
KvmSshToAgentEnabled,
HOST_MAINTENANCE_LOCAL_STRATEGY,
SystemVmPreferredArchitecture
};
}
}

View File

@ -44,7 +44,6 @@ import javax.crypto.spec.SecretKeySpec;
import javax.inject.Inject;
import javax.naming.ConfigurationException;
import com.cloud.cpu.CPU;
import org.apache.cloudstack.acl.ControlledEntity;
import org.apache.cloudstack.acl.SecurityChecker;
import org.apache.cloudstack.affinity.AffinityGroupProcessor;
@ -674,6 +673,7 @@ import com.cloud.configuration.Config;
import com.cloud.configuration.ConfigurationManagerImpl;
import com.cloud.consoleproxy.ConsoleProxyManagementState;
import com.cloud.consoleproxy.ConsoleProxyManager;
import com.cloud.cpu.CPU;
import com.cloud.dc.AccountVlanMapVO;
import com.cloud.dc.ClusterVO;
import com.cloud.dc.DataCenterVO;
@ -1274,6 +1274,7 @@ public class ManagementServerImpl extends ManagerBase implements ManagementServe
final Object clusterType = cmd.getClusterType();
final Object allocationState = cmd.getAllocationState();
final String keyword = cmd.getKeyword();
final CPU.CPUArch arch = cmd.getArch();
zoneId = _accountMgr.checkAccessAndSpecifyAuthority(CallContext.current().getCallingAccount(), zoneId);
final Filter searchFilter = new Filter(ClusterVO.class, "id", true, cmd.getStartIndex(), cmd.getPageSizeVal());
@ -1286,6 +1287,7 @@ public class ManagementServerImpl extends ManagerBase implements ManagementServe
sb.and("hypervisorType", sb.entity().getHypervisorType(), SearchCriteria.Op.EQ);
sb.and("clusterType", sb.entity().getClusterType(), SearchCriteria.Op.EQ);
sb.and("allocationState", sb.entity().getAllocationState(), SearchCriteria.Op.EQ);
sb.and("arch", sb.entity().getArch(), SearchCriteria.Op.EQ);
final SearchCriteria<ClusterVO> sc = sb.create();
if (id != null) {
@ -1325,6 +1327,10 @@ public class ManagementServerImpl extends ManagerBase implements ManagementServe
sc.addAnd("name", SearchCriteria.Op.SC, ssc);
}
if (arch != null) {
sc.setParameters("arch", arch);
}
final Pair<List<ClusterVO>, Integer> result = _clusterDao.searchAndCount(sc, searchFilter);
return new Pair<>(result.first(), result.second());
}
@ -4187,6 +4193,7 @@ public class ManagementServerImpl extends ManagerBase implements ManagementServe
final Long podId = cmd.getPodId();
final Long hostId = cmd.getHostId();
final Long storageId = cmd.getStorageId();
final CPU.CPUArch arch = cmd.getArch();
final Filter searchFilter = new Filter(VMInstanceVO.class, "id", true, cmd.getStartIndex(), cmd.getPageSizeVal());
final SearchBuilder<VMInstanceVO> sb = _vmInstanceDao.createSearchBuilder();
@ -4213,6 +4220,13 @@ public class ManagementServerImpl extends ManagerBase implements ManagementServe
}
}
boolean templateJoinNeeded = arch != null;
if (templateJoinNeeded) {
SearchBuilder<VMTemplateVO> templateSearch = templateDao.createSearchBuilder();
templateSearch.and("templateArch", templateSearch.entity().getArch(), SearchCriteria.Op.EQ);
sb.join("vmTemplate", templateSearch, templateSearch.entity().getId(), sb.entity().getTemplateId(), JoinBuilder.JoinType.INNER);
}
final SearchCriteria<VMInstanceVO> sc = sb.create();
if (keyword != null) {
@ -4260,6 +4274,10 @@ public class ManagementServerImpl extends ManagerBase implements ManagementServe
}
}
if (arch != null) {
sc.setJoinParameters("vmTemplate", "templateArch", arch);
}
final Pair<List<VMInstanceVO>, Integer> result = _vmInstanceDao.searchAndCount(sc, searchFilter);
return new Pair<>(result.first(), result.second());
}

View File

@ -178,6 +178,7 @@ import com.cloud.configuration.Config;
import com.cloud.configuration.ConfigurationManager;
import com.cloud.configuration.ConfigurationManagerImpl;
import com.cloud.configuration.Resource.ResourceType;
import com.cloud.cpu.CPU;
import com.cloud.dc.ClusterVO;
import com.cloud.dc.DataCenterVO;
import com.cloud.dc.VsphereStoragePolicyVO;
@ -3585,17 +3586,49 @@ public class StorageManagerImpl extends ManagerBase implements StorageManager, C
return (ImageStore)_dataStoreMgr.getDataStore(store.getId(), DataStoreRole.Image);
}
protected void registerSystemVmTemplateForHypervisorArch(final HypervisorType hypervisorType,
final CPU.CPUArch arch, final Long zoneId, final String url, final DataStore store,
final SystemVmTemplateRegistration systemVmTemplateRegistration, final String filePath,
final Pair<String, Long> storeUrlAndId, final String nfsVersion) {
if (HypervisorType.Simulator.equals(hypervisorType)) {
return;
}
String templateName = getValidTemplateName(zoneId, hypervisorType);
VMTemplateVO registeredTemplate = systemVmTemplateRegistration.getRegisteredTemplate(templateName, arch);
TemplateDataStoreVO templateDataStoreVO = null;
if (registeredTemplate != null) {
templateDataStoreVO = _templateStoreDao.findByStoreTemplate(store.getId(), registeredTemplate.getId());
if (templateDataStoreVO != null) {
try {
if (systemVmTemplateRegistration.validateIfSeeded(templateDataStoreVO, url,
templateDataStoreVO.getInstallPath(), nfsVersion)) {
return;
}
} catch (Exception e) {
logger.error("Failed to validated if template is seeded", e);
}
}
}
SystemVmTemplateRegistration.mountStore(storeUrlAndId.first(), filePath, nfsVersion);
if (templateDataStoreVO != null) {
systemVmTemplateRegistration.validateAndRegisterTemplate(hypervisorType, templateName,
storeUrlAndId.second(), registeredTemplate, templateDataStoreVO, filePath);
} else {
systemVmTemplateRegistration.validateAndRegisterTemplateForNonExistingEntries(hypervisorType, arch,
templateName, storeUrlAndId, filePath);
}
}
private void registerSystemVmTemplateOnFirstNfsStore(Long zoneId, String providerName, String url, DataStore store) {
if (DataStoreProvider.NFS_IMAGE.equals(providerName) && zoneId != null) {
Transaction.execute(new TransactionCallbackNoReturn() {
@Override
public void doInTransactionWithoutResult(final TransactionStatus status) {
List<ImageStoreVO> stores = _imageStoreDao.listAllStoresInZone(zoneId, providerName, DataStoreRole.Image);
stores = stores.stream().filter(str -> str.getId() != store.getId()).collect(Collectors.toList());
// Check if it's the only/first store in the zone
if (stores.size() == 0) {
List<HypervisorType> hypervisorTypes = _clusterDao.getAvailableHypervisorInZone(zoneId);
Set<HypervisorType> hypSet = new HashSet<>(hypervisorTypes);
List<ImageStoreVO> stores = _imageStoreDao.listAllStoresInZoneExceptId(zoneId, providerName,
DataStoreRole.Image, store.getId());
if (CollectionUtils.isEmpty(stores)) {
List<Pair<HypervisorType, CPU.CPUArch>> hypervisorTypes =
_clusterDao.listDistinctHypervisorsArchAcrossClusters(zoneId);
TransactionLegacy txn = TransactionLegacy.open("AutomaticTemplateRegister");
SystemVmTemplateRegistration systemVmTemplateRegistration = new SystemVmTemplateRegistration();
String filePath = null;
@ -3606,40 +3639,15 @@ public class StorageManagerImpl extends ManagerBase implements StorageManager, C
}
Pair<String, Long> storeUrlAndId = new Pair<>(url, store.getId());
String nfsVersion = imageStoreDetailsUtil.getNfsVersion(store.getId());
for (HypervisorType hypervisorType : hypSet) {
for (Pair<HypervisorType, CPU.CPUArch> hypervisorArchType : hypervisorTypes) {
try {
if (HypervisorType.Simulator == hypervisorType) {
continue;
}
String templateName = getValidTemplateName(zoneId, hypervisorType);
Pair<Hypervisor.HypervisorType, String> hypervisorAndTemplateName =
new Pair<>(hypervisorType, templateName);
Long templateId = systemVmTemplateRegistration.getRegisteredTemplateId(hypervisorAndTemplateName);
VMTemplateVO vmTemplateVO = null;
TemplateDataStoreVO templateVO = null;
if (templateId != null) {
vmTemplateVO = _templateDao.findById(templateId);
templateVO = _templateStoreDao.findByStoreTemplate(store.getId(), templateId);
if (templateVO != null) {
try {
if (systemVmTemplateRegistration.validateIfSeeded(
templateVO, url, templateVO.getInstallPath(), nfsVersion)) {
continue;
}
} catch (Exception e) {
logger.error("Failed to validated if template is seeded", e);
}
}
}
SystemVmTemplateRegistration.mountStore(storeUrlAndId.first(), filePath, nfsVersion);
if (templateVO != null && vmTemplateVO != null) {
systemVmTemplateRegistration.registerTemplate(hypervisorAndTemplateName, storeUrlAndId, vmTemplateVO, templateVO, filePath);
} else {
systemVmTemplateRegistration.registerTemplate(hypervisorAndTemplateName, storeUrlAndId, filePath);
}
registerSystemVmTemplateForHypervisorArch(hypervisorArchType.first(),
hypervisorArchType.second(), zoneId, url, store,
systemVmTemplateRegistration, filePath, storeUrlAndId, nfsVersion);
} catch (CloudRuntimeException e) {
SystemVmTemplateRegistration.unmountStore(filePath);
logger.error(String.format("Failed to register systemVM template for hypervisor: %s", hypervisorType.name()), e);
logger.error("Failed to register system VM template for hypervisor: {} {}",
hypervisorArchType.first().name(), hypervisorArchType.second().name(), e);
}
}
} catch (Exception e) {

View File

@ -21,6 +21,7 @@ import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import javax.inject.Inject;
@ -243,7 +244,11 @@ public abstract class TemplateAdapterBase extends AdapterBase implements Templat
List<VMTemplateVO> systemvmTmplts = _tmpltDao.listAllSystemVMTemplates();
for (VMTemplateVO template : systemvmTmplts) {
if (template.getName().equalsIgnoreCase(name) || template.getDisplayText().equalsIgnoreCase(displayText)) {
if ((template.getName().equalsIgnoreCase(name) ||
template.getDisplayText().equalsIgnoreCase(displayText)) &&
Objects.equals(template.getArch(), arch)) {
logger.error("{} for same arch {} is having same name or description", template,
template.getArch());
throw new IllegalArgumentException("Cannot use reserved names for templates");
}
}

View File

@ -0,0 +1,107 @@
// 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 com.cloud.consoleproxy;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnitRunner;
import com.cloud.hypervisor.Hypervisor;
import com.cloud.offering.ServiceOffering;
import com.cloud.storage.VMTemplateVO;
import com.cloud.user.Account;
import com.cloud.user.AccountManager;
import com.cloud.user.User;
import com.cloud.vm.ConsoleProxyVO;
import com.cloud.vm.dao.ConsoleProxyDao;
@RunWith(MockitoJUnitRunner.class)
public class ConsoleProxyManagerImplTest {
@InjectMocks
private ConsoleProxyManagerImpl consoleProxyManager;
@Mock
private ConsoleProxyDao consoleProxyDao;
@Mock
private AccountManager accountManager;
@Mock
private ServiceOffering serviceOffering;
@Mock
private VMTemplateVO template;
@Mock
private Account systemAccount;
@Mock
private User systemUser;
@Before
public void setUp() {
when(accountManager.getSystemUser()).thenReturn(systemUser);
}
@Test
public void testCreateConsoleProxy_New() {
long dataCenterId = 1L;
long id = 10L;
String name = "console1";
// When creating a new proxy, persist should be called.
when(consoleProxyDao.persist(any(ConsoleProxyVO.class)))
.thenAnswer(invocation -> invocation.getArgument(0));
ConsoleProxyVO result = consoleProxyManager.createOrUpdateConsoleProxy(null, dataCenterId, id, name, serviceOffering, template, systemAccount);
assertNotNull(result);
assertEquals(id, result.getId());
assertEquals(serviceOffering.getId(), result.getServiceOfferingId());
assertEquals(name, result.getName());
assertEquals(template.getId(), result.getTemplateId());
assertEquals(template.getHypervisorType(), result.getHypervisorType());
assertEquals(template.getGuestOSId(), result.getGuestOSId());
assertEquals(dataCenterId, result.getDataCenterId());
assertEquals(systemAccount.getDomainId(), result.getDomainId());
assertEquals(systemAccount.getId(), result.getAccountId());
assertEquals(serviceOffering.isOfferHA(), result.isHaEnabled());
assertEquals(template.isDynamicallyScalable(), result.isDynamicallyScalable());
assertEquals(serviceOffering.getLimitCpuUse(), result.limitCpuUse());
verify(consoleProxyDao).persist(any(ConsoleProxyVO.class));
}
@Test
public void testUpdateConsoleProxy() {
long dataCenterId = 1L;
long id = 10L;
String name = "console1";
ConsoleProxyVO existing = new ConsoleProxyVO(id, serviceOffering.getId(), name, 999L, Hypervisor.HypervisorType.KVM, 111L,
dataCenterId, systemAccount.getDomainId(), systemAccount.getId(),
systemUser.getId(), 0, serviceOffering.isOfferHA());
existing.setDynamicallyScalable(false);
ConsoleProxyVO result = consoleProxyManager.createOrUpdateConsoleProxy(existing, dataCenterId, id, name, serviceOffering, template, systemAccount);
verify(consoleProxyDao).update(existing.getId(), existing);
assertEquals(template.getId(), result.getTemplateId());
assertEquals(template.getHypervisorType(), result.getHypervisorType());
assertEquals(template.getGuestOSId(), result.getGuestOSId());
assertEquals(template.isDynamicallyScalable(), result.isDynamicallyScalable());
}
}

View File

@ -16,27 +16,11 @@
// under the License.
package com.cloud.network.router;
import com.cloud.agent.AgentManager;
import com.cloud.agent.api.Answer;
import com.cloud.agent.api.Command;
import com.cloud.agent.manager.Commands;
import com.cloud.exception.AgentUnavailableException;
import com.cloud.exception.OperationTimedoutException;
import com.cloud.exception.ResourceUnavailableException;
import com.cloud.network.NetworkModel;
import com.cloud.network.dao.NetworkDao;
import com.cloud.vm.dao.NicDao;
import org.apache.cloudstack.engine.orchestration.service.NetworkOrchestrationService;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.InjectMocks;
import org.mockito.ArgumentMatchers;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnitRunner;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.lenient;
import static org.mockito.Mockito.mock;
@ -45,6 +29,34 @@ import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import org.apache.cloudstack.engine.orchestration.service.NetworkOrchestrationService;
import org.apache.cloudstack.network.router.deployment.RouterDeploymentDefinition;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnitRunner;
import com.cloud.agent.AgentManager;
import com.cloud.agent.api.Answer;
import com.cloud.agent.api.Command;
import com.cloud.agent.manager.Commands;
import com.cloud.exception.AgentUnavailableException;
import com.cloud.exception.OperationTimedoutException;
import com.cloud.exception.ResourceUnavailableException;
import com.cloud.hypervisor.Hypervisor;
import com.cloud.network.NetworkModel;
import com.cloud.network.VirtualRouterProvider;
import com.cloud.network.dao.NetworkDao;
import com.cloud.service.ServiceOfferingVO;
import com.cloud.storage.VMTemplateVO;
import com.cloud.user.Account;
import com.cloud.vm.DomainRouterVO;
import com.cloud.vm.VirtualMachineName;
import com.cloud.vm.dao.DomainRouterDao;
import com.cloud.vm.dao.NicDao;
@RunWith(MockitoJUnitRunner.class)
public class NetworkHelperImplTest {
@ -54,6 +66,9 @@ public class NetworkHelperImplTest {
@Mock
protected AgentManager agentManager;
@Mock
DomainRouterDao routerDao;
@InjectMocks
protected NetworkHelperImpl nwHelper = new NetworkHelperImpl();
@Mock
@ -65,10 +80,27 @@ public class NetworkHelperImplTest {
@Mock
NicDao nicDao;
@Mock
private RouterDeploymentDefinition routerDeploymentDefinition;
@Mock
private VirtualRouterProvider virtualProvider;
@Mock
private Account owner;
@Mock
private ServiceOfferingVO routerOffering;
@Mock
private VMTemplateVO template;
@Before
public void setUp() {
nwHelper._networkDao = networkDao;
nwHelper._networkModel = networkModel;
when(template.getId()).thenReturn(1L);
when(template.isDynamicallyScalable()).thenReturn(true);
when(virtualProvider.getId()).thenReturn(1L);
when(routerDeploymentDefinition.getVirtualProvider()).thenReturn(virtualProvider);
when(routerDeploymentDefinition.isRedundant()).thenReturn(true);
when(routerOffering.getLimitCpuUse()).thenReturn(true);
}
@Test(expected=ResourceUnavailableException.class)
@ -83,7 +115,7 @@ public class NetworkHelperImplTest {
nwHelperUT.sendCommandsToRouter(vr, null);
// Assert
verify(this.agentManager, times(0)).send((Long) ArgumentMatchers.any(), (Command) ArgumentMatchers.any());
verify(this.agentManager, times(0)).send((Long) any(), (Command) any());
}
@Test
@ -187,4 +219,53 @@ public class NetworkHelperImplTest {
verify(answer1, times(0)).getResult();
assertFalse(result);
}
@Test
public void testCreateDomainRouter_New() {
long id = 1L;
long userId = 800L;
boolean offerHA = false;
Long vpcId = 900L;
when(routerDao.persist(any(DomainRouterVO.class))).thenAnswer(invocation -> invocation.getArgument(0));
DomainRouterVO result = nwHelper.createOrUpdateDomainRouter(
null, id, routerDeploymentDefinition, owner, userId, routerOffering, offerHA, vpcId, template);
assertNotNull(result);
assertEquals(id, result.getId());
assertEquals(routerOffering.getId(), result.getServiceOfferingId());
assertEquals(1L, result.getElementId());
assertEquals(VirtualMachineName.getRouterName(id, NetworkHelperImpl.s_vmInstanceName), result.getInstanceName());
assertEquals(template.getId(), result.getTemplateId());
assertEquals(template.getHypervisorType(), result.getHypervisorType());
assertEquals(template.getGuestOSId(), result.getGuestOSId());
assertEquals(owner.getDomainId(), result.getDomainId());
assertEquals(owner.getId(), result.getAccountId());
assertEquals(userId, result.getUserId());
assertTrue(result.getIsRedundantRouter());
assertEquals(VirtualRouter.RedundantState.UNKNOWN, result.getRedundantState());
assertEquals(offerHA, result.isHaEnabled());
assertEquals(vpcId, result.getVpcId());
assertTrue(result.isDynamicallyScalable());
assertEquals(VirtualRouter.Role.VIRTUAL_ROUTER, result.getRole());
assertTrue(result.limitCpuUse());
verify(routerDao).persist(any(DomainRouterVO.class));
}
@Test
public void testUpdateDomainRouter() {
long id = 1L;
long userId = 800L;
boolean offerHA = false;
Long vpcId = 900L;
DomainRouterVO existing = new DomainRouterVO(id, routerOffering.getId(), virtualProvider.getId(),
VirtualMachineName.getRouterName(id, NetworkHelperImpl.s_vmInstanceName), 999L, Hypervisor.HypervisorType.KVM, 888L,
owner.getDomainId(), owner.getId(), userId, routerDeploymentDefinition.isRedundant(), VirtualRouter.RedundantState.UNKNOWN,
offerHA, false, vpcId);
existing.setDynamicallyScalable(false);
DomainRouterVO result = nwHelper.createOrUpdateDomainRouter(
existing, id, routerDeploymentDefinition, owner, userId, routerOffering, offerHA, vpcId, template);
verify(routerDao).update(existing.getId(), existing);
assertEquals(template.getId(), result.getTemplateId());
assertEquals(Hypervisor.HypervisorType.KVM, result.getHypervisorType());
assertTrue(result.isDynamicallyScalable());
}
}

View File

@ -25,6 +25,7 @@ import java.util.Arrays;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
@ -618,6 +619,25 @@ public class SecondaryStorageManagerImpl extends ManagerBase implements Secondar
return defaultNetworks.get(0);
}
protected SecondaryStorageVmVO createOrUpdateSecondaryStorageVm(SecondaryStorageVmVO ssvm, long dataCenterId,
long id, String name, ServiceOffering serviceOffering, VMTemplateVO template, Account systemAccount,
SecondaryStorageVm.Role role) {
if (ssvm == null) {
ssvm = new SecondaryStorageVmVO(id, serviceOffering.getId(), name, template.getId(),
template.getHypervisorType(), template.getGuestOSId(), dataCenterId, systemAccount.getDomainId(),
systemAccount.getId(), _accountMgr.getSystemUser().getId(), role, serviceOffering.isOfferHA());
ssvm.setDynamicallyScalable(template.isDynamicallyScalable());
ssvm.setLimitCpuUse(serviceOffering.getLimitCpuUse());
return _secStorageVmDao.persist(ssvm);
}
ssvm.setTemplateId(template.getId());
ssvm.setHypervisorType(template.getHypervisorType());
ssvm.setGuestOSId(template.getGuestOSId());
ssvm.setDynamicallyScalable(template.isDynamicallyScalable());
_secStorageVmDao.update(ssvm.getId(), ssvm);
return ssvm;
}
protected Map<String, Object> createSecStorageVmInstance(long dataCenterId, SecondaryStorageVm.Role role) {
DataStore secStore = _dataStoreMgr.getImageStoreWithFreeCapacity(dataCenterId);
if (secStore == null) {
@ -657,28 +677,33 @@ public class SecondaryStorageManagerImpl extends ManagerBase implements Secondar
}
HypervisorType availableHypervisor = _resourceMgr.getAvailableHypervisor(dataCenterId);
VMTemplateVO template = _templateDao.findSystemVMReadyTemplate(dataCenterId, availableHypervisor);
if (template == null) {
throw new CloudRuntimeException(String.format("Unable to find the system templates or it was not downloaded in %s.", dc.toString()));
List<VMTemplateVO> templates = _templateDao.findSystemVMReadyTemplates(dataCenterId, availableHypervisor,
ResourceManager.SystemVmPreferredArchitecture.valueIn(dataCenterId));
if (CollectionUtils.isEmpty(templates)) {
throw new CloudRuntimeException(String.format("Unable to find the system templates or it was not downloaded in %s.", dc));
}
ServiceOfferingVO serviceOffering = _serviceOffering;
if (serviceOffering == null) {
serviceOffering = _offeringDao.findDefaultSystemOffering(ServiceOffering.ssvmDefaultOffUniqueName, ConfigurationManagerImpl.SystemVMUseLocalStorage.valueIn(dataCenterId));
}
SecondaryStorageVmVO secStorageVm =
new SecondaryStorageVmVO(id, serviceOffering.getId(), name, template.getId(), template.getHypervisorType(), template.getGuestOSId(), dataCenterId,
systemAcct.getDomainId(), systemAcct.getId(), _accountMgr.getSystemUser().getId(), role, serviceOffering.isOfferHA());
secStorageVm.setDynamicallyScalable(template.isDynamicallyScalable());
secStorageVm.setLimitCpuUse(serviceOffering.getLimitCpuUse());
secStorageVm = _secStorageVmDao.persist(secStorageVm);
try {
_itMgr.allocate(name, template, serviceOffering, networks, plan, null);
secStorageVm = _secStorageVmDao.findById(secStorageVm.getId());
} catch (InsufficientCapacityException e) {
String errorMessage = String.format("Unable to allocate secondary storage VM [%s] due to [%s].", name, e.getMessage());
logger.warn(errorMessage, e);
throw new CloudRuntimeException(errorMessage, e);
SecondaryStorageVmVO secStorageVm = null;
for (final Iterator<VMTemplateVO> templateIterator = templates.iterator(); templateIterator.hasNext();) {
VMTemplateVO template = templateIterator.next();
secStorageVm = createOrUpdateSecondaryStorageVm(secStorageVm, dataCenterId, id, name, serviceOffering,
template, systemAcct, role);
try {
_itMgr.allocate(name, template, serviceOffering, networks, plan, template.getHypervisorType());
secStorageVm = _secStorageVmDao.findById(secStorageVm.getId());
_itMgr.checkDeploymentPlan(secStorageVm, template, serviceOffering, systemAcct, plan);
break;
} catch (InsufficientCapacityException e) {
if (templateIterator.hasNext()) {
logger.debug("Unable to allocate secondary storage {} with {} due to [{}]. Retrying with another template", secStorageVm, template, e.getMessage(), e);
continue;
}
throw new CloudRuntimeException("Failed to allocate secondary storage VM [%s] in zone [%s] with available templates", e);
}
}
Map<String, Object> context = new HashMap<>();
@ -820,8 +845,9 @@ public class SecondaryStorageManagerImpl extends ManagerBase implements Secondar
}
ZoneHostInfo zoneHostInfo = zoneHostInfoMap.get(dataCenterId);
if (zoneHostInfo != null && (zoneHostInfo.getFlags() & RunningHostInfoAgregator.ZoneHostInfo.ROUTING_HOST_MASK) != 0) {
VMTemplateVO template = _templateDao.findSystemVMReadyTemplate(dataCenterId, HypervisorType.Any);
if (template == null) {
List<VMTemplateVO> templates = _templateDao.findSystemVMReadyTemplates(dataCenterId,
HypervisorType.Any, null);
if (CollectionUtils.isEmpty(templates)) {
if (logger.isDebugEnabled()) {
logger.debug(String.format("System VM template is not ready at zone [%s], wait until it is ready to launch secondary storage VM.", dataCenterId));
}
@ -834,13 +860,6 @@ public class SecondaryStorageManagerImpl extends ManagerBase implements Secondar
return false;
}
if (!template.isDirectDownload() && templateMgr.getImageStore(dataCenterId, template.getId()) == null) {
if (logger.isDebugEnabled()) {
logger.debug(String.format("No secondary storage available in zone [%s], wait until it is ready to launch secondary storage VM.", dataCenterId));
}
return false;
}
boolean useLocalStorage = BooleanUtils.toBoolean(ConfigurationManagerImpl.SystemVMUseLocalStorage.valueIn(dataCenterId));
boolean hasStoragePoolHostInfo = _storagePoolHostDao.hasDatacenterStoragePoolHostInfo(dataCenterId, !useLocalStorage);
if (hasStoragePoolHostInfo) {

View File

@ -16,39 +16,69 @@
// under the License.
package org.apache.cloudstack.secondarystorage;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import java.security.SecureRandom;
import java.util.ArrayList;
import java.util.List;
import org.apache.cloudstack.engine.subsystem.api.storage.DataStore;
import org.apache.commons.lang3.StringUtils;
import org.junit.Assert;
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 com.cloud.agent.api.to.DataStoreTO;
import com.cloud.hypervisor.Hypervisor;
import com.cloud.offering.ServiceOffering;
import com.cloud.storage.VMTemplateVO;
import com.cloud.user.Account;
import com.cloud.user.AccountManager;
import com.cloud.user.User;
import com.cloud.utils.net.NetUtils;
import com.cloud.vm.SecondaryStorageVm;
import com.cloud.vm.SecondaryStorageVmVO;
import com.cloud.vm.dao.SecondaryStorageVmDao;
import com.google.common.net.InetAddresses;
@RunWith(MockitoJUnitRunner.class)
public class SecondaryStorageManagerImplTest {
private final SecureRandom secureRandom = new SecureRandom();
@Mock
private SecondaryStorageVmDao secStorageVmDao;
@Mock
private AccountManager accountManager;
@Spy
@InjectMocks
private SecondaryStorageManagerImpl secondaryStorageManager;
@Mock
private ServiceOffering serviceOffering;
@Mock
private VMTemplateVO template;
@Mock
private Account systemAccount;
@Mock
private User systemUser;
private List<DataStore> mockDataStoresForTestAddSecondaryStorageServerAddressToBuffer(List<String> addresses) {
List<DataStore> dataStores = new ArrayList<>();
for (String address: addresses) {
DataStore dataStore = Mockito.mock(DataStore.class);
DataStoreTO dataStoreTO = Mockito.mock(DataStoreTO.class);
Mockito.when(dataStoreTO.getUrl()).thenReturn(NetUtils.isValidIp4(address) ? String.format("http://%s", address) : address);
Mockito.when(dataStore.getTO()).thenReturn(dataStoreTO);
when(dataStoreTO.getUrl()).thenReturn(NetUtils.isValidIp4(address) ? String.format("http://%s", address) : address);
when(dataStore.getTO()).thenReturn(dataStoreTO);
dataStores.add(dataStore);
}
return dataStores;
@ -60,7 +90,7 @@ public class SecondaryStorageManagerImplTest {
secondaryStorageManager.addSecondaryStorageServerAddressToBuffer(builder, dataStores, "VM");
String result = builder.toString();
result = result.contains("=") ? result.split("=")[1] : null;
Assert.assertEquals(expected, result);
assertEquals(expected, result);
}
@Test
@ -86,4 +116,55 @@ public class SecondaryStorageManagerImplTest {
List<String> addresses = List.of(randomIp1, "garbage", randomIp2);
runAddSecondaryStorageServerAddressToBufferTest(addresses, StringUtils.join(List.of(randomIp1, randomIp2), ","));
}
@Test
public void testCreateSecondaryStorageVm_New() {
long dataCenterId = 1L;
long id = 100L;
String name = "ssvm1";
SecondaryStorageVm.Role role = SecondaryStorageVm.Role.templateProcessor;
when(systemUser.getId()).thenReturn(1L);
when(accountManager.getSystemUser()).thenReturn(systemUser);
when(secStorageVmDao.persist(any(SecondaryStorageVmVO.class)))
.thenAnswer(invocation -> invocation.getArgument(0));
SecondaryStorageVmVO result = secondaryStorageManager.createOrUpdateSecondaryStorageVm(
null, dataCenterId, id, name, serviceOffering, template, systemAccount, role);
assertNotNull(result);
assertEquals(id, result.getId());
assertEquals(serviceOffering.getId(), result.getServiceOfferingId());
assertEquals(name, result.getName());
assertEquals(template.getId(), result.getTemplateId());
assertEquals(template.getHypervisorType(), result.getHypervisorType());
assertEquals(template.getGuestOSId(), result.getGuestOSId());
assertEquals(dataCenterId, result.getDataCenterId());
assertEquals(systemAccount.getDomainId(), result.getDomainId());
assertEquals(systemAccount.getId(), result.getAccountId());
assertEquals(role, result.getRole());
assertEquals(template.isDynamicallyScalable(), result.isDynamicallyScalable());
assertEquals(serviceOffering.getLimitCpuUse(), result.limitCpuUse());
verify(secStorageVmDao).persist(any(SecondaryStorageVmVO.class));
}
@Test
public void testUpdateSecondaryStorageVm() {
long dataCenterId = 1L;
long id = 100L;
String name = "ssvm1";
SecondaryStorageVm.Role role = SecondaryStorageVm.Role.commandExecutor;
SecondaryStorageVmVO existing = new SecondaryStorageVmVO(id, serviceOffering.getId(), name,
999L, Hypervisor.HypervisorType.KVM, 888L, dataCenterId, systemAccount.getDomainId(), systemAccount.getId(),
systemUser.getId(), role, serviceOffering.isOfferHA());
existing.setDynamicallyScalable(false);
SecondaryStorageVmVO result = secondaryStorageManager.createOrUpdateSecondaryStorageVm(
existing, dataCenterId, id, name, serviceOffering, template, systemAccount, role);
assertNotNull(result);
assertEquals(template.getId(), result.getTemplateId());
assertEquals(template.getHypervisorType(), result.getHypervisorType());
assertEquals(template.getGuestOSId(), result.getGuestOSId());
assertEquals(template.isDynamicallyScalable(), result.isDynamicallyScalable());
verify(secStorageVmDao).update(existing.getId(), existing);
}
}

View File

@ -43,6 +43,7 @@ CREATE TABLE `simulator`.`mockhost` (
`cpus` int(10) unsigned,
`speed` int(10) unsigned,
`ram` bigint unsigned,
`arch` varchar(8) DEFAULT "x86_64" COMMENT "the CPU architecture of the host",
`capabilities` varchar(255) COMMENT 'host capabilities in comma separated list',
`vm_id` bigint unsigned,
`resource` varchar(255) DEFAULT NULL COMMENT 'If it is a local resource, this is the class name',

View File

@ -27,7 +27,7 @@ except ImportError:
raise RuntimeError("python setuptools is required to build Marvin")
VERSION = "4.20.1.0-SNAPSHOT"
VERSION = "4.20.1.0"
setup(name="Marvin",
version=VERSION,

View File

@ -72,6 +72,9 @@
<a-tag v-if="resource.broadcasturi">
{{ resource.broadcasturi }}
</a-tag>
<a-tag v-if="resource.arch">
{{ resource.arch }}
</a-tag>
<a-tag v-if="resource.hypervisor">
{{ resource.hypervisor }}
</a-tag>

View File

@ -310,7 +310,9 @@ export default {
}
if (['zoneid', 'domainid', 'imagestoreid', 'storageid', 'state', 'account', 'hypervisor', 'level',
'clusterid', 'podid', 'groupid', 'entitytype', 'accounttype', 'systemvmtype', 'scope', 'provider',
'type', 'scope', 'managementserverid', 'serviceofferingid', 'diskofferingid', 'networkid', 'usagetype', 'restartrequired', 'displaynetwork'].includes(item)
'type', 'scope', 'managementserverid', 'serviceofferingid',
'diskofferingid', 'networkid', 'usagetype', 'restartrequired',
'displaynetwork', 'arch'].includes(item)
) {
type = 'list'
} else if (item === 'tags') {
@ -445,6 +447,13 @@ export default {
]
this.fields[apiKeyAccessIndex].loading = false
}
if (arrayField.includes('arch')) {
const typeIndex = this.fields.findIndex(item => item.name === 'arch')
this.fields[typeIndex].loading = true
this.fields[typeIndex].opts = this.$fetchCpuArchitectureTypes()
this.fields[typeIndex].loading = false
}
},
async fetchDynamicFieldData (arrayField, searchKeyword) {
const promises = []

View File

@ -63,6 +63,7 @@ export default {
if (store.getters.metrics) {
fields.push(...metricsFields)
}
fields.push('arch')
if (store.getters.userInfo.roletype === 'Admin') {
fields.splice(2, 0, 'instancename')
fields.push('hostname')
@ -78,10 +79,10 @@ export default {
fields.push('zonename')
return fields
},
searchFilters: ['name', 'zoneid', 'domainid', 'account', 'groupid', 'tags'],
searchFilters: ['name', 'zoneid', 'domainid', 'account', 'groupid', 'arch', 'tags'],
details: () => {
var fields = ['name', 'displayname', 'id', 'state', 'ipaddress', 'ip6address', 'templatename', 'ostypename',
'serviceofferingname', 'isdynamicallyscalable', 'haenable', 'hypervisor', 'boottype', 'bootmode', 'account',
'serviceofferingname', 'isdynamicallyscalable', 'haenable', 'hypervisor', 'arch', 'boottype', 'bootmode', 'account',
'domain', 'zonename', 'userdataid', 'userdataname', 'userdataparams', 'userdatadetails', 'userdatapolicy',
'hostcontrolstate', 'deleteprotection']
const listZoneHaveSGEnabled = store.getters.zones.filter(zone => zone.securitygroupsenabled === true)

View File

@ -43,7 +43,7 @@ export default {
}
return 'Not Ready'
}
}, 'ostypename', 'hypervisor']
}, 'ostypename', 'arch', 'hypervisor']
if (['Admin', 'DomainAdmin'].includes(store.getters.userInfo.roletype)) {
fields.push('size')
fields.push('account')
@ -67,7 +67,7 @@ export default {
return fields
},
searchFilters: () => {
var filters = ['name', 'zoneid', 'tags']
var filters = ['name', 'zoneid', 'tags', 'arch']
if (['Admin', 'DomainAdmin'].includes(store.getters.userInfo.roletype)) {
filters.push('storageid')
filters.push('imagestoreid')
@ -220,7 +220,7 @@ export default {
}
return 'Not Ready'
}
}, 'ostypename']
}, 'ostypename', 'arch']
if (['Admin', 'DomainAdmin'].includes(store.getters.userInfo.roletype)) {
fields.push('size')
fields.push('account')
@ -235,7 +235,7 @@ export default {
},
details: ['name', 'id', 'displaytext', 'checksum', 'ostypename', 'size', 'arch', 'bootable', 'isready', 'passwordenabled', 'directdownload', 'isextractable', 'ispublic', 'isfeatured', 'isdynamicallyscalable', 'crosszones', 'account', 'domain', 'created', 'userdatadetails', 'userdatapolicy', 'url'],
searchFilters: () => {
var filters = ['name', 'zoneid', 'tags']
var filters = ['name', 'zoneid', 'tags', 'arch']
if (['Admin', 'DomainAdmin'].includes(store.getters.userInfo.roletype)) {
filters.push('storageid')
filters.push('imagestoreid')
@ -370,9 +370,9 @@ export default {
icon: ['fa-solid', 'fa-dharmachakra'],
docHelp: 'plugins/cloudstack-kubernetes-service.html#kubernetes-supported-versions',
permission: ['listKubernetesSupportedVersions'],
searchFilters: ['zoneid', 'minimumsemanticversion'],
columns: ['name', 'state', 'semanticversion', 'isostate', 'mincpunumber', 'minmemory', 'zonename'],
details: ['name', 'semanticversion', 'supportsautoscaling', 'zoneid', 'zonename', 'isoid', 'isoname', 'isostate', 'mincpunumber', 'minmemory', 'supportsha', 'state', 'created'],
searchFilters: ['zoneid', 'minimumsemanticversion', 'arch'],
columns: ['name', 'state', 'semanticversion', 'isostate', 'mincpunumber', 'minmemory', 'arch', 'zonename'],
details: ['name', 'semanticversion', 'supportsautoscaling', 'zoneid', 'zonename', 'isoid', 'isoname', 'isostate', 'arch', 'mincpunumber', 'minmemory', 'supportsha', 'state', 'created'],
tabs: [
{
name: 'details',

View File

@ -24,9 +24,9 @@ export default {
icon: 'cluster-outlined',
docHelp: 'conceptsandterminology/concepts.html#about-clusters',
permission: ['listClustersMetrics'],
searchFilters: ['name', 'zoneid', 'podid', 'hypervisor'],
searchFilters: ['name', 'zoneid', 'podid', 'arch', 'hypervisor'],
columns: () => {
const fields = ['name', 'state', 'allocationstate', 'clustertype', 'hypervisortype', 'hosts']
const fields = ['name', 'state', 'allocationstate', 'clustertype', 'arch', 'hypervisortype', 'hosts']
const metricsFields = ['cpuused', 'cpumaxdeviation', 'cpuallocated', 'cputotal', 'memoryused', 'memorymaxdeviation', 'memoryallocated', 'memorytotal', 'drsimbalance']
if (store.getters.metrics) {
fields.push(...metricsFields)

View File

@ -24,7 +24,7 @@ export default {
icon: 'database-outlined',
docHelp: 'conceptsandterminology/concepts.html#about-hosts',
permission: ['listHostsMetrics'],
searchFilters: ['name', 'zoneid', 'podid', 'clusterid', 'hypervisor'],
searchFilters: ['name', 'zoneid', 'podid', 'clusterid', 'arch', 'hypervisor'],
resourceType: 'Host',
filters: () => {
const filters = ['enabled', 'disabled', 'maintenance', 'up', 'down', 'disconnected', 'alert']
@ -32,7 +32,7 @@ export default {
},
params: { type: 'routing' },
columns: () => {
const fields = ['name', 'state', 'resourcestate', 'ipaddress', 'hypervisor', 'instances', 'powerstate', 'version']
const fields = ['name', 'state', 'resourcestate', 'ipaddress', 'arch', 'hypervisor', 'instances', 'powerstate', 'version']
const metricsFields = ['cpunumber', 'cputotalghz', 'cpuusedghz', 'cpuallocatedghz', 'memorytotalgb', 'memoryusedgb', 'memoryallocatedgb', 'networkread', 'networkwrite']
if (store.getters.metrics) {
fields.push(...metricsFields)

View File

@ -26,12 +26,12 @@ export default {
permission: ['listRouters'],
params: { projectid: '-1' },
columns: () => {
var columns = ['name', 'state', 'publicip', { field: 'guestnetworkname', customTitle: 'network' }, 'redundantstate', 'softwareversion', 'hostname', 'account', 'zonename', 'requiresupgrade']
var columns = ['name', 'state', 'publicip', { field: 'guestnetworkname', customTitle: 'network' }, 'redundantstate', 'softwareversion', 'hostname', 'arch', 'account', 'zonename', 'requiresupgrade']
columns.splice(6, 0, { field: 'version', customTitle: 'templateversion' })
return columns
},
searchFilters: ['name', 'zoneid', 'podid', 'clusterid'],
details: ['name', 'id', 'version', 'softwareversion', 'requiresupgrade', 'guestnetworkname', 'vpcname', 'publicip', 'guestipaddress', 'linklocalip', 'serviceofferingname', 'networkdomain', 'isredundantrouter', 'redundantstate', 'hostname', 'account', 'zonename', 'created', 'hostcontrolstate'],
searchFilters: ['name', 'zoneid', 'podid', 'clusterid', 'arch'],
details: ['name', 'id', 'version', 'softwareversion', 'requiresupgrade', 'guestnetworkname', 'vpcname', 'publicip', 'guestipaddress', 'linklocalip', 'serviceofferingname', 'networkdomain', 'isredundantrouter', 'redundantstate', 'hostname', 'arch', 'account', 'zonename', 'created', 'hostcontrolstate'],
resourceType: 'VirtualRouter',
filters: () => {
const filters = ['starting', 'running', 'stopping', 'stopped', 'destroyed', 'expunging', 'migrating', 'error', 'unknown', 'shutdown']

View File

@ -24,9 +24,9 @@ export default {
icon: 'thunderbolt-outlined',
docHelp: 'adminguide/systemvm.html',
permission: ['listSystemVms'],
searchFilters: ['name', 'zoneid', 'podid', 'hostid', 'systemvmtype', 'storageid'],
columns: ['name', 'state', 'agentstate', 'systemvmtype', 'publicip', 'privateip', 'linklocalip', 'version', 'hostname', 'zonename'],
details: ['name', 'id', 'agentstate', 'systemvmtype', 'publicip', 'privateip', 'linklocalip', 'gateway', 'hostname', 'version', 'zonename', 'created', 'activeviewersessions', 'isdynamicallyscalable', 'hostcontrolstate'],
searchFilters: ['name', 'zoneid', 'podid', 'hostid', 'systemvmtype', 'storageid', 'arch'],
columns: ['name', 'state', 'agentstate', 'systemvmtype', 'publicip', 'privateip', 'linklocalip', 'version', 'hostname', 'arch', 'zonename'],
details: ['name', 'id', 'agentstate', 'systemvmtype', 'publicip', 'privateip', 'linklocalip', 'gateway', 'hostname', 'arch', 'version', 'zonename', 'created', 'activeviewersessions', 'isdynamicallyscalable', 'hostcontrolstate'],
resourceType: 'SystemVm',
filters: () => {
const filters = ['starting', 'running', 'stopping', 'stopped', 'destroyed', 'expunging', 'migrating', 'error', 'unknown', 'shutdown']

View File

@ -36,7 +36,8 @@ import {
fileSizeUtilPlugin,
genericUtilPlugin,
localesPlugin,
dialogUtilPlugin
dialogUtilPlugin,
cpuArchitectureUtilPlugin
} from './utils/plugins'
import { VueAxios } from './utils/request'
import directives from './utils/directives'
@ -53,6 +54,7 @@ vueApp.use(fileSizeUtilPlugin)
vueApp.use(localesPlugin)
vueApp.use(genericUtilPlugin)
vueApp.use(dialogUtilPlugin)
vueApp.use(cpuArchitectureUtilPlugin)
vueApp.use(extensions)
vueApp.use(directives)

View File

@ -538,3 +538,15 @@ export const dialogUtilPlugin = {
}
}
}
export const cpuArchitectureUtilPlugin = {
install (app) {
app.config.globalProperties.$fetchCpuArchitectureTypes = function () {
const architectures = [
{ id: 'x86_64', name: 'AMD 64 bits (x86_64)' },
{ id: 'aarch64', name: 'ARM 64 bits (aarch64)' }
]
return architectures.map(item => ({ ...item, description: item.name }))
}
}
}

View File

@ -2569,14 +2569,28 @@ export default {
if (this.clusterId === null) {
this.form.clusterid = undefined
}
this.fetchOptions(this.params.hosts, 'hosts')
if (this.clusterId && Array.isArray(this.options.clusters)) {
const cluster = this.options.clusters.find(c => c.id === this.clusterId)
this.handleArchResourceSelected(cluster.arch)
}
},
onSelectHostId (value) {
this.hostId = value
if (this.hostId === null) {
this.form.hostid = undefined
}
if (this.hostId && Array.isArray(this.options.hosts)) {
const host = this.options.hosts.find(h => h.id === this.hostId)
this.handleArchResourceSelected(host.arch)
}
},
handleArchResourceSelected (resourceArch) {
if (!resourceArch || !this.isZoneSelectedMultiArch || this.selectedArchitecture === resourceArch) {
return
}
this.selectedArchitecture = resourceArch
this.changeArchitecture(resourceArch, this.tabKey === 'templateid')
},
handleSearchFilter (name, options) {
this.params[name].options = { ...this.params[name].options, ...options }

View File

@ -216,7 +216,7 @@ export default {
})
},
fetchData () {
this.fetchArchitectureTypes()
this.architectureTypes.opts = this.$fetchCpuArchitectureTypes()
this.fetchZoneData()
},
isValidValueForKey (obj, key) {
@ -225,19 +225,6 @@ export default {
arrayHasItems (array) {
return array !== null && array !== undefined && Array.isArray(array) && array.length > 0
},
fetchArchitectureTypes () {
this.architectureTypes.opts = []
const typesList = []
typesList.push({
id: 'x86_64',
description: 'AMD 64 bits (x86_64)'
})
typesList.push({
id: 'aarch64',
description: 'ARM 64 bits (aarch64)'
})
this.architectureTypes.opts = typesList
},
fetchZoneData () {
const params = {}
params.showicon = true

View File

@ -384,7 +384,7 @@ export default {
fetchData () {
this.fetchZoneData()
this.fetchOsType()
this.fetchArchitectureTypes()
this.architectureTypes.opts = this.$fetchCpuArchitectureTypes()
this.fetchUserData()
this.fetchUserdataPolicy()
if ('listDomains' in this.$store.getters.apis) {
@ -410,19 +410,6 @@ export default {
this.form.zoneid = (this.zones[0].id ? this.zones[0].id : '')
})
},
fetchArchitectureTypes () {
this.architectureTypes.opts = []
const typesList = []
typesList.push({
id: 'x86_64',
description: 'AMD 64 bits (x86_64)'
})
typesList.push({
id: 'aarch64',
description: 'ARM 64 bits (aarch64)'
})
this.architectureTypes.opts = typesList
},
fetchOsType () {
this.osTypeLoading = true

View File

@ -583,7 +583,7 @@ export default {
this.fetchZone()
this.fetchOsTypes()
this.fetchTemplateTypes()
this.fetchArchitectureTypes()
this.architectureTypes.opts = this.$fetchCpuArchitectureTypes()
this.fetchUserData()
this.fetchUserdataPolicy()
if ('listDomains' in this.$store.getters.apis) {
@ -753,19 +753,6 @@ export default {
}
this.templateTypes.opts = templatetypes
},
fetchArchitectureTypes () {
this.architectureTypes.opts = []
const typesList = []
typesList.push({
id: 'x86_64',
description: 'AMD 64 bits (x86_64)'
})
typesList.push({
id: 'aarch64',
description: 'ARM 64 bits (aarch64)'
})
this.architectureTypes.opts = typesList
},
fetchUserData () {
const params = {}
params.listAll = true

View File

@ -216,7 +216,7 @@ export default {
},
fetchData () {
this.fetchOsTypes()
this.fetchArchitectureTypes()
this.architectureTypes.opts = this.$fetchCpuArchitectureTypes()
this.fetchUserdata()
this.fetchUserdataPolicy()
},
@ -235,19 +235,6 @@ export default {
this.osTypes.loading = false
})
},
fetchArchitectureTypes () {
this.architectureTypes.opts = []
const typesList = []
typesList.push({
id: 'x86_64',
description: 'AMD 64 bits (x86_64)'
})
typesList.push({
id: 'aarch64',
description: 'ARM 64 bits (aarch64)'
})
this.architectureTypes.opts = typesList
},
fetchUserdataPolicy () {
const userdataPolicy = []
userdataPolicy.push({

View File

@ -310,7 +310,7 @@ export default {
},
fetchData () {
this.fetchOsTypes()
this.fetchArchitectureTypes()
this.architectureTypes.opts = this.$fetchCpuArchitectureTypes()
this.fetchRootDiskControllerTypes(this.resource.hypervisor)
this.fetchNicAdapterTypes()
this.fetchKeyboardTypes()
@ -335,19 +335,6 @@ export default {
this.osTypes.loading = false
})
},
fetchArchitectureTypes () {
this.architectureTypes.opts = []
const typesList = []
typesList.push({
id: 'x86_64',
description: 'AMD 64 bits (x86_64)'
})
typesList.push({
id: 'aarch64',
description: 'ARM 64 bits (aarch64)'
})
this.architectureTypes.opts = typesList
},
fetchRootDiskControllerTypes (hyperVisor) {
const controller = []
this.rootDisk.opts = []

View File

@ -213,7 +213,8 @@ export default {
fetchData () {
this.fetchZones()
this.fetchHypervisors()
this.fetchArchitectureTypes()
this.architectureTypes.opts = this.$fetchCpuArchitectureTypes()
this.selectedArchitecture = this.architectureTypes?.opts?.[0]?.id || null
this.params = this.$store.getters.apis.addCluster.params
Object.keys(this.placeholder).forEach(item => { this.returnPlaceholder(item) })
},
@ -240,20 +241,6 @@ export default {
this.loading = false
})
},
fetchArchitectureTypes () {
this.architectureTypes.opts = []
const typesList = []
typesList.push({
id: 'x86_64',
description: 'AMD 64 bits (x86_64)'
})
typesList.push({
id: 'aarch64',
description: 'ARM 64 bits (aarch64)'
})
this.architectureTypes.opts = typesList
this.selectedArchitecture = this.architectureTypes.opts[0].id
},
fetchPods () {
this.loading = true
api('listPods', {

View File

@ -96,6 +96,7 @@
<a-input
v-else
v-model:value="form[field.key]"
:defaultValue="field.defaultValue"
v-focus="index === 0"
/>
</a-form-item>

View File

@ -151,15 +151,12 @@ export default {
return this.prefillContent?.zoneSuperType === 'Edge' || false
},
steps () {
const steps = []
const steps = [{
title: 'label.cluster',
fromKey: 'clusterResource',
description: 'message.desc.cluster'
}]
const hypervisor = this.prefillContent.hypervisor ? this.prefillContent.hypervisor : null
if (!this.isEdgeZone) {
steps.push({
title: 'label.cluster',
fromKey: 'clusterResource',
description: 'message.desc.cluster'
})
}
if (hypervisor !== 'VMware') {
steps.push({
title: 'label.host',
@ -190,7 +187,8 @@ export default {
title: 'label.cluster.name',
key: 'clusterName',
placeHolder: 'message.error.cluster.name',
required: true
required: true,
defaultValue: this.isEdgeZone ? 'Cluster-' + (this.prefillContent?.name || 'Edge') : undefined
},
{
title: 'label.arch',

View File

@ -1280,7 +1280,7 @@ export default {
params.clustertype = clusterType
params.podId = this.stepData.podReturned.id
let clusterName = this.prefillContent?.clusterName || null
if (this.isEdgeZone) {
if (!clusterName && this.isEdgeZone) {
clusterName = 'Cluster-' + this.stepData.zoneReturned.name
}
params.arch = this.prefillContent?.arch || null

View File

@ -25,7 +25,12 @@ import org.apache.logging.log4j.LogManager;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.Map;
public class HttpUtils {
@ -151,4 +156,50 @@ public class HttpUtils {
}
}
public static boolean downloadFileWithProgress(final String fileURL, final String savePath, final Logger logger) {
HttpURLConnection httpConn = null;
try {
URL url = new URL(fileURL);
httpConn = (HttpURLConnection) url.openConnection();
int responseCode = httpConn.getResponseCode();
if (responseCode == HttpURLConnection.HTTP_OK) {
int contentLength = httpConn.getContentLength();
if (contentLength < 0) {
logger.warn("Content length not provided for {}, progress updates may not be accurate",
fileURL);
}
try (InputStream inputStream = httpConn.getInputStream();
FileOutputStream outputStream = new FileOutputStream(savePath)) {
byte[] buffer = new byte[4096];
int bytesRead;
int downloaded = 0;
int lastReportedPercent = 0;
while ((bytesRead = inputStream.read(buffer)) != -1) {
outputStream.write(buffer, 0, bytesRead);
downloaded += bytesRead;
if (contentLength > 0) {
int percentDownloaded = (int) ((downloaded / (double) contentLength) * 100);
// Update every 5 percent or on completion
if (percentDownloaded - lastReportedPercent >= 5 || percentDownloaded == 100) {
logger.debug("Downloaded {}% from {}", percentDownloaded, fileURL);
lastReportedPercent = percentDownloaded;
}
}
}
}
logger.info("File {} downloaded successfully using {}.", fileURL, savePath);
} else {
logger.error("No file to download {}. Server replied with code: {}", fileURL, responseCode);
return false;
}
} catch (IOException ex) {
logger.error("Failed to download {} due to: {}", fileURL, ex.getMessage(), ex);
return false;
} finally {
if (httpConn != null) {
httpConn.disconnect();
}
}
return true;
}
}

View File

@ -19,21 +19,54 @@
package com.cloud.utils;
import org.junit.Test;
import org.springframework.mock.web.MockHttpSession;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpSession;
import java.util.HashMap;
import java.util.Map;
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.contains;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.atLeastOnce;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.IOException;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLConnection;
import java.net.URLStreamHandler;
import java.nio.file.Files;
import java.util.HashMap;
import java.util.Map;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpSession;
import org.apache.logging.log4j.Logger;
import org.junit.BeforeClass;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnitRunner;
import org.springframework.mock.web.MockHttpSession;
@RunWith(MockitoJUnitRunner.class)
public class HttpUtilsTest {
// Use a custom protocol (e.g., "mockhttp") for testing.
private static FakeURLStreamHandler fakeHandler;
@Mock
private Logger logger;
@Mock
private HttpURLConnection httpConn;
@Test
public void findCookieTest() {
Cookie[] cookies = null;
@ -153,4 +186,97 @@ public class HttpUtilsTest {
assertFalse(HttpUtils.validateSessionKey(session, params, cookies, sessionKeyString, HttpUtils.ApiSessionKeyCheckOption.ParameterOnly));
assertFalse(HttpUtils.validateSessionKey(session, params, cookies, sessionKeyString, HttpUtils.ApiSessionKeyCheckOption.CookieAndParameter));
}
private static class FakeURLStreamHandler extends URLStreamHandler {
private HttpURLConnection connection;
public void setHttpURLConnection(HttpURLConnection connection) {
this.connection = connection;
}
@Override
protected URLConnection openConnection(URL u) {
return connection;
}
}
// Register our custom URLStreamHandlerFactory once for the tests.
@BeforeClass
public static void setUpOnce() {
fakeHandler = new FakeURLStreamHandler();
try {
URL.setURLStreamHandlerFactory(protocol -> {
if ("mockhttp".equals(protocol)) {
return fakeHandler;
}
return null;
});
} catch (Error e) {
// The factory can only be set once. In case it is already set, ignore.
}
}
@Test
public void testSuccessfulDownload_withContentLength() throws Exception {
String fileURL = "mockhttp://example.com/file.txt";
File tempFile = File.createTempFile("downloadTest", ".tmp");
tempFile.deleteOnExit();
byte[] fileData = "Hello World".getBytes();
ByteArrayInputStream inputStream = new ByteArrayInputStream(fileData);
when(httpConn.getResponseCode()).thenReturn(HttpURLConnection.HTTP_OK);
when(httpConn.getContentLength()).thenReturn(fileData.length);
when(httpConn.getInputStream()).thenReturn(inputStream);
fakeHandler.setHttpURLConnection(httpConn);
boolean result = HttpUtils.downloadFileWithProgress(fileURL, tempFile.getAbsolutePath(), logger);
assertTrue(result);
verify(logger, atLeastOnce()).debug(anyString(), anyInt(), eq(fileURL));
verify(logger).info("File {} downloaded successfully using {}.", fileURL, tempFile.getAbsolutePath());
byte[] actualData = Files.readAllBytes(tempFile.toPath());
assertArrayEquals(fileData, actualData);
}
@Test
public void testSuccessfulDownload_negativeContentLength() throws Exception {
String fileURL = "mockhttp://example.com/file.txt";
File tempFile = File.createTempFile("downloadTest", ".tmp");
tempFile.deleteOnExit();
byte[] fileData = "Hello World".getBytes();
ByteArrayInputStream inputStream = new ByteArrayInputStream(fileData);
when(httpConn.getResponseCode()).thenReturn(HttpURLConnection.HTTP_OK);
// Simulate missing content length
when(httpConn.getContentLength()).thenReturn(-1);
when(httpConn.getInputStream()).thenReturn(inputStream);
fakeHandler.setHttpURLConnection(httpConn);
boolean result = HttpUtils.downloadFileWithProgress(fileURL, tempFile.getAbsolutePath(), logger);
assertTrue(result);
verify(logger).warn("Content length not provided for {}, progress updates may not be accurate", fileURL);
verify(logger).info("File {} downloaded successfully using {}.", fileURL, tempFile.getAbsolutePath());
byte[] actualData = Files.readAllBytes(tempFile.toPath());
assertArrayEquals(fileData, actualData);
}
@Test
public void testDownloadFile_nonOKResponse() throws Exception {
String fileURL = "mockhttp://example.com/file.txt";
String savePath = "dummyPath";
when(httpConn.getResponseCode()).thenReturn(HttpURLConnection.HTTP_NOT_FOUND);
fakeHandler.setHttpURLConnection(httpConn);
boolean result = HttpUtils.downloadFileWithProgress(fileURL, savePath, logger);
assertFalse(result);
verify(logger).error("No file to download {}. Server replied with code: {}", fileURL, HttpURLConnection.HTTP_NOT_FOUND);
}
@Test
public void testDownloadFile_exceptionDuringDownload() throws Exception {
String fileURL = "mockhttp://example.com/file.txt";
String savePath = "dummyPath";
when(httpConn.getResponseCode()).thenReturn(HttpURLConnection.HTTP_OK);
when(httpConn.getContentLength()).thenReturn(100);
// Simulate an IOException when trying to get the InputStream
when(httpConn.getInputStream()).thenThrow(new IOException("Connection error"));
fakeHandler.setHttpURLConnection(httpConn);
boolean result = HttpUtils.downloadFileWithProgress(fileURL, savePath, logger);
assertFalse(result);
verify(logger).error(contains("Failed to download {} due to: {}"), eq(fileURL), eq("Connection error"), any(IOException.class));
}
}