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. // under the License.
package com.cloud.cpu; package com.cloud.cpu;
import com.cloud.utils.exception.CloudRuntimeException;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import java.util.LinkedHashMap;
import java.util.Map;
public class CPU { public class CPU {
public enum CPUArch {
x86("i686", 32),
amd64("x86_64", 64),
arm64("aarch64", 64);
public static final String archX86Identifier = "i686"; private final String type;
public static final String archX86_64Identifier = "x86_64"; private final int bits;
public static final String archARM64Identifier = "aarch64";
public static class CPUArch { CPUArch(String type, int bits) {
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) {
this.type = type; this.type = type;
this.bits = bits; this.bits = bits;
cpuArchMap.put(type, this); }
public static CPUArch getDefault() {
return amd64;
} }
public String getType() { public String getType() {
return this.type; return type;
} }
public int getBits() { public int getBits() {
return this.bits; return bits;
} }
public static CPUArch fromType(String type) { public static CPUArch fromType(String type) {
if (StringUtils.isBlank(type)) { if (StringUtils.isBlank(type)) {
return amd64; return getDefault();
} }
switch (type) { for (CPUArch arch : values()) {
case archX86Identifier: return archX86; if (arch.type.equals(type)) {
case archX86_64Identifier: return amd64; return arch;
case archARM64Identifier: return arm64; }
default: throw new CloudRuntimeException(String.format("Unsupported arch type: %s", type));
} }
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.ArrayList;
import java.util.List; import java.util.List;
import org.apache.cloudstack.api.APICommand; import org.apache.cloudstack.api.APICommand;
import org.apache.cloudstack.api.ApiConstants; import org.apache.cloudstack.api.ApiConstants;
import org.apache.cloudstack.api.BaseListCmd; 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.ListResponse;
import org.apache.cloudstack.api.response.PodResponse; import org.apache.cloudstack.api.response.PodResponse;
import org.apache.cloudstack.api.response.ZoneResponse; 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.org.Cluster;
import com.cloud.utils.Pair; 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") @Parameter(name = ApiConstants.SHOW_CAPACITIES, type = CommandType.BOOLEAN, description = "flag to display the capacity of the clusters")
private Boolean showCapacities; private Boolean showCapacities;
@Parameter(name = ApiConstants.ARCH, type = CommandType.STRING,
description = "CPU arch of the clusters",
since = "4.20.1")
private String arch;
///////////////////////////////////////////////////// /////////////////////////////////////////////////////
/////////////////// Accessors /////////////////////// /////////////////// Accessors ///////////////////////
///////////////////////////////////////////////////// /////////////////////////////////////////////////////
@ -112,6 +118,10 @@ public class ListClustersCmd extends BaseListCmd {
return showCapacities; return showCapacities;
} }
public CPU.CPUArch getArch() {
return StringUtils.isBlank(arch) ? null : CPU.CPUArch.fromType(arch);
}
///////////////////////////////////////////////////// /////////////////////////////////////////////////////
/////////////// API Implementation/////////////////// /////////////// API Implementation///////////////////
///////////////////////////////////////////////////// /////////////////////////////////////////////////////

View File

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

View File

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

View File

@ -19,7 +19,6 @@ package org.apache.cloudstack.api.command.admin.systemvm;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import org.apache.cloudstack.api.APICommand; import org.apache.cloudstack.api.APICommand;
import org.apache.cloudstack.api.ApiCommandResourceType; import org.apache.cloudstack.api.ApiCommandResourceType;
import org.apache.cloudstack.api.ApiConstants; 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.StoragePoolResponse;
import org.apache.cloudstack.api.response.SystemVmResponse; import org.apache.cloudstack.api.response.SystemVmResponse;
import org.apache.cloudstack.api.response.ZoneResponse; 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.utils.Pair;
import com.cloud.vm.VirtualMachine; import com.cloud.vm.VirtualMachine;
@ -74,6 +75,11 @@ public class ListSystemVMsCmd extends BaseListCmd {
since = "3.0.1") since = "3.0.1")
private Long storageId; 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 /////////////////////// /////////////////// Accessors ///////////////////////
///////////////////////////////////////////////////// /////////////////////////////////////////////////////
@ -110,6 +116,10 @@ public class ListSystemVMsCmd extends BaseListCmd {
return storageId; return storageId;
} }
public CPU.CPUArch getArch() {
return StringUtils.isBlank(arch) ? null : CPU.CPUArch.fromType(arch);
}
///////////////////////////////////////////////////// /////////////////////////////////////////////////////
/////////////// API Implementation/////////////////// /////////////// 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.UserVmResponse;
import org.apache.cloudstack.api.response.VpcResponse; import org.apache.cloudstack.api.response.VpcResponse;
import org.apache.cloudstack.api.response.ZoneResponse; import org.apache.cloudstack.api.response.ZoneResponse;
import org.apache.commons.lang3.BooleanUtils;
import org.apache.commons.collections.CollectionUtils; 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.exception.InvalidParameterValueException;
import com.cloud.server.ResourceIcon; import com.cloud.server.ResourceIcon;
import com.cloud.server.ResourceTag; 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") @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; private Long userdataId;
@Parameter(name = ApiConstants.ARCH, type = CommandType.STRING,
description = "CPU arch of the VM",
since = "4.20.1")
private String arch;
///////////////////////////////////////////////////// /////////////////////////////////////////////////////
/////////////////// Accessors /////////////////////// /////////////////// Accessors ///////////////////////
///////////////////////////////////////////////////// /////////////////////////////////////////////////////
@ -292,6 +299,10 @@ public class ListVMsCmd extends BaseListRetrieveOnlyResourceCountCmd implements
return isVnf; return isVnf;
} }
public CPU.CPUArch getArch() {
return StringUtils.isBlank(arch) ? null : CPU.CPUArch.fromType(arch);
}
///////////////////////////////////////////////////// /////////////////////////////////////////////////////
/////////////// API Implementation/////////////////// /////////////// 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") @Param(description = "the version of the code / software in the router")
private String softwareVersion; private String softwareVersion;
@SerializedName(ApiConstants.ARCH)
@Param(description = "CPU arch of the router", since = "4.20.1")
private String arch;
public DomainRouterResponse() { public DomainRouterResponse() {
nics = new LinkedHashSet<NicResponse>(); nics = new LinkedHashSet<NicResponse>();
} }
@ -518,4 +522,8 @@ public class DomainRouterResponse extends BaseResponseWithAnnotations implements
public void setSoftwareVersion(String softwareVersion) { public void setSoftwareVersion(String softwareVersion) {
this.softwareVersion = 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.") @Param(description = "the name of the service offering of the system virtual machine.")
private String serviceOfferingName; private String serviceOfferingName;
@SerializedName(ApiConstants.ARCH)
@Param(description = "CPU arch of the system VM", since = "4.20.1")
private String arch;
@Override @Override
public String getObjectId() { public String getObjectId() {
return this.getId(); return this.getId();
@ -490,4 +494,8 @@ public class SystemVmResponse extends BaseResponseWithAnnotations {
public void setServiceOfferingName(String serviceOfferingName) { public void setServiceOfferingName(String serviceOfferingName) {
this.serviceOfferingName = 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.ApiConstants;
import org.apache.cloudstack.api.BaseResponseWithTagInformation; import org.apache.cloudstack.api.BaseResponseWithTagInformation;
import org.apache.cloudstack.api.EntityReference; import org.apache.cloudstack.api.EntityReference;
import org.apache.commons.collections.CollectionUtils;
import com.cloud.network.router.VirtualRouter; import com.cloud.network.router.VirtualRouter;
import com.cloud.serializer.Param; import com.cloud.serializer.Param;
import com.cloud.uservm.UserVm; import com.cloud.uservm.UserVm;
import com.cloud.vm.VirtualMachine; import com.cloud.vm.VirtualMachine;
import com.google.gson.annotations.SerializedName; import com.google.gson.annotations.SerializedName;
import org.apache.commons.collections.CollectionUtils;
@SuppressWarnings("unused") @SuppressWarnings("unused")
@EntityReference(value = {VirtualMachine.class, UserVm.class, VirtualRouter.class}) @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") @Param(description = "User VM type", since = "4.20.0")
private String vmType; private String vmType;
@SerializedName(ApiConstants.ARCH)
@Param(description = "CPU arch of the VM", since = "4.20.1")
private String arch;
public UserVmResponse() { public UserVmResponse() {
securityGroupList = new LinkedHashSet<>(); securityGroupList = new LinkedHashSet<>();
nics = new TreeSet<>(Comparator.comparingInt(x -> Integer.parseInt(x.getDeviceId()))); 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) { public void setIpAddress(String ipAddress) {
this.ipAddress = 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} chmod 0640 ${CONFDIR}/${DBPROPS}
chgrp cloud ${CONFDIR}/${DBPROPS} chgrp cloud ${CONFDIR}/${DBPROPS}
chown -R cloud:cloud /var/log/cloudstack/management 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 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); 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.StartupRoutingCommand;
import com.cloud.agent.api.VgpuTypesInfo; import com.cloud.agent.api.VgpuTypesInfo;
import com.cloud.agent.api.to.GPUDeviceTO; import com.cloud.agent.api.to.GPUDeviceTO;
import com.cloud.cpu.CPU;
import com.cloud.dc.DataCenterVO; import com.cloud.dc.DataCenterVO;
import com.cloud.dc.HostPodVO; import com.cloud.dc.HostPodVO;
import com.cloud.dc.PodCluster; import com.cloud.dc.PodCluster;
@ -61,6 +62,17 @@ public interface ResourceManager extends ResourceService, Configurable {
+ "To force-stop VMs, choose 'ForceStop' strategy", + "To force-stop VMs, choose 'ForceStop' strategy",
true, ConfigKey.Scope.Global, null, null, null, null, null, ConfigKey.Kind.Select, "Error,Migration,ForceStop"); 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. * Register a listener for different types of resource life cycle events.
* There can only be one type of listener per type of host. * 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; 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> </execution>
</executions> </executions>
</plugin> </plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<configuration>
<systemPropertyVariables>
<test.mode>true</test.mode>
</systemPropertyVariables>
</configuration>
</plugin>
</plugins> </plugins>
</build> </build>
<profiles> <profiles>

View File

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

View File

@ -21,10 +21,8 @@ import java.sql.ResultSet;
import java.sql.SQLException; import java.sql.SQLException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashMap; import java.util.HashMap;
import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import javax.inject.Inject; import javax.inject.Inject;
@ -39,6 +37,7 @@ import com.cloud.dc.HostPodVO;
import com.cloud.hypervisor.Hypervisor.HypervisorType; import com.cloud.hypervisor.Hypervisor.HypervisorType;
import com.cloud.org.Grouping; import com.cloud.org.Grouping;
import com.cloud.org.Managed; import com.cloud.org.Managed;
import com.cloud.utils.Pair;
import com.cloud.utils.db.GenericDaoBase; import com.cloud.utils.db.GenericDaoBase;
import com.cloud.utils.db.GenericSearchBuilder; import com.cloud.utils.db.GenericSearchBuilder;
import com.cloud.utils.db.JoinBuilder; import com.cloud.utils.db.JoinBuilder;
@ -148,14 +147,6 @@ public class ClusterDaoImpl extends GenericDaoBase<ClusterVO, Long> implements C
return findOneBy(sc); return findOneBy(sc);
} }
@Override
public List<ClusterVO> listByHyTypeWithoutGuid(String hyType) {
SearchCriteria<ClusterVO> sc = HyTypeWithoutGuidSearch.create();
sc.setParameters("hypervisorType", hyType);
return listBy(sc);
}
@Override @Override
public List<ClusterVO> listByDcHyType(long dcId, String hyType) { public List<ClusterVO> listByDcHyType(long dcId, String hyType) {
SearchCriteria<ClusterVO> sc = ZoneHyTypeSearch.create(); SearchCriteria<ClusterVO> sc = ZoneHyTypeSearch.create();
@ -178,8 +169,19 @@ public class ClusterDaoImpl extends GenericDaoBase<ClusterVO, Long> implements C
} }
@Override @Override
public Set<HypervisorType> getDistinctAvailableHypervisorsAcrossClusters() { public List<Pair<HypervisorType, CPU.CPUArch>> listDistinctHypervisorsArchAcrossClusters(Long zoneId) {
return new HashSet<>(getAvailableHypervisorInZone(null)); 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 @Override

View File

@ -19,6 +19,7 @@ package com.cloud.host.dao;
import java.util.Date; import java.util.Date;
import java.util.List; import java.util.List;
import com.cloud.cpu.CPU;
import com.cloud.host.Host; import com.cloud.host.Host;
import com.cloud.host.Host.Type; import com.cloud.host.Host.Type;
import com.cloud.host.HostVO; 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<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); 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.HostTransferMapVO;
import com.cloud.cluster.agentlb.dao.HostTransferMapDao; import com.cloud.cluster.agentlb.dao.HostTransferMapDao;
import com.cloud.configuration.ManagementServiceConfiguration; import com.cloud.configuration.ManagementServiceConfiguration;
import com.cloud.cpu.CPU;
import com.cloud.dc.ClusterVO; import com.cloud.dc.ClusterVO;
import com.cloud.dc.dao.ClusterDao; import com.cloud.dc.dao.ClusterDao;
import com.cloud.gpu.dao.HostGpuGroupsDao; import com.cloud.gpu.dao.HostGpuGroupsDao;
@ -1743,17 +1744,52 @@ public class HostDaoImpl extends GenericDaoBase<HostVO, Long> implements HostDao
@Override @Override
public List<HypervisorType> listDistinctHypervisorTypes(final Long zoneId) { 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("zoneId", sb.entity().getDataCenterId(), SearchCriteria.Op.EQ);
sb.and("type", sb.entity().getType(), SearchCriteria.Op.EQ); sb.and("type", sb.entity().getType(), SearchCriteria.Op.EQ);
sb.select(null, Func.DISTINCT, sb.entity().getHypervisorType()); sb.select(null, Func.DISTINCT, sb.entity().getHypervisorType());
sb.done(); sb.done();
SearchCriteria<HypervisorType> sc = sb.create(); SearchCriteria<String> sc = sb.create();
if (zoneId != null) { if (zoneId != null) {
sc.setParameters("zoneId", zoneId); sc.setParameters("zoneId", zoneId);
} }
sc.setParameters("type", Type.Routing); 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 @Override

View File

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

View File

@ -18,7 +18,9 @@ package com.cloud.storage.dao;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.Comparator;
import java.util.Date; import java.util.Date;
import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; 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.TemplateDataStoreDao;
import org.apache.cloudstack.storage.datastore.db.TemplateDataStoreVO; import org.apache.cloudstack.storage.datastore.db.TemplateDataStoreVO;
import org.apache.commons.collections.CollectionUtils; import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import com.cloud.cpu.CPU;
import com.cloud.dc.dao.DataCenterDao; import com.cloud.dc.dao.DataCenterDao;
import com.cloud.domain.dao.DomainDao; import com.cloud.domain.dao.DomainDao;
import com.cloud.host.Host; import com.cloud.host.Host;
@ -47,6 +51,7 @@ import com.cloud.storage.VMTemplateZoneVO;
import com.cloud.tags.ResourceTagVO; import com.cloud.tags.ResourceTagVO;
import com.cloud.tags.dao.ResourceTagDao; import com.cloud.tags.dao.ResourceTagDao;
import com.cloud.template.VirtualMachineTemplate; import com.cloud.template.VirtualMachineTemplate;
import com.cloud.utils.Pair;
import com.cloud.utils.db.DB; import com.cloud.utils.db.DB;
import com.cloud.utils.db.Filter; import com.cloud.utils.db.Filter;
import com.cloud.utils.db.GenericDaoBase; import com.cloud.utils.db.GenericDaoBase;
@ -111,6 +116,7 @@ public class VMTemplateDaoImpl extends GenericDaoBase<VMTemplateVO, Long> implem
LatestTemplateByHypervisorTypeSearch = createSearchBuilder(); LatestTemplateByHypervisorTypeSearch = createSearchBuilder();
LatestTemplateByHypervisorTypeSearch.and("hypervisorType", LatestTemplateByHypervisorTypeSearch.entity().getHypervisorType(), SearchCriteria.Op.EQ); LatestTemplateByHypervisorTypeSearch.and("hypervisorType", LatestTemplateByHypervisorTypeSearch.entity().getHypervisorType(), SearchCriteria.Op.EQ);
LatestTemplateByHypervisorTypeSearch.and("templateType", LatestTemplateByHypervisorTypeSearch.entity().getTemplateType(), 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); LatestTemplateByHypervisorTypeSearch.and("removed", LatestTemplateByHypervisorTypeSearch.entity().getRemoved(), SearchCriteria.Op.NULL);
} }
@ -238,10 +244,16 @@ public class VMTemplateDaoImpl extends GenericDaoBase<VMTemplateVO, Long> implem
@Override @Override
public VMTemplateVO findLatestTemplateByName(String name) { public VMTemplateVO findLatestTemplateByName(String name, CPU.CPUArch arch) {
SearchCriteria<VMTemplateVO> sc = createSearchCriteria(); SearchBuilder<VMTemplateVO> sb = createSearchBuilder();
sc.addAnd("name", SearchCriteria.Op.EQ, name); sb.and("name", sb.entity().getName(), SearchCriteria.Op.EQ);
sc.addAnd("removed", SearchCriteria.Op.NULL); 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); Filter filter = new Filter(VMTemplateVO.class, "id", false, null, 1L);
List<VMTemplateVO> templates = listBy(sc, filter); List<VMTemplateVO> templates = listBy(sc, filter);
if ((templates != null) && !templates.isEmpty()) { if ((templates != null) && !templates.isEmpty()) {
@ -580,6 +592,59 @@ public class VMTemplateDaoImpl extends GenericDaoBase<VMTemplateVO, Long> implem
.orElse(null); .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 @Override
public VMTemplateVO findRoutingTemplate(HypervisorType hType, String templateName) { public VMTemplateVO findRoutingTemplate(HypervisorType hType, String templateName) {
SearchCriteria<VMTemplateVO> sc = tmpltTypeHyperSearch2.create(); SearchCriteria<VMTemplateVO> sc = tmpltTypeHyperSearch2.create();
@ -618,10 +683,43 @@ public class VMTemplateDaoImpl extends GenericDaoBase<VMTemplateVO, Long> implem
} }
@Override @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(); SearchCriteria<VMTemplateVO> sc = LatestTemplateByHypervisorTypeSearch.create();
sc.setParameters("hypervisorType", hypervisorType); sc.setParameters("hypervisorType", hypervisorType);
sc.setParameters("templateType", type); sc.setParameters("templateType", type);
if (arch != null) {
sc.setParameters("arch", arch);
}
Filter filter = new Filter(VMTemplateVO.class, "id", false, null, 1L); Filter filter = new Filter(VMTemplateVO.class, "id", false, null, 1L);
List<VMTemplateVO> templates = listBy(sc, filter); List<VMTemplateVO> templates = listBy(sc, filter);
if (templates != null && !templates.isEmpty()) { if (templates != null && !templates.isEmpty()) {

View File

@ -16,6 +16,47 @@
// under the License. // under the License.
package com.cloud.upgrade; 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.DataCenterVO;
import com.cloud.dc.dao.ClusterDao; import com.cloud.dc.dao.ClusterDao;
import com.cloud.dc.dao.ClusterDaoImpl; import com.cloud.dc.dao.ClusterDaoImpl;
@ -36,6 +77,7 @@ import com.cloud.template.VirtualMachineTemplate;
import com.cloud.upgrade.dao.BasicTemplateDataStoreDaoImpl; import com.cloud.upgrade.dao.BasicTemplateDataStoreDaoImpl;
import com.cloud.user.Account; import com.cloud.user.Account;
import com.cloud.utils.DateUtil; import com.cloud.utils.DateUtil;
import com.cloud.utils.HttpUtils;
import com.cloud.utils.Pair; import com.cloud.utils.Pair;
import com.cloud.utils.UriUtils; import com.cloud.utils.UriUtils;
import com.cloud.utils.db.GlobalLock; 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.utils.script.Script;
import com.cloud.vm.dao.VMInstanceDao; import com.cloud.vm.dao.VMInstanceDao;
import com.cloud.vm.dao.VMInstanceDaoImpl; 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 { public class SystemVmTemplateRegistration {
protected static Logger LOGGER = LogManager.getLogger(SystemVmTemplateRegistration.class); 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 UMOUNT_COMMAND = "sudo umount %s";
private static final String RELATIVE_TEMPLATE_PATH = "./engine/schema/dist/systemvm-templates/"; 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/"; 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 LINUX_7_ID = 183;
private static final Integer SCRIPT_TIMEOUT = 1800000; private static final Integer SCRIPT_TIMEOUT = 1800000;
private static final Integer LOCK_WAIT_TIMEOUT = 1200; 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; public static String CS_MAJOR_VERSION = null;
@ -128,6 +135,8 @@ public class SystemVmTemplateRegistration {
private String systemVmTemplateVersion; private String systemVmTemplateVersion;
private final File tempDownloadDir;
public SystemVmTemplateRegistration() { public SystemVmTemplateRegistration() {
dataCenterDao = new DataCenterDaoImpl(); dataCenterDao = new DataCenterDaoImpl();
vmTemplateDao = new VMTemplateDaoImpl(); vmTemplateDao = new VMTemplateDaoImpl();
@ -138,6 +147,7 @@ public class SystemVmTemplateRegistration {
imageStoreDetailsDao = new ImageStoreDetailsDaoImpl(); imageStoreDetailsDao = new ImageStoreDetailsDaoImpl();
clusterDao = new ClusterDaoImpl(); clusterDao = new ClusterDaoImpl();
configurationDao = new ConfigurationDaoImpl(); 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) { 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)) { if (StringUtils.isNotBlank(nfsVersion)) {
cmd = String.format("%s -o vers=%s", cmd, nfsVersion); cmd = String.format("%s -o vers=%s", cmd, nfsVersion);
} }
@ -163,6 +173,10 @@ public class SystemVmTemplateRegistration {
return systemVmTemplateVersion; return systemVmTemplateVersion;
} }
public File getTempDownloadDir() {
return tempDownloadDir;
}
private static class SystemVMTemplateDetails { private static class SystemVMTemplateDetails {
Long id; Long id;
String uuid; String uuid;
@ -174,6 +188,7 @@ public class SystemVmTemplateRegistration {
ImageFormat format; ImageFormat format;
Integer guestOsId; Integer guestOsId;
Hypervisor.HypervisorType hypervisorType; Hypervisor.HypervisorType hypervisorType;
CPU.CPUArch arch;
Long storeId; Long storeId;
Long size; Long size;
Long physicalSize; Long physicalSize;
@ -183,7 +198,7 @@ public class SystemVmTemplateRegistration {
SystemVMTemplateDetails(String uuid, String name, Date created, String url, String checksum, SystemVMTemplateDetails(String uuid, String name, Date created, String url, String checksum,
ImageFormat format, Integer guestOsId, Hypervisor.HypervisorType hypervisorType, ImageFormat format, Integer guestOsId, Hypervisor.HypervisorType hypervisorType,
Long storeId) { CPU.CPUArch arch, Long storeId) {
this.uuid = uuid; this.uuid = uuid;
this.name = name; this.name = name;
this.created = created; this.created = created;
@ -192,6 +207,7 @@ public class SystemVmTemplateRegistration {
this.format = format; this.format = format;
this.guestOsId = guestOsId; this.guestOsId = guestOsId;
this.hypervisorType = hypervisorType; this.hypervisorType = hypervisorType;
this.arch = arch;
this.storeId = storeId; this.storeId = storeId;
} }
@ -235,6 +251,10 @@ public class SystemVmTemplateRegistration {
return hypervisorType; return hypervisorType;
} }
public CPU.CPUArch getArch() {
return arch;
}
public Long getStoreId() { public Long getStoreId() {
return storeId; return storeId;
} }
@ -288,18 +308,17 @@ public class SystemVmTemplateRegistration {
} }
} }
public static final List<Hypervisor.HypervisorType> hypervisorList = Arrays.asList(Hypervisor.HypervisorType.KVM, public static final List<Pair<Hypervisor.HypervisorType, CPU.CPUArch>> hypervisorList = Arrays.asList(
Hypervisor.HypervisorType.VMware, new Pair<>(Hypervisor.HypervisorType.KVM, CPU.CPUArch.amd64),
Hypervisor.HypervisorType.XenServer, new Pair<>(Hypervisor.HypervisorType.KVM, CPU.CPUArch.arm64),
Hypervisor.HypervisorType.Hyperv, new Pair<>(Hypervisor.HypervisorType.VMware, null),
Hypervisor.HypervisorType.LXC, new Pair<>(Hypervisor.HypervisorType.XenServer, null),
Hypervisor.HypervisorType.Ovm3 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<String, MetadataTemplateDetails> NewTemplateMap = new HashMap<>();
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<Hypervisor.HypervisorType, String> RouterTemplateConfigurationNames = new HashMap<Hypervisor.HypervisorType, String>() { 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) { private static String getHypervisorArchLog(Hypervisor.HypervisorType hypervisorType, CPU.CPUArch arch) {
VMTemplateVO vmTemplate = vmTemplateDao.findLatestTemplateByName(hypervisorAndTemplateName.second()); StringBuilder sb = new StringBuilder("hypervisor: ").append(hypervisorType.name());
Long templateId = null; if (Hypervisor.HypervisorType.KVM.equals(hypervisorType)) {
if (vmTemplate != null) { sb.append(", arch: ").append(arch == null ? CPU.CPUArch.amd64.getType() : arch.getType());
templateId = vmTemplate.getId();
} }
return templateId; return sb.toString();
} }
private static String fetchTemplatesPath() { protected static String getHypervisorArchKey(Hypervisor.HypervisorType hypervisorType, CPU.CPUArch arch) {
String filePath = RELATIVE_TEMPLATE_PATH + METADATA_FILE_NAME; if (Hypervisor.HypervisorType.KVM.equals(hypervisorType)) {
LOGGER.debug(String.format("Looking for file [ %s ] in the classpath.", filePath)); return String.format("%s-%s", hypervisorType.name().toLowerCase(),
File metaFile = new File(filePath); arch == null ? CPU.CPUArch.amd64.getType() : arch.getType());
String templatePath = null;
if (metaFile.exists()) {
templatePath = RELATIVE_TEMPLATE_PATH;
} }
if (templatePath == null) { return hypervisorType.name().toLowerCase();
filePath = ABSOLUTE_TEMPLATE_PATH + METADATA_FILE_NAME; }
metaFile = new File(filePath);
templatePath = ABSOLUTE_TEMPLATE_PATH; protected static MetadataTemplateDetails getMetadataTemplateDetails(Hypervisor.HypervisorType hypervisorType,
LOGGER.debug(String.format("Looking for file [ %s ] in the classpath.", filePath)); CPU.CPUArch arch) {
if (!metaFile.exists()) { return NewTemplateMap.get(getHypervisorArchKey(hypervisorType, arch));
String errMsg = String.format("Unable to locate metadata file in your setup at %s", filePath.toString()); }
LOGGER.error(errMsg);
throw new CloudRuntimeException(errMsg); 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; 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 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);
} }
private List<Long> getEligibleZoneIds() { private List<Long> getEligibleZoneIds() {
@ -430,28 +467,28 @@ public class SystemVmTemplateRegistration {
return zoneIds; return zoneIds;
} }
private Pair<String, Long> getNfsStoreInZone(Long zoneId) { protected Pair<String, Long> getNfsStoreInZone(Long zoneId) {
String url = null;
Long storeId = null;
ImageStoreVO storeVO = imageStoreDao.findOneByZoneAndProtocol(zoneId, "nfs"); ImageStoreVO storeVO = imageStoreDao.findOneByZoneAndProtocol(zoneId, "nfs");
if (storeVO == null) { 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); LOGGER.error(errMsg);
throw new CloudRuntimeException(errMsg); throw new CloudRuntimeException(errMsg);
} }
url = storeVO.getUrl(); String url = storeVO.getUrl();
storeId = storeVO.getId(); Long storeId = storeVO.getId();
return new Pair<>(url, storeId); return new Pair<>(url, storeId);
} }
public static void mountStore(String storeUrl, String path, String nfsVersion) { public static void mountStore(String storeUrl, String path, String nfsVersion) {
try { try {
if (storeUrl != null) { if (storeUrl == null) {
URI uri = new URI(UriUtils.encodeURIComponent(storeUrl)); return;
String host = uri.getHost();
String mountPath = uri.getPath();
Script.runSimpleBashScript(getMountCommand(nfsVersion, host + ":" + mountPath, path));
} }
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) { } catch (Exception e) {
String msg = "NFS Store URL is not in the correct format"; String msg = "NFS Store URL is not in the correct format";
LOGGER.error(msg, e); 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) { private VMTemplateVO createTemplateObjectInDB(SystemVMTemplateDetails details) {
Long templateId = vmTemplateDao.getNextInSequence(Long.class, "id"); Long templateId = vmTemplateDao.getNextInSequence(Long.class, "id");
VMTemplateVO template = new VMTemplateVO(); VMTemplateVO template = new VMTemplateVO();
@ -486,6 +516,7 @@ public class SystemVmTemplateRegistration {
template.setGuestOSId(details.getGuestOsId()); template.setGuestOSId(details.getGuestOsId());
template.setCrossZones(true); template.setCrossZones(true);
template.setHypervisorType(details.getHypervisorType()); template.setHypervisorType(details.getHypervisorType());
template.setArch(details.getArch());
template.setState(VirtualMachineTemplate.State.Inactive); template.setState(VirtualMachineTemplate.State.Inactive);
template.setDeployAsIs(false); template.setDeployAsIs(false);
template = vmTemplateDao.persist(template); template = vmTemplateDao.persist(template);
@ -627,16 +658,20 @@ public class SystemVmTemplateRegistration {
} }
} }
private void setupTemplate(String templateName, Pair<Hypervisor.HypervisorType, String> hypervisorAndTemplateName, private void setupTemplate(String templateName, Hypervisor.HypervisorType hypervisor, CPU.CPUArch arch,
String destTempFolder) throws CloudRuntimeException { String destTempFolder) throws CloudRuntimeException {
String setupTmpltScript = Script.findScript(storageScriptsDir, "setup-sysvm-tmplt"); String setupTmpltScript = Script.findScript(storageScriptsDir, "setup-sysvm-tmplt");
if (setupTmpltScript == null) { if (setupTmpltScript == null) {
throw new CloudRuntimeException("Unable to find the createtmplt.sh"); throw new CloudRuntimeException("Unable to find the createtmplt.sh");
} }
Script scr = new Script(setupTmpltScript, SCRIPT_TIMEOUT, LOGGER); Script scr = new Script(setupTmpltScript, SCRIPT_TIMEOUT, LOGGER);
scr.add("-u", templateName); scr.add("-u", templateName);
scr.add("-f", TEMPLATES_PATH + FileNames.get(hypervisorAndTemplateName.first())); MetadataTemplateDetails templateDetails = NewTemplateMap.get(getHypervisorArchKey(hypervisor, arch));
scr.add("-h", hypervisorAndTemplateName.first().name().toLowerCase(Locale.ROOT)); 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); scr.add("-d", destTempFolder);
String result = scr.execute(); String result = scr.execute();
if (result != null) { if (result != null) {
@ -644,17 +679,15 @@ public class SystemVmTemplateRegistration {
LOGGER.error(errMsg); LOGGER.error(errMsg);
throw new CloudRuntimeException(errMsg); throw new CloudRuntimeException(errMsg);
} }
} }
private Long performTemplateRegistrationOperations(Pair<Hypervisor.HypervisorType, String> hypervisorAndTemplateName, private Long performTemplateRegistrationOperations(Hypervisor.HypervisorType hypervisor,
String url, String checksum, ImageFormat format, long guestOsId, String name, CPU.CPUArch arch, String url, String checksum, ImageFormat format, long guestOsId,
Long storeId, Long templateId, String filePath, TemplateDataStoreVO templateDataStoreVO) { Long storeId, Long templateId, String filePath, TemplateDataStoreVO templateDataStoreVO) {
Hypervisor.HypervisorType hypervisor = hypervisorAndTemplateName.first();
String templateName = UUID.randomUUID().toString(); String templateName = UUID.randomUUID().toString();
Date created = new Date(DateUtil.currentGMTTime().getTime()); Date created = new Date(DateUtil.currentGMTTime().getTime());
SystemVMTemplateDetails details = new SystemVMTemplateDetails(templateName, hypervisorAndTemplateName.second(), created, SystemVMTemplateDetails details = new SystemVMTemplateDetails(templateName, name, created,
url, checksum, format, (int) guestOsId, hypervisor, storeId); url, checksum, format, (int) guestOsId, hypervisor, arch, storeId);
if (templateId == null) { if (templateId == null) {
VMTemplateVO template = createTemplateObjectInDB(details); VMTemplateVO template = createTemplateObjectInDB(details);
if (template == null) { if (template == null) {
@ -671,23 +704,44 @@ public class SystemVmTemplateRegistration {
if (templateDataStoreVO == null) { if (templateDataStoreVO == null) {
createTemplateStoreRefEntry(details); createTemplateStoreRefEntry(details);
} }
setupTemplate(templateName, hypervisorAndTemplateName, destTempFolder); setupTemplate(templateName, hypervisor, arch, destTempFolder);
readTemplateProperties(destTempFolder + "/template.properties", details); readTemplateProperties(destTempFolder + "/template.properties", details);
details.setUpdated(new Date(DateUtil.currentGMTTime().getTime())); details.setUpdated(new Date(DateUtil.currentGMTTime().getTime()));
updateTemplateDetails(details); updateTemplateDetails(details);
return templateId; return templateId;
} }
public void registerTemplate(Pair<Hypervisor.HypervisorType, String> hypervisorAndTemplateName, public void registerTemplate(Hypervisor.HypervisorType hypervisor, String name, Long storeId,
Pair<String, Long> storeUrlAndId, VMTemplateVO templateVO, VMTemplateVO templateVO, TemplateDataStoreVO templateDataStoreVO, String filePath) {
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; Long templateId = null;
try { try {
templateId = templateVO.getId(); MetadataTemplateDetails templateDetails = getMetadataTemplateDetails(hypervisor, arch);
performTemplateRegistrationOperations(hypervisorAndTemplateName, templateVO.getUrl(), templateVO.getChecksum(), templateId = performTemplateRegistrationOperations(hypervisor, name,
templateVO.getFormat(), templateVO.getGuestOSId(), storeUrlAndId.second(), templateId, filePath, templateDataStoreVO); 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) { } 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); LOGGER.error(errMsg, e);
if (templateId != null) { if (templateId != null) {
updateTemplateTablesOnFailure(templateId); updateTemplateTablesOnFailure(templateId);
@ -697,26 +751,31 @@ public class SystemVmTemplateRegistration {
} }
} }
public void registerTemplate(Pair<Hypervisor.HypervisorType, String> hypervisorAndTemplateName, Pair<String, Long> storeUrlAndId, String filePath) { protected void validateTemplateFileForHypervisorAndArch(Hypervisor.HypervisorType hypervisor, CPU.CPUArch arch) {
Long templateId = null; MetadataTemplateDetails templateDetails = getMetadataTemplateDetails(hypervisor, arch);
try { File templateFile = getTemplateFile(templateDetails);
Hypervisor.HypervisorType hypervisor = hypervisorAndTemplateName.first(); if (templateFile == null) {
templateId = performTemplateRegistrationOperations(hypervisorAndTemplateName, NewTemplateUrl.get(hypervisor), NewTemplateChecksum.get(hypervisor), throw new CloudRuntimeException("Failed to find local template file");
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);
} }
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. * exist a template corresponding to the current code version.
*/ */
public static String parseMetadataFile() { public static String parseMetadataFile() {
try { String metadataFilePath = getMetadataFilePath();
Ini ini = new Ini(); String errMsg = String.format("Failed to parse systemVM template metadata file: %s", metadataFilePath);
ini.load(new FileReader(METADATA_FILE)); final Ini ini = new Ini();
for (Hypervisor.HypervisorType hypervisorType : hypervisorList) { try (FileReader reader = new FileReader(metadataFilePath)) {
String hypervisor = hypervisorType.name().toLowerCase(Locale.ROOT); ini.load(reader);
Ini.Section section = ini.get(hypervisor); } catch (IOException e) {
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);
LOGGER.error(errMsg, e); LOGGER.error(errMsg, e);
throw new CloudRuntimeException(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) { private static void cleanupStore(Long templateId, String filePath) {
String destTempFolder = filePath + PARTIAL_TEMPLATE_FOLDER + String.valueOf(templateId); String destTempFolder = filePath + PARTIAL_TEMPLATE_FOLDER + String.valueOf(templateId);
try { try {
@ -755,31 +829,60 @@ public class SystemVmTemplateRegistration {
} }
} }
private void validateTemplates(Set<Hypervisor.HypervisorType> hypervisorsInUse) { protected File getTemplateFile(MetadataTemplateDetails templateDetails) {
Set<String> hypervisors = hypervisorsInUse.stream(). File templateFile = new File(templateDetails.getDefaultFilePath());
map(Hypervisor.HypervisorType::name).map(name -> name.toLowerCase(Locale.ROOT)).map(this::getHypervisorName).collect(Collectors.toSet()); if (templateFile.exists()) {
List<String> templates = new ArrayList<>(); return templateFile;
for (Hypervisor.HypervisorType hypervisorType : hypervisorsInUse) {
templates.add(FileNames.get(hypervisorType));
} }
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; boolean templatesFound = true;
for (String hypervisor : hypervisors) { for (Pair<Hypervisor.HypervisorType, CPU.CPUArch> hypervisorArch : hypervisorsArchInUse) {
String matchedTemplate = templates.stream().filter(x -> x.contains(hypervisor)).findAny().orElse(null); MetadataTemplateDetails matchedTemplate = getMetadataTemplateDetails(hypervisorArch.first(),
hypervisorArch.second());
if (matchedTemplate == null) { if (matchedTemplate == null) {
templatesFound = false; templatesFound = false;
break; break;
} }
File tempFile = getTemplateFile(matchedTemplate);
File tempFile = new File(TEMPLATES_PATH + matchedTemplate); if (tempFile == null) {
String templateChecksum = DigestHelper.calculateChecksum(tempFile); LOGGER.warn("Failed to download template for {}, moving ahead",
if (!templateChecksum.equals(NewTemplateChecksum.get(getHypervisorType(hypervisor)))) { matchedTemplate.getHypervisorArchLog());
LOGGER.error(String.format("Checksum mismatch: %s != %s ", templateChecksum, NewTemplateChecksum.get(getHypervisorType(hypervisor)))); continue;
}
if (isTemplateFileChecksumDifferent(matchedTemplate, tempFile)) {
templatesFound = false; templatesFound = false;
break; break;
} }
} }
if (!templatesFound) { if (!templatesFound) {
String errMsg = "SystemVm template not found. Cannot upgrade system Vms"; String errMsg = "SystemVm template not found. Cannot upgrade system Vms";
LOGGER.error(errMsg); 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"); GlobalLock lock = GlobalLock.getInternLock("UpgradeDatabase-Lock");
try { try {
LOGGER.info("Grabbing lock to register templates."); 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."); throw new CloudRuntimeException("Unable to acquire lock to register SystemVM template.");
} }
try { try {
validateTemplates(hypervisorsInUse); validateTemplates(hypervisorsArchInUse);
// Perform Registration if templates not already registered // Perform Registration if templates not already registered
Transaction.execute(new TransactionCallbackNoReturn() { Transaction.execute(new TransactionCallbackNoReturn() {
@Override @Override
@ -808,32 +944,7 @@ public class SystemVmTemplateRegistration {
if (filePath == null) { if (filePath == null) {
throw new CloudRuntimeException("Failed to create temporary file path to mount the store"); throw new CloudRuntimeException("Failed to create temporary file path to mount the store");
} }
Pair<String, Long> storeUrlAndId = getNfsStoreInZone(zoneId); registerTemplatesForZone(zoneId, filePath);
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);
}
unmountStore(filePath); unmountStore(filePath);
} catch (Exception e) { } catch (Exception e) {
unmountStore(filePath); unmountStore(filePath);
@ -851,12 +962,7 @@ public class SystemVmTemplateRegistration {
} }
} }
private void updateRegisteredTemplateDetails(Long templateId, Map.Entry<Hypervisor.HypervisorType, String> hypervisorAndTemplateName) { private void updateRegisteredTemplateDetails(Long templateId, MetadataTemplateDetails templateDetails) {
Pair<Hypervisor.HypervisorType, String> entry = new Pair<>(hypervisorAndTemplateName.getKey(), hypervisorAndTemplateName.getValue());
updateRegisteredTemplateDetails(templateId, entry);
}
private void updateRegisteredTemplateDetails(Long templateId, Pair<Hypervisor.HypervisorType, String> hypervisorAndTemplateName) {
VMTemplateVO templateVO = vmTemplateDao.findById(templateId); VMTemplateVO templateVO = vmTemplateDao.findById(templateId);
templateVO.setTemplateType(Storage.TemplateType.SYSTEM); templateVO.setTemplateType(Storage.TemplateType.SYSTEM);
boolean updated = vmTemplateDao.update(templateVO.getId(), templateVO); boolean updated = vmTemplateDao.update(templateVO.getId(), templateVO);
@ -865,68 +971,81 @@ public class SystemVmTemplateRegistration {
LOGGER.error(errMsg); LOGGER.error(errMsg);
throw new CloudRuntimeException(errMsg); throw new CloudRuntimeException(errMsg);
} }
Hypervisor.HypervisorType hypervisorType = templateDetails.getHypervisorType();
updateSystemVMEntries(templateId, hypervisorAndTemplateName.first()); updateSystemVMEntries(templateId, hypervisorType);
// Change value of global configuration parameter router.template.* for the corresponding hypervisor and minreq.sysvmtemplate.version for the ACS version // 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<>(); 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()); configParams.put("minreq.sysvmtemplate.version", getSystemVmTemplateVersion());
updateConfigurationParams(configParams); updateConfigurationParams(configParams);
} }
private void updateTemplateUrlAndChecksum(VMTemplateVO templateVO, Map.Entry<Hypervisor.HypervisorType, String> hypervisorAndTemplateName) { private void updateTemplateUrlAndChecksum(VMTemplateVO templateVO, MetadataTemplateDetails templateDetails) {
templateVO.setUrl(NewTemplateUrl.get(hypervisorAndTemplateName.getKey())); templateVO.setUrl(templateDetails.getUrl());
templateVO.setChecksum(NewTemplateChecksum.get(hypervisorAndTemplateName.getKey())); templateVO.setChecksum(templateDetails.getChecksum());
boolean updated = vmTemplateDao.update(templateVO.getId(), templateVO); boolean updated = vmTemplateDao.update(templateVO.getId(), templateVO);
if (!updated) { 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); LOGGER.error(errMsg);
throw new CloudRuntimeException(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) { public void updateSystemVmTemplates(final Connection conn) {
LOGGER.debug("Updating System Vm template IDs"); LOGGER.debug("Updating System Vm template IDs");
Transaction.execute(new TransactionCallbackNoReturn() { Transaction.execute(new TransactionCallbackNoReturn() {
@Override @Override
public void doInTransactionWithoutResult(final TransactionStatus status) { public void doInTransactionWithoutResult(final TransactionStatus status) {
Set<Hypervisor.HypervisorType> hypervisorsListInUse = new HashSet<Hypervisor.HypervisorType>(); List<Pair<Hypervisor.HypervisorType, CPU.CPUArch>> hypervisorsInUse;
try { try {
hypervisorsListInUse = clusterDao.getDistinctAvailableHypervisorsAcrossClusters(); hypervisorsInUse = clusterDao.listDistinctHypervisorsArchAcrossClusters(null);
} catch (final Exception e) { } catch (final Exception e) {
LOGGER.error("updateSystemVmTemplates: Exception caught while getting hypervisor types from clusters: " + e.getMessage()); throw new CloudRuntimeException("Exception while getting hypervisor types from clusters", e);
throw new CloudRuntimeException("updateSystemVmTemplates:Exception while getting hypervisor types from clusters", e);
} }
Collection<MetadataTemplateDetails> templateEntries = NewTemplateMap.values();
for (final Map.Entry<Hypervisor.HypervisorType, String> hypervisorAndTemplateName : NewTemplateNameList.entrySet()) { for (MetadataTemplateDetails templateDetails : templateEntries) {
LOGGER.debug("Updating " + hypervisorAndTemplateName.getKey() + " System Vms");
Long templateId = getRegisteredTemplateId(new Pair<>(hypervisorAndTemplateName.getKey(), hypervisorAndTemplateName.getValue()));
try { try {
// change template type to SYSTEM if (registerOrUpdateSystemVmTemplate(templateDetails, hypervisorsInUse)) {
if (templateId != null) { break;
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);
}
}
} }
} catch (final Exception e) { } 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); LOGGER.error(errMsg, e);
throw new CloudRuntimeException(errMsg, e); throw new CloudRuntimeException(errMsg, e);
} }
@ -948,4 +1067,64 @@ public class SystemVmTemplateRegistration {
} }
return null; 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 { try {
systemVmTemplateRegistration.updateSystemVmTemplates(conn); systemVmTemplateRegistration.updateSystemVmTemplates(conn);
} catch (Exception e) { } 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> 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); 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> nameSearch;
private SearchBuilder<ImageStoreVO> providerSearch; private SearchBuilder<ImageStoreVO> providerSearch;
private SearchBuilder<ImageStoreVO> regionSearch; private SearchBuilder<ImageStoreVO> regionSearch;
private SearchBuilder<ImageStoreVO> storeSearch; private SearchBuilder<ImageStoreVO> storesExceptIdSearch;
private SearchBuilder<ImageStoreVO> protocolSearch; private SearchBuilder<ImageStoreVO> protocolSearch;
private SearchBuilder<ImageStoreVO> zoneProtocolSearch; 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.and("role", regionSearch.entity().getRole(), SearchCriteria.Op.EQ);
regionSearch.done(); regionSearch.done();
storeSearch = createSearchBuilder(); storesExceptIdSearch = createSearchBuilder();
storeSearch.and("providerName", storeSearch.entity().getProviderName(), SearchCriteria.Op.EQ); storesExceptIdSearch.and("providerName", storesExceptIdSearch.entity().getProviderName(), SearchCriteria.Op.EQ);
storeSearch.and("role", storeSearch.entity().getRole(), SearchCriteria.Op.EQ); storesExceptIdSearch.and("role", storesExceptIdSearch.entity().getRole(), SearchCriteria.Op.EQ);
storeSearch.and("dataCenterId", storeSearch.entity().getDcId(), SearchCriteria.Op.EQ); storesExceptIdSearch.and("dataCenterId", storesExceptIdSearch.entity().getDcId(), SearchCriteria.Op.EQ);
storeSearch.done(); storesExceptIdSearch.and("id", storesExceptIdSearch.entity().getId(), SearchCriteria.Op.NEQ);
storesExceptIdSearch.done();
return true; return true;
} }
@ -113,11 +114,12 @@ public class ImageStoreDaoImpl extends GenericDaoBase<ImageStoreVO, Long> implem
} }
@Override @Override
public List<ImageStoreVO> listAllStoresInZone(Long zoneId, String provider, DataStoreRole role) { public List<ImageStoreVO> listAllStoresInZoneExceptId(Long zoneId, String provider, DataStoreRole role, long id) {
SearchCriteria<ImageStoreVO> sc = storeSearch.create(); SearchCriteria<ImageStoreVO> sc = storesExceptIdSearch.create();
sc.setParameters("providerName", provider); sc.setParameters("providerName", provider);
sc.setParameters("role", role); sc.setParameters("role", role);
sc.setParameters("dataCenterId", zoneId); sc.setParameters("dataCenterId", zoneId);
sc.setParameters("id", id);
return listBy(sc); return listBy(sc);
} }

View File

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

View File

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

View File

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

View File

@ -17,6 +17,7 @@
package com.cloud.dc.dao; package com.cloud.dc.dao;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue; import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.any; import static org.mockito.Mockito.any;
import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.doReturn;
@ -36,9 +37,13 @@ import org.mockito.InjectMocks;
import org.mockito.Spy; import org.mockito.Spy;
import org.mockito.junit.MockitoJUnitRunner; import org.mockito.junit.MockitoJUnitRunner;
import com.cloud.cpu.CPU;
import com.cloud.dc.ClusterVO; 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.GenericSearchBuilder;
import com.cloud.utils.db.SearchBuilder; import com.cloud.utils.db.SearchBuilder;
import com.cloud.utils.db.SearchCriteria;
@RunWith(MockitoJUnitRunner.class) @RunWith(MockitoJUnitRunner.class)
public class ClusterDaoImplTest { public class ClusterDaoImplTest {
@ -75,4 +80,39 @@ public class ClusterDaoImplTest {
verify(clusterDao).customSearch(genericSearchBuilder.create(), null); verify(clusterDao).customSearch(genericSearchBuilder.create(), null);
assertTrue(result.isEmpty()); 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. // under the License.
package com.cloud.host.dao; 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.List;
import java.util.stream.Collectors;
import org.junit.Assert; import org.junit.Assert;
import org.junit.Test; import org.junit.Test;
@ -26,6 +37,7 @@ import org.mockito.Mockito;
import org.mockito.Spy; import org.mockito.Spy;
import org.mockito.junit.MockitoJUnitRunner; import org.mockito.junit.MockitoJUnitRunner;
import com.cloud.cpu.CPU;
import com.cloud.host.Host; import com.cloud.host.Host;
import com.cloud.host.HostVO; import com.cloud.host.HostVO;
import com.cloud.host.Status; import com.cloud.host.Status;
@ -52,10 +64,10 @@ public class HostDaoImplTest {
public void testCountUpAndEnabledHostsInZone() { public void testCountUpAndEnabledHostsInZone() {
long testZoneId = 100L; long testZoneId = 100L;
hostDao.HostTypeCountSearch = mockSearchBuilder; hostDao.HostTypeCountSearch = mockSearchBuilder;
Mockito.when(mockSearchBuilder.create()).thenReturn(mockSearchCriteria); when(mockSearchBuilder.create()).thenReturn(mockSearchCriteria);
Mockito.doNothing().when(mockSearchCriteria).setParameters(Mockito.anyString(), Mockito.any()); Mockito.doNothing().when(mockSearchCriteria).setParameters(Mockito.anyString(), any());
int expected = 5; int expected = 5;
Mockito.doReturn(expected).when(hostDao).getCount(mockSearchCriteria); doReturn(expected).when(hostDao).getCount(mockSearchCriteria);
Integer count = hostDao.countUpAndEnabledHostsInZone(testZoneId); Integer count = hostDao.countUpAndEnabledHostsInZone(testZoneId);
Assert.assertSame(expected, count); Assert.assertSame(expected, count);
Mockito.verify(mockSearchCriteria).setParameters("type", Host.Type.Routing); Mockito.verify(mockSearchCriteria).setParameters("type", Host.Type.Routing);
@ -70,16 +82,16 @@ public class HostDaoImplTest {
GenericDaoBase.SumCount mockSumCount = new GenericDaoBase.SumCount(); GenericDaoBase.SumCount mockSumCount = new GenericDaoBase.SumCount();
mockSumCount.count = 10; mockSumCount.count = 10;
mockSumCount.sum = 20; mockSumCount.sum = 20;
HostVO host = Mockito.mock(HostVO.class); HostVO host = mock(HostVO.class);
GenericSearchBuilder<HostVO, GenericDaoBase.SumCount> sb = Mockito.mock(GenericSearchBuilder.class); GenericSearchBuilder<HostVO, GenericDaoBase.SumCount> sb = mock(GenericSearchBuilder.class);
Mockito.when(sb.entity()).thenReturn(host); when(sb.entity()).thenReturn(host);
Mockito.doReturn(sb).when(hostDao).createSearchBuilder(GenericDaoBase.SumCount.class); doReturn(sb).when(hostDao).createSearchBuilder(GenericDaoBase.SumCount.class);
SearchCriteria<GenericDaoBase.SumCount> sc = Mockito.mock(SearchCriteria.class); SearchCriteria<GenericDaoBase.SumCount> sc = mock(SearchCriteria.class);
Mockito.when(sb.create()).thenReturn(sc); when(sb.create()).thenReturn(sc);
Mockito.doReturn(List.of(mockSumCount)).when(hostDao).customSearch(Mockito.any(SearchCriteria.class), Mockito.any()); doReturn(List.of(mockSumCount)).when(hostDao).customSearch(any(SearchCriteria.class), any());
Pair<Integer, Integer> result = hostDao.countAllHostsAndCPUSocketsByType(type); Pair<Integer, Integer> result = hostDao.countAllHostsAndCPUSocketsByType(type);
Assert.assertEquals(10, result.first().intValue()); assertEquals(10, result.first().intValue());
Assert.assertEquals(20, result.second().intValue()); assertEquals(20, result.second().intValue());
Mockito.verify(sc).setParameters("type", type); Mockito.verify(sc).setParameters("type", type);
} }
@ -87,13 +99,13 @@ public class HostDaoImplTest {
public void testIsHostUp() { public void testIsHostUp() {
long testHostId = 101L; long testHostId = 101L;
List<Status> statuses = List.of(Status.Up); List<Status> statuses = List.of(Status.Up);
HostVO host = Mockito.mock(HostVO.class); HostVO host = mock(HostVO.class);
GenericSearchBuilder<HostVO, Status> sb = Mockito.mock(GenericSearchBuilder.class); GenericSearchBuilder<HostVO, Status> sb = mock(GenericSearchBuilder.class);
Mockito.when(sb.entity()).thenReturn(host); when(sb.entity()).thenReturn(host);
SearchCriteria<Status> sc = Mockito.mock(SearchCriteria.class); SearchCriteria<Status> sc = mock(SearchCriteria.class);
Mockito.when(sb.create()).thenReturn(sc); when(sb.create()).thenReturn(sc);
Mockito.doReturn(sb).when(hostDao).createSearchBuilder(Status.class); doReturn(sb).when(hostDao).createSearchBuilder(Status.class);
Mockito.doReturn(statuses).when(hostDao).customSearch(Mockito.any(SearchCriteria.class), Mockito.any()); doReturn(statuses).when(hostDao).customSearch(any(SearchCriteria.class), any());
boolean result = hostDao.isHostUp(testHostId); boolean result = hostDao.isHostUp(testHostId);
Assert.assertTrue("Host should be up", result); Assert.assertTrue("Host should be up", result);
Mockito.verify(sc).setParameters("id", testHostId); Mockito.verify(sc).setParameters("id", testHostId);
@ -108,17 +120,17 @@ public class HostDaoImplTest {
List<Host.Type> types = List.of(Host.Type.Routing); List<Host.Type> types = List.of(Host.Type.Routing);
List<Hypervisor.HypervisorType> hypervisorTypes = List.of(Hypervisor.HypervisorType.KVM); List<Hypervisor.HypervisorType> hypervisorTypes = List.of(Hypervisor.HypervisorType.KVM);
List<Long> mockResults = List.of(1001L, 1002L); // Mocked result List<Long> mockResults = List.of(1001L, 1002L); // Mocked result
HostVO host = Mockito.mock(HostVO.class); HostVO host = mock(HostVO.class);
GenericSearchBuilder<HostVO, Long> sb = Mockito.mock(GenericSearchBuilder.class); GenericSearchBuilder<HostVO, Long> sb = mock(GenericSearchBuilder.class);
Mockito.when(sb.entity()).thenReturn(host); when(sb.entity()).thenReturn(host);
SearchCriteria<Long> sc = Mockito.mock(SearchCriteria.class); SearchCriteria<Long> sc = mock(SearchCriteria.class);
Mockito.when(sb.create()).thenReturn(sc); when(sb.create()).thenReturn(sc);
Mockito.when(sb.and()).thenReturn(sb); when(sb.and()).thenReturn(sb);
Mockito.doReturn(sb).when(hostDao).createSearchBuilder(Long.class); doReturn(sb).when(hostDao).createSearchBuilder(Long.class);
Mockito.doReturn(mockResults).when(hostDao).customSearch(Mockito.any(SearchCriteria.class), Mockito.any()); doReturn(mockResults).when(hostDao).customSearch(any(SearchCriteria.class), any());
List<Long> hostIds = hostDao.findHostIdsByZoneClusterResourceStateTypeAndHypervisorType( List<Long> hostIds = hostDao.findHostIdsByZoneClusterResourceStateTypeAndHypervisorType(
zoneId, clusterId, resourceStates, types, hypervisorTypes); zoneId, clusterId, resourceStates, types, hypervisorTypes);
Assert.assertEquals(mockResults, hostIds); assertEquals(mockResults, hostIds);
Mockito.verify(sc).setParameters("zoneId", zoneId); Mockito.verify(sc).setParameters("zoneId", zoneId);
Mockito.verify(sc).setParameters("clusterId", clusterId); Mockito.verify(sc).setParameters("clusterId", clusterId);
Mockito.verify(sc).setParameters("resourceState", resourceStates.toArray()); Mockito.verify(sc).setParameters("resourceState", resourceStates.toArray());
@ -130,15 +142,16 @@ public class HostDaoImplTest {
public void testListDistinctHypervisorTypes() { public void testListDistinctHypervisorTypes() {
Long zoneId = 1L; Long zoneId = 1L;
List<Hypervisor.HypervisorType> mockResults = List.of(Hypervisor.HypervisorType.KVM, Hypervisor.HypervisorType.XenServer); List<Hypervisor.HypervisorType> mockResults = List.of(Hypervisor.HypervisorType.KVM, Hypervisor.HypervisorType.XenServer);
HostVO host = Mockito.mock(HostVO.class); HostVO host = mock(HostVO.class);
GenericSearchBuilder<HostVO, Hypervisor.HypervisorType> sb = Mockito.mock(GenericSearchBuilder.class); GenericSearchBuilder<HostVO, String> sb = mock(GenericSearchBuilder.class);
Mockito.when(sb.entity()).thenReturn(host); when(sb.entity()).thenReturn(host);
SearchCriteria<Hypervisor.HypervisorType> sc = Mockito.mock(SearchCriteria.class); SearchCriteria<String> sc = mock(SearchCriteria.class);
Mockito.when(sb.create()).thenReturn(sc); when(sb.create()).thenReturn(sc);
Mockito.doReturn(sb).when(hostDao).createSearchBuilder(Hypervisor.HypervisorType.class); doReturn(sb).when(hostDao).createSearchBuilder(String.class);
Mockito.doReturn(mockResults).when(hostDao).customSearch(Mockito.any(SearchCriteria.class), Mockito.any()); doReturn(mockResults.stream().map(h -> h.name()).collect(Collectors.toList())).when(hostDao)
.customSearch(any(SearchCriteria.class), any());
List<Hypervisor.HypervisorType> hypervisorTypes = hostDao.listDistinctHypervisorTypes(zoneId); List<Hypervisor.HypervisorType> hypervisorTypes = hostDao.listDistinctHypervisorTypes(zoneId);
Assert.assertEquals(mockResults, hypervisorTypes); assertEquals(mockResults, hypervisorTypes);
Mockito.verify(sc).setParameters("zoneId", zoneId); Mockito.verify(sc).setParameters("zoneId", zoneId);
Mockito.verify(sc).setParameters("type", Host.Type.Routing); Mockito.verify(sc).setParameters("type", Host.Type.Routing);
} }
@ -146,12 +159,12 @@ public class HostDaoImplTest {
@Test @Test
public void testListByIds() { public void testListByIds() {
List<Long> ids = List.of(101L, 102L); 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; hostDao.IdsSearch = mockSearchBuilder;
Mockito.when(mockSearchBuilder.create()).thenReturn(mockSearchCriteria); when(mockSearchBuilder.create()).thenReturn(mockSearchCriteria);
Mockito.doReturn(mockResults).when(hostDao).search(Mockito.any(SearchCriteria.class), Mockito.any()); doReturn(mockResults).when(hostDao).search(any(SearchCriteria.class), any());
List<HostVO> hosts = hostDao.listByIds(ids); List<HostVO> hosts = hostDao.listByIds(ids);
Assert.assertEquals(mockResults, hosts); assertEquals(mockResults, hosts);
Mockito.verify(mockSearchCriteria).setParameters("id", ids.toArray()); Mockito.verify(mockSearchCriteria).setParameters("id", ids.toArray());
Mockito.verify(hostDao).search(mockSearchCriteria, null); Mockito.verify(hostDao).search(mockSearchCriteria, null);
} }
@ -164,15 +177,15 @@ public class HostDaoImplTest {
Hypervisor.HypervisorType hypervisorType = Hypervisor.HypervisorType.KVM; Hypervisor.HypervisorType hypervisorType = Hypervisor.HypervisorType.KVM;
Long zoneId = 1L, podId = 2L, clusterId = 3L; Long zoneId = 1L, podId = 2L, clusterId = 3L;
List<Long> mockResults = List.of(1001L, 1002L); List<Long> mockResults = List.of(1001L, 1002L);
HostVO host = Mockito.mock(HostVO.class); HostVO host = mock(HostVO.class);
GenericSearchBuilder<HostVO, Long> sb = Mockito.mock(GenericSearchBuilder.class); GenericSearchBuilder<HostVO, Long> sb = mock(GenericSearchBuilder.class);
Mockito.when(sb.entity()).thenReturn(host); when(sb.entity()).thenReturn(host);
SearchCriteria<Long> sc = Mockito.mock(SearchCriteria.class); SearchCriteria<Long> sc = mock(SearchCriteria.class);
Mockito.when(sb.create()).thenReturn(sc); when(sb.create()).thenReturn(sc);
Mockito.doReturn(sb).when(hostDao).createSearchBuilder(Long.class); doReturn(sb).when(hostDao).createSearchBuilder(Long.class);
Mockito.doReturn(mockResults).when(hostDao).customSearch(Mockito.any(SearchCriteria.class), Mockito.any()); doReturn(mockResults).when(hostDao).customSearch(any(SearchCriteria.class), any());
List<Long> hostIds = hostDao.listIdsBy(type, status, resourceState, hypervisorType, zoneId, podId, clusterId); 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("type", type);
Mockito.verify(sc).setParameters("status", status); Mockito.verify(sc).setParameters("status", status);
Mockito.verify(sc).setParameters("resourceState", resourceState); Mockito.verify(sc).setParameters("resourceState", resourceState);
@ -181,4 +194,57 @@ public class HostDaoImplTest {
Mockito.verify(sc).setParameters("podId", podId); Mockito.verify(sc).setParameters("podId", podId);
Mockito.verify(sc).setParameters("clusterId", clusterId); 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[@]}" for template in "${templates[@]}"
do do
section="${template%%:*}" section="${template%%:*}"
hvName=$(getGenericName $section) sectionHv="${section%%-*}"
hvName=$(getGenericName $sectionHv)
downloadurl="${template#*:}" downloadurl="${template#*:}"
arch=$(echo ${downloadurl#*"/systemvmtemplate-$VERSION-"} | cut -d'-' -f 1) 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") checksum=$(getChecksum "$fileData" "$VERSION-${arch}-$hvName")
filename=$(echo ${downloadurl##*'/'}) filename=$(echo ${downloadurl##*'/'})
echo -e "["$section"]\ntemplatename = $templatename\nchecksum = $checksum\ndownloadurl = $downloadurl\nfilename = $filename\narch = $arch\n" >> $METADATAFILE echo -e "["$section"]\ntemplatename = $templatename\nchecksum = $checksum\ndownloadurl = $downloadurl\nfilename = $filename\narch = $arch\n" >> $METADATAFILE
@ -71,7 +72,8 @@ function createMetadataFile() {
declare -a templates declare -a templates
getTemplateVersion $1 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" "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" "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" "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 { } else {
field.set(entity, rs.getLong(index)); 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()) { } else if (type.isEnum()) {
final Enumerated enumerated = field.getAnnotation(Enumerated.class); final Enumerated enumerated = field.getAnnotation(Enumerated.class);
final EnumType enumType = (enumerated == null) ? EnumType.STRING : enumerated.value(); 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) { } else if (type == byte[].class) {
field.set(entity, rs.getBytes(index)); 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 { } else {
field.set(entity, rs.getObject(index)); field.set(entity, rs.getObject(index));
} }
@ -949,7 +949,7 @@ public abstract class GenericDaoBase<T, ID extends Serializable> extends Compone
} }
@DB() @DB()
protected List<T> listBy(SearchCriteria<T> sc, final Filter filter) { public List<T> listBy(SearchCriteria<T> sc, final Filter filter) {
sc = checkAndSetRemovedIsNull(sc); sc = checkAndSetRemovedIsNull(sc);
return listIncludingRemovedBy(sc, filter); 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.Factory;
import net.sf.cglib.proxy.MethodInterceptor; import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy; import net.sf.cglib.proxy.MethodProxy;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
/** /**
@ -122,7 +124,7 @@ public abstract class SearchBase<J extends SearchBase<?, T, K>, T, K> {
if (_entity == null) { if (_entity == null) {
throw new RuntimeException("SearchBuilder cannot be modified once it has been setup"); 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"); throw new RuntimeException("You can't specify more than one field to search on");
} }
if (func.getCount() != -1 && (func.getCount() != (params.length + 1))) { 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); _selects.add(select);
_specifiedAttrs.clear(); _specifiedAttrs.clear();
@ -185,7 +187,7 @@ public abstract class SearchBase<J extends SearchBase<?, T, K>, T, K> {
} catch (final SecurityException e) { } catch (final SecurityException e) {
} catch (final NoSuchFieldException 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(); _specifiedAttrs.clear();
@ -528,16 +530,18 @@ public abstract class SearchBase<J extends SearchBase<?, T, K>, T, K> {
protected static class Select { protected static class Select {
public Func func; public Func func;
public Attribute attr; public List<Attribute> attributes = new ArrayList<>();
public Object[] params; public Object[] params;
public Field field; public Field field;
protected Select() { 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.func = func;
this.attr = attr; if (CollectionUtils.isNotEmpty(attributes)) {
this.attributes.addAll(attributes);
}
this.params = params; this.params = params;
this.field = field; this.field = field;
} }

View File

@ -23,6 +23,7 @@ import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.ArrayUtils; import org.apache.commons.lang3.ArrayUtils;
import com.cloud.utils.Pair; import com.cloud.utils.Pair;
@ -59,7 +60,7 @@ public class SearchCriteria<K> {
} }
public enum Func { 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 String func;
private int count; private int count;
@ -135,10 +136,12 @@ public class SearchCriteria<K> {
for (Select select : _selects) { for (Select select : _selects) {
String func = select.func.toString() + ","; String func = select.func.toString() + ",";
if (select.attr == null) { if (CollectionUtils.isEmpty(select.attributes)) {
func = func.replace("@", "*"); func = func.replace("@", "*");
} else { } 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); str.insert(insertAt, func);
insertAt += func.length(); insertAt += func.length();

View File

@ -470,6 +470,8 @@ if [ -f %{_sysconfdir}/sysconfig/%{name}-management ] ; then
fi fi
chown -R cloud:cloud /var/log/cloudstack/management 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 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/*.sh
%{_datadir}/%{name}-management/setup/server-setup.xml %{_datadir}/%{name}-management/setup/server-setup.xml
%{_datadir}/%{name}-management/webapp/* %{_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) %{_bindir}/%{name}-external-ipallocator.py
%attr(0755,root,root) %{_initrddir}/%{name}-ipallocator %attr(0755,root,root) %{_initrddir}/%{name}-ipallocator
%dir %attr(0770,root,root) %{_localstatedir}/log/%{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 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_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 int DEFAULT_HOST_SPEED_MHZ = 8000; // 1 GHz CPUs
public static final String DEFAULT_HOST_ARCH = "x86_64";
@Override @Override
boolean configure(String name, Map<String, Object> params) throws ConfigurationException; 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 cpuSpeed = Long.parseLong((String)params.get("cpuspeed"));
long memory = Long.parseLong((String)params.get("memory")); long memory = Long.parseLong((String)params.get("memory"));
long localStorageSize = Long.parseLong((String)params.get("localstorage")); long localStorageSize = Long.parseLong((String)params.get("localstorage"));
String arch = (String)params.get("arch");
synchronized (this) { synchronized (this) {
long dataCenterId = Long.parseLong((String)params.get("zone")); long dataCenterId = Long.parseLong((String)params.get("zone"));
long podId = Long.parseLong((String)params.get("pod")); long podId = Long.parseLong((String)params.get("pod"));
@ -170,6 +171,7 @@ public class MockAgentManagerImpl extends ManagerBase implements MockAgentManage
mockHost.setCpuCount(cpuCore); mockHost.setCpuCount(cpuCore);
mockHost.setCpuSpeed(cpuSpeed); mockHost.setCpuSpeed(cpuSpeed);
mockHost.setMemorySize(memory); mockHost.setMemorySize(memory);
mockHost.setArch(arch);
String guid = UUID.randomUUID().toString(); String guid = UUID.randomUUID().toString();
mockHost.setGuid(guid); mockHost.setGuid(guid);
mockHost.setName("SimulatedAgent." + guid); mockHost.setName("SimulatedAgent." + guid);

View File

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

View File

@ -87,6 +87,7 @@ public class SimulatorDiscoverer extends DiscovererBase implements Discoverer, L
long cpuCores = MockAgentManager.DEFAULT_HOST_CPU_CORES; long cpuCores = MockAgentManager.DEFAULT_HOST_CPU_CORES;
long memory = MockAgentManager.DEFAULT_HOST_MEM_SIZE; long memory = MockAgentManager.DEFAULT_HOST_MEM_SIZE;
long localstorageSize = MockStorageManager.DEFAULT_HOST_STORAGE_SIZE; long localstorageSize = MockStorageManager.DEFAULT_HOST_STORAGE_SIZE;
String arch = MockAgentManager.DEFAULT_HOST_ARCH;
if (scheme.equals("http")) { if (scheme.equals("http")) {
if (host == null || !host.startsWith("sim")) { 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; 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]); memory = Long.parseLong(parameter[1]);
} else if (parameter[0].equalsIgnoreCase("localstorage") && parameter[1] != null) { } else if (parameter[0].equalsIgnoreCase("localstorage") && parameter[1] != null) {
localstorageSize = Long.parseLong(parameter[1]); 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("cpucore", Long.toString(cpuCores));
params.put("memory", Long.toString(memory)); params.put("memory", Long.toString(memory));
params.put("localstorage", Long.toString(localstorageSize)); params.put("localstorage", Long.toString(localstorageSize));
params.put("arch", arch);
resources = createAgentResources(params); resources = createAgentResources(params);
return resources; return resources;

View File

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

View File

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

View File

@ -22,7 +22,6 @@ import java.util.List;
import javax.inject.Inject; import javax.inject.Inject;
import com.cloud.cpu.CPU;
import org.apache.cloudstack.api.ApiCommandResourceType; import org.apache.cloudstack.api.ApiCommandResourceType;
import org.apache.cloudstack.api.ApiConstants; import org.apache.cloudstack.api.ApiConstants;
import org.apache.cloudstack.api.command.admin.kubernetes.version.AddKubernetesSupportedVersionCmd; 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.dao.TemplateJoinDao;
import com.cloud.api.query.vo.TemplateJoinVO; import com.cloud.api.query.vo.TemplateJoinVO;
import com.cloud.cpu.CPU;
import com.cloud.dc.DataCenter; import com.cloud.dc.DataCenter;
import com.cloud.dc.DataCenterVO; import com.cloud.dc.DataCenterVO;
import com.cloud.dc.dao.DataCenterDao; 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.ComponentContext;
import com.cloud.utils.component.ManagerBase; import com.cloud.utils.component.ManagerBase;
import com.cloud.utils.db.Filter; import com.cloud.utils.db.Filter;
import com.cloud.utils.db.JoinBuilder;
import com.cloud.utils.db.SearchBuilder; import com.cloud.utils.db.SearchBuilder;
import com.cloud.utils.db.SearchCriteria; import com.cloud.utils.db.SearchCriteria;
import com.cloud.utils.exception.CloudRuntimeException; import com.cloud.utils.exception.CloudRuntimeException;
@ -94,6 +95,7 @@ public class KubernetesVersionManagerImpl extends ManagerBase implements Kuberne
if (template.getState() != null) { if (template.getState() != null) {
response.setIsoState(template.getState().toString()); response.setIsoState(template.getState().toString());
} }
response.setIsoArch(template.getArch().getType());
response.setDirectDownload(template.isDirectDownload()); response.setDirectDownload(template.isDirectDownload());
} }
@ -267,6 +269,7 @@ public class KubernetesVersionManagerImpl extends ManagerBase implements Kuberne
final Long zoneId = cmd.getZoneId(); final Long zoneId = cmd.getZoneId();
String minimumSemanticVersion = cmd.getMinimumSemanticVersion(); String minimumSemanticVersion = cmd.getMinimumSemanticVersion();
final Long minimumKubernetesVersionId = cmd.getMinimumKubernetesVersionId(); final Long minimumKubernetesVersionId = cmd.getMinimumKubernetesVersionId();
final String arch = cmd.getArch();
if (StringUtils.isNotEmpty(minimumSemanticVersion) && minimumKubernetesVersionId != null) { 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)); 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(); SearchBuilder<KubernetesSupportedVersionVO> sb = kubernetesSupportedVersionDao.createSearchBuilder();
sb.and("id", sb.entity().getId(), SearchCriteria.Op.EQ); sb.and("id", sb.entity().getId(), SearchCriteria.Op.EQ);
sb.and("keyword", sb.entity().getName(), SearchCriteria.Op.LIKE); 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(); SearchCriteria<KubernetesSupportedVersionVO> sc = sb.create();
String keyword = cmd.getKeyword(); String keyword = cmd.getKeyword();
if (versionId != null) { if (versionId != null) {
@ -295,6 +305,9 @@ public class KubernetesVersionManagerImpl extends ManagerBase implements Kuberne
if(keyword != null){ if(keyword != null){
sc.setParameters("keyword", "%" + keyword + "%"); sc.setParameters("keyword", "%" + keyword + "%");
} }
if (StringUtils.isNotBlank(arch)) {
sc.setJoinParameters("isoSearch", "arch", arch);
}
Pair<List<KubernetesSupportedVersionVO>, Integer> versionsAndCount = Pair<List<KubernetesSupportedVersionVO>, Integer> versionsAndCount =
kubernetesSupportedVersionDao.searchAndCount(sc, searchFilter); kubernetesSupportedVersionDao.searchAndCount(sc, searchFilter);
List<KubernetesSupportedVersionVO> versions = List<KubernetesSupportedVersionVO> versions =

View File

@ -66,6 +66,11 @@ public class ListKubernetesSupportedVersionsCmd extends BaseListCmd {
description = "the ID of the minimum Kubernetes supported version") description = "the ID of the minimum Kubernetes supported version")
private Long minimumKubernetesVersionId; 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 /////////////////////// /////////////////// Accessors ///////////////////////
///////////////////////////////////////////////////// /////////////////////////////////////////////////////
@ -85,6 +90,10 @@ public class ListKubernetesSupportedVersionsCmd extends BaseListCmd {
return minimumSemanticVersion; return minimumSemanticVersion;
} }
public String getArch() {
return arch;
}
public Long getMinimumKubernetesVersionId() { public Long getMinimumKubernetesVersionId() {
return minimumKubernetesVersionId; 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") @Param(description = "the state of the binaries ISO for Kubernetes supported version")
private String isoState; 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) @SerializedName(ApiConstants.ZONE_ID)
@Param(description = "the id of the zone in which Kubernetes supported version is available") @Param(description = "the id of the zone in which Kubernetes supported version is available")
private String zoneId; private String zoneId;
@ -138,6 +142,14 @@ public class KubernetesSupportedVersionResponse extends BaseResponse {
this.isoState = isoState; this.isoState = isoState;
} }
public String getIsoArch() {
return isoArch;
}
public void setIsoArch(String isoArch) {
this.isoArch = isoArch;
}
public String getZoneId() { public String getZoneId() {
return zoneId; 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.dao.TemplateJoinDao;
import com.cloud.api.query.vo.TemplateJoinVO; import com.cloud.api.query.vo.TemplateJoinVO;
import com.cloud.cpu.CPU;
@RunWith(MockitoJUnitRunner.class) @RunWith(MockitoJUnitRunner.class)
public class KubernetesVersionManagerImplTest { public class KubernetesVersionManagerImplTest {
@ -57,6 +58,7 @@ public class KubernetesVersionManagerImplTest {
Mockito.when(kubernetesSupportedVersion.getIsoId()).thenReturn(1L); Mockito.when(kubernetesSupportedVersion.getIsoId()).thenReturn(1L);
KubernetesSupportedVersionResponse response = new KubernetesSupportedVersionResponse(); KubernetesSupportedVersionResponse response = new KubernetesSupportedVersionResponse();
TemplateJoinVO templateJoinVO = Mockito.mock(TemplateJoinVO.class); TemplateJoinVO templateJoinVO = Mockito.mock(TemplateJoinVO.class);
Mockito.when(templateJoinVO.getArch()).thenReturn(CPU.CPUArch.getDefault());
String uuid = UUID.randomUUID().toString(); String uuid = UUID.randomUUID().toString();
Mockito.when(templateJoinVO.getUuid()).thenReturn(uuid); Mockito.when(templateJoinVO.getUuid()).thenReturn(uuid);
Mockito.when(templateJoinDao.findById(1L)).thenReturn(templateJoinVO); Mockito.when(templateJoinDao.findById(1L)).thenReturn(templateJoinVO);

View File

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

View File

@ -16,6 +16,12 @@
// under the License. // under the License.
package org.apache.cloudstack.network.lb; 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.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.Iterator; 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.framework.config.dao.ConfigurationDao;
import org.apache.cloudstack.lb.ApplicationLoadBalancerRuleVO; import org.apache.cloudstack.lb.ApplicationLoadBalancerRuleVO;
import org.apache.cloudstack.lb.dao.ApplicationLoadBalancerRuleDao; import org.apache.cloudstack.lb.dao.ApplicationLoadBalancerRuleDao;
import org.apache.commons.collections.CollectionUtils;
import com.cloud.agent.AgentManager; import com.cloud.agent.AgentManager;
import com.cloud.agent.api.Answer; 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.DomainRouterDao;
import com.cloud.vm.dao.NicDao; 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 { public class InternalLoadBalancerVMManagerImpl extends ManagerBase implements InternalLoadBalancerVMManager, InternalLoadBalancerVMService, VirtualMachineGuru {
static final private String InternalLbVmNamePrefix = "b"; static final private String InternalLbVmNamePrefix = "b";
@ -732,6 +733,70 @@ public class InternalLoadBalancerVMManagerImpl extends ManagerBase implements In
return internalLbVms; 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, 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, final long svcOffId, final Long vpcId, final LinkedHashMap<Network, List<? extends NicProfile>> networks, final boolean startVm) throws ConcurrentOperationException,
InsufficientAddressCapacityException, InsufficientServerCapacityException, InsufficientCapacityException, StorageUnavailableException, 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 // 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); 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 allocateRetry = 0;
int startRetry = 0; int startRetry = 0;
DomainRouterVO internalLbVm = null; DomainRouterVO internalLbVm = null;
@ -751,45 +824,23 @@ public class InternalLoadBalancerVMManagerImpl extends ManagerBase implements In
try { try {
final long id = _internalLbVmDao.getNextInSequence(Long.class, "id"); final long id = _internalLbVmDao.getNextInSequence(Long.class, "id");
if (logger.isDebugEnabled()) { 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; final long zoneId = dest.getDataCenter().getId();
if (hType.equals(XenServer)) { final String templateName = getRouterTemplateForHypervisor(hType, zoneId);
templateName = VirtualNetworkApplianceManager.RouterTemplateXen.valueIn(dest.getDataCenter().getId()); final String preferredArch = ResourceManager.SystemVmPreferredArchitecture.valueIn(zoneId);
} else if (hType.equals(KVM)) { final List<VMTemplateVO> templates = _templateDao.findRoutingTemplates(hType, templateName,
templateName = VirtualNetworkApplianceManager.RouterTemplateKvm.valueIn(dest.getDataCenter().getId()); preferredArch);
} else if (hType.equals(VMware)) { if (CollectionUtils.isEmpty(templates)) {
templateName = VirtualNetworkApplianceManager.RouterTemplateVmware.valueIn(dest.getDataCenter().getId()); logger.debug("{} won't support system vm, skip it", hType);
} 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");
continue; continue;
} }
internalLbVm = deployInternalLbVmWithTemplates(internalLbVm, id, plan, internalLbProviderId, owner,
long userId = CallContext.current().getCallingUserId(); userId, vpcId, routerOffering, networks, templates);
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());
} catch (final InsufficientCapacityException ex) { } catch (final InsufficientCapacityException ex) {
if (allocateRetry < 2 && iter.hasNext()) { 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; continue;
} else { } else {
throw ex; throw ex;
@ -804,8 +855,7 @@ public class InternalLoadBalancerVMManagerImpl extends ManagerBase implements In
break; break;
} catch (final InsufficientCapacityException ex) { } catch (final InsufficientCapacityException ex) {
if (startRetry < 2 && iter.hasNext()) { if (startRetry < 2 && iter.hasNext()) {
logger.debug("Failed to start the Internal lb vm " + internalLbVm + " with hypervisor type " + hType + ", " + logger.debug("Failed to start the Internal lb vm {} with hypervisor type {}, destroying it and recreating one more time", internalLbVm, hType);
"destroying it and recreating one more time");
// destroy the internal lb vm // destroy the internal lb vm
destroyInternalLbVm(internalLbVm.getId(), _accountMgr.getSystemAccount(), User.UID_SYSTEM); destroyInternalLbVm(internalLbVm.getId(), _accountMgr.getSystemAccount(), User.UID_SYSTEM);
continue; 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) { if (template != null) {
vmResponse.setTemplateId(template.getUuid()); vmResponse.setTemplateId(template.getUuid());
vmResponse.setTemplateName(template.getName()); vmResponse.setTemplateName(template.getName());
vmResponse.setArch(template.getArch().getType());
} }
vmResponse.setCreated(vm.getCreated()); vmResponse.setCreated(vm.getCreated());
vmResponse.setHypervisor(vm.getHypervisorType().getHypervisorDisplayName()); vmResponse.setHypervisor(vm.getHypervisorType().getHypervisorDisplayName());

View File

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

View File

@ -188,6 +188,7 @@ public class UserVmJoinDaoImpl extends GenericDaoBaseWithTagInformation<UserVmJo
userVmResponse.setInstanceName(userVm.getInstanceName()); userVmResponse.setInstanceName(userVm.getInstanceName());
userVmResponse.setHostId(userVm.getHostUuid()); userVmResponse.setHostId(userVm.getHostUuid());
userVmResponse.setHostName(userVm.getHostName()); userVmResponse.setHostName(userVm.getHostName());
userVmResponse.setArch(userVm.getArch());
} }
if (userVm.getHostStatus() != null) { if (userVm.getHostStatus() != null) {
userVmResponse.setHostControlState(ControlState.getControlState(userVm.getHostStatus(), userVm.getHostResourceState()).toString()); 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.Id;
import javax.persistence.Table; import javax.persistence.Table;
import com.cloud.cpu.CPU;
import com.cloud.host.Status; import com.cloud.host.Status;
import com.cloud.hypervisor.Hypervisor; import com.cloud.hypervisor.Hypervisor;
import com.cloud.network.Network.GuestType; import com.cloud.network.Network.GuestType;
@ -38,6 +39,8 @@ import com.cloud.user.Account;
import com.cloud.utils.db.GenericDao; import com.cloud.utils.db.GenericDao;
import com.cloud.vm.VirtualMachine; import com.cloud.vm.VirtualMachine;
import com.cloud.vm.VirtualMachine.State; import com.cloud.vm.VirtualMachine.State;
import org.apache.cloudstack.util.CPUArchConverter;
import org.apache.cloudstack.util.HypervisorTypeConverter; import org.apache.cloudstack.util.HypervisorTypeConverter;
@Entity @Entity
@ -143,6 +146,10 @@ public class DomainRouterJoinVO extends BaseViewVO implements ControlledViewEnti
@Convert(converter = HypervisorTypeConverter.class) @Convert(converter = HypervisorTypeConverter.class)
private Hypervisor.HypervisorType hypervisorType; 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) @Column(name = "template_id", updatable = true, nullable = true, length = 17)
private long templateId; private long templateId;
@ -376,6 +383,10 @@ public class DomainRouterJoinVO extends BaseViewVO implements ControlledViewEnti
return hypervisorType; return hypervisorType;
} }
public CPU.CPUArch getArch() {
return arch;
}
public Long getClusterId() { public Long getClusterId() {
return clusterId; return clusterId;
} }

View File

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

View File

@ -28,8 +28,6 @@ import java.util.Map;
import javax.annotation.PostConstruct; import javax.annotation.PostConstruct;
import javax.inject.Inject; 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.api.ApiConstants;
import org.apache.cloudstack.context.CallContext; import org.apache.cloudstack.context.CallContext;
import org.apache.cloudstack.engine.orchestration.service.NetworkOrchestrationService; 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.network.router.deployment.RouterDeploymentDefinition;
import org.apache.cloudstack.utils.CloudStackVersion; import org.apache.cloudstack.utils.CloudStackVersion;
import org.apache.commons.collections.CollectionUtils; import org.apache.commons.collections.CollectionUtils;
import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import com.cloud.agent.AgentManager; import com.cloud.agent.AgentManager;
import com.cloud.agent.api.Answer; 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.RedundantState;
import com.cloud.network.router.VirtualRouter.Role; import com.cloud.network.router.VirtualRouter.Role;
import com.cloud.network.rules.LbStickinessMethod; import com.cloud.network.rules.LbStickinessMethod;
import com.cloud.network.vpc.dao.VpcDao;
import com.cloud.network.vpn.Site2SiteVpnManager; import com.cloud.network.vpn.Site2SiteVpnManager;
import com.cloud.offering.NetworkOffering; import com.cloud.offering.NetworkOffering;
import com.cloud.resource.ResourceManager; import com.cloud.resource.ResourceManager;
@ -104,6 +103,7 @@ import com.cloud.user.dao.UserDao;
import com.cloud.utils.Pair; import com.cloud.utils.Pair;
import com.cloud.utils.exception.CloudRuntimeException; import com.cloud.utils.exception.CloudRuntimeException;
import com.cloud.utils.net.NetUtils; import com.cloud.utils.net.NetUtils;
import com.cloud.utils.validation.ChecksumUtil;
import com.cloud.vm.DomainRouterVO; import com.cloud.vm.DomainRouterVO;
import com.cloud.vm.Nic; import com.cloud.vm.Nic;
import com.cloud.vm.NicProfile; import com.cloud.vm.NicProfile;
@ -498,18 +498,77 @@ public class NetworkHelperImpl implements NetworkHelper {
return templateName; 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 @Override
public DomainRouterVO deployRouter(final RouterDeploymentDefinition routerDeploymentDefinition, final boolean startRouter) public DomainRouterVO deployRouter(final RouterDeploymentDefinition routerDeploymentDefinition, final boolean startRouter)
throws InsufficientAddressCapacityException, InsufficientServerCapacityException, InsufficientCapacityException, StorageUnavailableException, ResourceUnavailableException { throws InsufficientAddressCapacityException, InsufficientServerCapacityException, InsufficientCapacityException, StorageUnavailableException, ResourceUnavailableException {
final ServiceOfferingVO routerOffering = _serviceOfferingDao.findById(routerDeploymentDefinition.getServiceOfferingId()); final ServiceOfferingVO routerOffering = _serviceOfferingDao.findById(routerDeploymentDefinition.getServiceOfferingId());
final boolean offerHA = routerOffering.isOfferHA();
final Account owner = routerDeploymentDefinition.getOwner(); 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. // Router is the network element, we don't know the hypervisor type yet.
// Try to allocate the domR twice using diff hypervisors, and when // Try to allocate the domR twice using diff hypervisors, and when
// failed both times, throw the exception up // failed both times, throw the exception up
final List<HypervisorType> hypervisors = getHypervisors(routerDeploymentDefinition); 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; DomainRouterVO router = null;
for (final Iterator<HypervisorType> iter = hypervisors.iterator(); iter.hasNext();) { for (final Iterator<HypervisorType> iter = hypervisors.iterator(); iter.hasNext();) {
final HypervisorType hType = iter.next(); 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() logger.debug(String.format("Allocating the VR with id=%s in datacenter %s with the hypervisor type %s", id, routerDeploymentDefinition.getDest()
.getDataCenter(), hType)); .getDataCenter(), hType));
} }
final long zoneId = routerDeploymentDefinition.getDest().getDataCenter().getId();
final String templateName = retrieveTemplateName(hType, routerDeploymentDefinition.getDest().getDataCenter().getId()); final String templateName = retrieveTemplateName(hType, zoneId);
final VMTemplateVO template = _templateDao.findRoutingTemplate(hType, templateName); final String preferredArch = ResourceManager.SystemVmPreferredArchitecture.valueIn(zoneId);
final List<VMTemplateVO> templates = _templateDao.findRoutingTemplates(hType, templateName,
if (template == null) { preferredArch);
logger.debug(hType + " won't support system vm, skip it"); if (CollectionUtils.isEmpty(templates)) {
logger.debug("{} won't support system vm, skip it", hType);
continue; continue;
} }
router = deployRouterWithTemplates(router, id, routerDeploymentDefinition, owner, userId,
final boolean offerHA = routerOffering.isOfferHA(); routerOffering, offerHA, vpcId, templates);
// 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());
} catch (final InsufficientCapacityException ex) { } catch (final InsufficientCapacityException ex) {
if (iter.hasNext()) { 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; continue;
} else { } else {
throw ex; throw ex;

View File

@ -1201,6 +1201,12 @@ public class ResourceManagerImpl extends ManagerBase implements ResourceManager,
} }
if (arch != null) { 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()); cluster.setArch(arch.getType());
doUpdate = true; doUpdate = true;
} }
@ -3537,6 +3543,10 @@ public class ResourceManagerImpl extends ManagerBase implements ResourceManager,
@Override @Override
public ConfigKey<?>[] getConfigKeys() { 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.inject.Inject;
import javax.naming.ConfigurationException; import javax.naming.ConfigurationException;
import com.cloud.cpu.CPU;
import org.apache.cloudstack.acl.ControlledEntity; import org.apache.cloudstack.acl.ControlledEntity;
import org.apache.cloudstack.acl.SecurityChecker; import org.apache.cloudstack.acl.SecurityChecker;
import org.apache.cloudstack.affinity.AffinityGroupProcessor; import org.apache.cloudstack.affinity.AffinityGroupProcessor;
@ -674,6 +673,7 @@ import com.cloud.configuration.Config;
import com.cloud.configuration.ConfigurationManagerImpl; import com.cloud.configuration.ConfigurationManagerImpl;
import com.cloud.consoleproxy.ConsoleProxyManagementState; import com.cloud.consoleproxy.ConsoleProxyManagementState;
import com.cloud.consoleproxy.ConsoleProxyManager; import com.cloud.consoleproxy.ConsoleProxyManager;
import com.cloud.cpu.CPU;
import com.cloud.dc.AccountVlanMapVO; import com.cloud.dc.AccountVlanMapVO;
import com.cloud.dc.ClusterVO; import com.cloud.dc.ClusterVO;
import com.cloud.dc.DataCenterVO; import com.cloud.dc.DataCenterVO;
@ -1274,6 +1274,7 @@ public class ManagementServerImpl extends ManagerBase implements ManagementServe
final Object clusterType = cmd.getClusterType(); final Object clusterType = cmd.getClusterType();
final Object allocationState = cmd.getAllocationState(); final Object allocationState = cmd.getAllocationState();
final String keyword = cmd.getKeyword(); final String keyword = cmd.getKeyword();
final CPU.CPUArch arch = cmd.getArch();
zoneId = _accountMgr.checkAccessAndSpecifyAuthority(CallContext.current().getCallingAccount(), zoneId); zoneId = _accountMgr.checkAccessAndSpecifyAuthority(CallContext.current().getCallingAccount(), zoneId);
final Filter searchFilter = new Filter(ClusterVO.class, "id", true, cmd.getStartIndex(), cmd.getPageSizeVal()); 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("hypervisorType", sb.entity().getHypervisorType(), SearchCriteria.Op.EQ);
sb.and("clusterType", sb.entity().getClusterType(), SearchCriteria.Op.EQ); sb.and("clusterType", sb.entity().getClusterType(), SearchCriteria.Op.EQ);
sb.and("allocationState", sb.entity().getAllocationState(), 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(); final SearchCriteria<ClusterVO> sc = sb.create();
if (id != null) { if (id != null) {
@ -1325,6 +1327,10 @@ public class ManagementServerImpl extends ManagerBase implements ManagementServe
sc.addAnd("name", SearchCriteria.Op.SC, ssc); sc.addAnd("name", SearchCriteria.Op.SC, ssc);
} }
if (arch != null) {
sc.setParameters("arch", arch);
}
final Pair<List<ClusterVO>, Integer> result = _clusterDao.searchAndCount(sc, searchFilter); final Pair<List<ClusterVO>, Integer> result = _clusterDao.searchAndCount(sc, searchFilter);
return new Pair<>(result.first(), result.second()); 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 podId = cmd.getPodId();
final Long hostId = cmd.getHostId(); final Long hostId = cmd.getHostId();
final Long storageId = cmd.getStorageId(); 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 Filter searchFilter = new Filter(VMInstanceVO.class, "id", true, cmd.getStartIndex(), cmd.getPageSizeVal());
final SearchBuilder<VMInstanceVO> sb = _vmInstanceDao.createSearchBuilder(); 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(); final SearchCriteria<VMInstanceVO> sc = sb.create();
if (keyword != null) { 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); final Pair<List<VMInstanceVO>, Integer> result = _vmInstanceDao.searchAndCount(sc, searchFilter);
return new Pair<>(result.first(), result.second()); 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.ConfigurationManager;
import com.cloud.configuration.ConfigurationManagerImpl; import com.cloud.configuration.ConfigurationManagerImpl;
import com.cloud.configuration.Resource.ResourceType; import com.cloud.configuration.Resource.ResourceType;
import com.cloud.cpu.CPU;
import com.cloud.dc.ClusterVO; import com.cloud.dc.ClusterVO;
import com.cloud.dc.DataCenterVO; import com.cloud.dc.DataCenterVO;
import com.cloud.dc.VsphereStoragePolicyVO; 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); 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) { private void registerSystemVmTemplateOnFirstNfsStore(Long zoneId, String providerName, String url, DataStore store) {
if (DataStoreProvider.NFS_IMAGE.equals(providerName) && zoneId != null) { if (DataStoreProvider.NFS_IMAGE.equals(providerName) && zoneId != null) {
Transaction.execute(new TransactionCallbackNoReturn() { Transaction.execute(new TransactionCallbackNoReturn() {
@Override @Override
public void doInTransactionWithoutResult(final TransactionStatus status) { public void doInTransactionWithoutResult(final TransactionStatus status) {
List<ImageStoreVO> stores = _imageStoreDao.listAllStoresInZone(zoneId, providerName, DataStoreRole.Image); List<ImageStoreVO> stores = _imageStoreDao.listAllStoresInZoneExceptId(zoneId, providerName,
stores = stores.stream().filter(str -> str.getId() != store.getId()).collect(Collectors.toList()); DataStoreRole.Image, store.getId());
// Check if it's the only/first store in the zone if (CollectionUtils.isEmpty(stores)) {
if (stores.size() == 0) { List<Pair<HypervisorType, CPU.CPUArch>> hypervisorTypes =
List<HypervisorType> hypervisorTypes = _clusterDao.getAvailableHypervisorInZone(zoneId); _clusterDao.listDistinctHypervisorsArchAcrossClusters(zoneId);
Set<HypervisorType> hypSet = new HashSet<>(hypervisorTypes);
TransactionLegacy txn = TransactionLegacy.open("AutomaticTemplateRegister"); TransactionLegacy txn = TransactionLegacy.open("AutomaticTemplateRegister");
SystemVmTemplateRegistration systemVmTemplateRegistration = new SystemVmTemplateRegistration(); SystemVmTemplateRegistration systemVmTemplateRegistration = new SystemVmTemplateRegistration();
String filePath = null; String filePath = null;
@ -3606,40 +3639,15 @@ public class StorageManagerImpl extends ManagerBase implements StorageManager, C
} }
Pair<String, Long> storeUrlAndId = new Pair<>(url, store.getId()); Pair<String, Long> storeUrlAndId = new Pair<>(url, store.getId());
String nfsVersion = imageStoreDetailsUtil.getNfsVersion(store.getId()); String nfsVersion = imageStoreDetailsUtil.getNfsVersion(store.getId());
for (HypervisorType hypervisorType : hypSet) { for (Pair<HypervisorType, CPU.CPUArch> hypervisorArchType : hypervisorTypes) {
try { try {
if (HypervisorType.Simulator == hypervisorType) { registerSystemVmTemplateForHypervisorArch(hypervisorArchType.first(),
continue; hypervisorArchType.second(), zoneId, url, store,
} systemVmTemplateRegistration, filePath, storeUrlAndId, nfsVersion);
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);
}
} catch (CloudRuntimeException e) { } catch (CloudRuntimeException e) {
SystemVmTemplateRegistration.unmountStore(filePath); 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) { } catch (Exception e) {

View File

@ -21,6 +21,7 @@ import java.util.Arrays;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Objects;
import javax.inject.Inject; import javax.inject.Inject;
@ -243,7 +244,11 @@ public abstract class TemplateAdapterBase extends AdapterBase implements Templat
List<VMTemplateVO> systemvmTmplts = _tmpltDao.listAllSystemVMTemplates(); List<VMTemplateVO> systemvmTmplts = _tmpltDao.listAllSystemVMTemplates();
for (VMTemplateVO template : systemvmTmplts) { 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"); 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. // under the License.
package com.cloud.network.router; package com.cloud.network.router;
import com.cloud.agent.AgentManager; import static org.junit.Assert.assertEquals;
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.assertFalse; import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue; import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.lenient; import static org.mockito.Mockito.lenient;
import static org.mockito.Mockito.mock; 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.verify;
import static org.mockito.Mockito.when; 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) @RunWith(MockitoJUnitRunner.class)
public class NetworkHelperImplTest { public class NetworkHelperImplTest {
@ -54,6 +66,9 @@ public class NetworkHelperImplTest {
@Mock @Mock
protected AgentManager agentManager; protected AgentManager agentManager;
@Mock
DomainRouterDao routerDao;
@InjectMocks @InjectMocks
protected NetworkHelperImpl nwHelper = new NetworkHelperImpl(); protected NetworkHelperImpl nwHelper = new NetworkHelperImpl();
@Mock @Mock
@ -65,10 +80,27 @@ public class NetworkHelperImplTest {
@Mock @Mock
NicDao nicDao; NicDao nicDao;
@Mock
private RouterDeploymentDefinition routerDeploymentDefinition;
@Mock
private VirtualRouterProvider virtualProvider;
@Mock
private Account owner;
@Mock
private ServiceOfferingVO routerOffering;
@Mock
private VMTemplateVO template;
@Before @Before
public void setUp() { public void setUp() {
nwHelper._networkDao = networkDao; nwHelper._networkDao = networkDao;
nwHelper._networkModel = networkModel; 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) @Test(expected=ResourceUnavailableException.class)
@ -83,7 +115,7 @@ public class NetworkHelperImplTest {
nwHelperUT.sendCommandsToRouter(vr, null); nwHelperUT.sendCommandsToRouter(vr, null);
// Assert // Assert
verify(this.agentManager, times(0)).send((Long) ArgumentMatchers.any(), (Command) ArgumentMatchers.any()); verify(this.agentManager, times(0)).send((Long) any(), (Command) any());
} }
@Test @Test
@ -187,4 +219,53 @@ public class NetworkHelperImplTest {
verify(answer1, times(0)).getResult(); verify(answer1, times(0)).getResult();
assertFalse(result); 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.Collections;
import java.util.Date; import java.util.Date;
import java.util.HashMap; import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashMap; import java.util.LinkedHashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
@ -618,6 +619,25 @@ public class SecondaryStorageManagerImpl extends ManagerBase implements Secondar
return defaultNetworks.get(0); 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) { protected Map<String, Object> createSecStorageVmInstance(long dataCenterId, SecondaryStorageVm.Role role) {
DataStore secStore = _dataStoreMgr.getImageStoreWithFreeCapacity(dataCenterId); DataStore secStore = _dataStoreMgr.getImageStoreWithFreeCapacity(dataCenterId);
if (secStore == null) { if (secStore == null) {
@ -657,28 +677,33 @@ public class SecondaryStorageManagerImpl extends ManagerBase implements Secondar
} }
HypervisorType availableHypervisor = _resourceMgr.getAvailableHypervisor(dataCenterId); HypervisorType availableHypervisor = _resourceMgr.getAvailableHypervisor(dataCenterId);
VMTemplateVO template = _templateDao.findSystemVMReadyTemplate(dataCenterId, availableHypervisor); List<VMTemplateVO> templates = _templateDao.findSystemVMReadyTemplates(dataCenterId, availableHypervisor,
if (template == null) { ResourceManager.SystemVmPreferredArchitecture.valueIn(dataCenterId));
throw new CloudRuntimeException(String.format("Unable to find the system templates or it was not downloaded in %s.", dc.toString())); 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; ServiceOfferingVO serviceOffering = _serviceOffering;
if (serviceOffering == null) { if (serviceOffering == null) {
serviceOffering = _offeringDao.findDefaultSystemOffering(ServiceOffering.ssvmDefaultOffUniqueName, ConfigurationManagerImpl.SystemVMUseLocalStorage.valueIn(dataCenterId)); serviceOffering = _offeringDao.findDefaultSystemOffering(ServiceOffering.ssvmDefaultOffUniqueName, ConfigurationManagerImpl.SystemVMUseLocalStorage.valueIn(dataCenterId));
} }
SecondaryStorageVmVO secStorageVm = SecondaryStorageVmVO secStorageVm = null;
new SecondaryStorageVmVO(id, serviceOffering.getId(), name, template.getId(), template.getHypervisorType(), template.getGuestOSId(), dataCenterId, for (final Iterator<VMTemplateVO> templateIterator = templates.iterator(); templateIterator.hasNext();) {
systemAcct.getDomainId(), systemAcct.getId(), _accountMgr.getSystemUser().getId(), role, serviceOffering.isOfferHA()); VMTemplateVO template = templateIterator.next();
secStorageVm.setDynamicallyScalable(template.isDynamicallyScalable()); secStorageVm = createOrUpdateSecondaryStorageVm(secStorageVm, dataCenterId, id, name, serviceOffering,
secStorageVm.setLimitCpuUse(serviceOffering.getLimitCpuUse()); template, systemAcct, role);
secStorageVm = _secStorageVmDao.persist(secStorageVm); try {
try { _itMgr.allocate(name, template, serviceOffering, networks, plan, template.getHypervisorType());
_itMgr.allocate(name, template, serviceOffering, networks, plan, null); secStorageVm = _secStorageVmDao.findById(secStorageVm.getId());
secStorageVm = _secStorageVmDao.findById(secStorageVm.getId()); _itMgr.checkDeploymentPlan(secStorageVm, template, serviceOffering, systemAcct, plan);
} catch (InsufficientCapacityException e) { break;
String errorMessage = String.format("Unable to allocate secondary storage VM [%s] due to [%s].", name, e.getMessage()); } catch (InsufficientCapacityException e) {
logger.warn(errorMessage, e); if (templateIterator.hasNext()) {
throw new CloudRuntimeException(errorMessage, e); 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<>(); Map<String, Object> context = new HashMap<>();
@ -820,8 +845,9 @@ public class SecondaryStorageManagerImpl extends ManagerBase implements Secondar
} }
ZoneHostInfo zoneHostInfo = zoneHostInfoMap.get(dataCenterId); ZoneHostInfo zoneHostInfo = zoneHostInfoMap.get(dataCenterId);
if (zoneHostInfo != null && (zoneHostInfo.getFlags() & RunningHostInfoAgregator.ZoneHostInfo.ROUTING_HOST_MASK) != 0) { if (zoneHostInfo != null && (zoneHostInfo.getFlags() & RunningHostInfoAgregator.ZoneHostInfo.ROUTING_HOST_MASK) != 0) {
VMTemplateVO template = _templateDao.findSystemVMReadyTemplate(dataCenterId, HypervisorType.Any); List<VMTemplateVO> templates = _templateDao.findSystemVMReadyTemplates(dataCenterId,
if (template == null) { HypervisorType.Any, null);
if (CollectionUtils.isEmpty(templates)) {
if (logger.isDebugEnabled()) { 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)); 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; 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 useLocalStorage = BooleanUtils.toBoolean(ConfigurationManagerImpl.SystemVMUseLocalStorage.valueIn(dataCenterId));
boolean hasStoragePoolHostInfo = _storagePoolHostDao.hasDatacenterStoragePoolHostInfo(dataCenterId, !useLocalStorage); boolean hasStoragePoolHostInfo = _storagePoolHostDao.hasDatacenterStoragePoolHostInfo(dataCenterId, !useLocalStorage);
if (hasStoragePoolHostInfo) { if (hasStoragePoolHostInfo) {

View File

@ -16,39 +16,69 @@
// under the License. // under the License.
package org.apache.cloudstack.secondarystorage; 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.security.SecureRandom;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import org.apache.cloudstack.engine.subsystem.api.storage.DataStore; import org.apache.cloudstack.engine.subsystem.api.storage.DataStore;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.junit.Assert;
import org.junit.Test; import org.junit.Test;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
import org.mockito.InjectMocks; import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.Mockito; import org.mockito.Mockito;
import org.mockito.Spy; import org.mockito.Spy;
import org.mockito.junit.MockitoJUnitRunner; import org.mockito.junit.MockitoJUnitRunner;
import com.cloud.agent.api.to.DataStoreTO; 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.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; import com.google.common.net.InetAddresses;
@RunWith(MockitoJUnitRunner.class) @RunWith(MockitoJUnitRunner.class)
public class SecondaryStorageManagerImplTest { public class SecondaryStorageManagerImplTest {
private final SecureRandom secureRandom = new SecureRandom(); private final SecureRandom secureRandom = new SecureRandom();
@Mock
private SecondaryStorageVmDao secStorageVmDao;
@Mock
private AccountManager accountManager;
@Spy @Spy
@InjectMocks @InjectMocks
private SecondaryStorageManagerImpl secondaryStorageManager; 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) { private List<DataStore> mockDataStoresForTestAddSecondaryStorageServerAddressToBuffer(List<String> addresses) {
List<DataStore> dataStores = new ArrayList<>(); List<DataStore> dataStores = new ArrayList<>();
for (String address: addresses) { for (String address: addresses) {
DataStore dataStore = Mockito.mock(DataStore.class); DataStore dataStore = Mockito.mock(DataStore.class);
DataStoreTO dataStoreTO = Mockito.mock(DataStoreTO.class); DataStoreTO dataStoreTO = Mockito.mock(DataStoreTO.class);
Mockito.when(dataStoreTO.getUrl()).thenReturn(NetUtils.isValidIp4(address) ? String.format("http://%s", address) : address); when(dataStoreTO.getUrl()).thenReturn(NetUtils.isValidIp4(address) ? String.format("http://%s", address) : address);
Mockito.when(dataStore.getTO()).thenReturn(dataStoreTO); when(dataStore.getTO()).thenReturn(dataStoreTO);
dataStores.add(dataStore); dataStores.add(dataStore);
} }
return dataStores; return dataStores;
@ -60,7 +90,7 @@ public class SecondaryStorageManagerImplTest {
secondaryStorageManager.addSecondaryStorageServerAddressToBuffer(builder, dataStores, "VM"); secondaryStorageManager.addSecondaryStorageServerAddressToBuffer(builder, dataStores, "VM");
String result = builder.toString(); String result = builder.toString();
result = result.contains("=") ? result.split("=")[1] : null; result = result.contains("=") ? result.split("=")[1] : null;
Assert.assertEquals(expected, result); assertEquals(expected, result);
} }
@Test @Test
@ -86,4 +116,55 @@ public class SecondaryStorageManagerImplTest {
List<String> addresses = List.of(randomIp1, "garbage", randomIp2); List<String> addresses = List.of(randomIp1, "garbage", randomIp2);
runAddSecondaryStorageServerAddressToBufferTest(addresses, StringUtils.join(List.of(randomIp1, 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, `cpus` int(10) unsigned,
`speed` int(10) unsigned, `speed` int(10) unsigned,
`ram` bigint 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', `capabilities` varchar(255) COMMENT 'host capabilities in comma separated list',
`vm_id` bigint unsigned, `vm_id` bigint unsigned,
`resource` varchar(255) DEFAULT NULL COMMENT 'If it is a local resource, this is the class name', `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") raise RuntimeError("python setuptools is required to build Marvin")
VERSION = "4.20.1.0-SNAPSHOT" VERSION = "4.20.1.0"
setup(name="Marvin", setup(name="Marvin",
version=VERSION, version=VERSION,

View File

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

View File

@ -310,7 +310,9 @@ export default {
} }
if (['zoneid', 'domainid', 'imagestoreid', 'storageid', 'state', 'account', 'hypervisor', 'level', if (['zoneid', 'domainid', 'imagestoreid', 'storageid', 'state', 'account', 'hypervisor', 'level',
'clusterid', 'podid', 'groupid', 'entitytype', 'accounttype', 'systemvmtype', 'scope', 'provider', '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' type = 'list'
} else if (item === 'tags') { } else if (item === 'tags') {
@ -445,6 +447,13 @@ export default {
] ]
this.fields[apiKeyAccessIndex].loading = false 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) { async fetchDynamicFieldData (arrayField, searchKeyword) {
const promises = [] const promises = []

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -36,7 +36,8 @@ import {
fileSizeUtilPlugin, fileSizeUtilPlugin,
genericUtilPlugin, genericUtilPlugin,
localesPlugin, localesPlugin,
dialogUtilPlugin dialogUtilPlugin,
cpuArchitectureUtilPlugin
} from './utils/plugins' } from './utils/plugins'
import { VueAxios } from './utils/request' import { VueAxios } from './utils/request'
import directives from './utils/directives' import directives from './utils/directives'
@ -53,6 +54,7 @@ vueApp.use(fileSizeUtilPlugin)
vueApp.use(localesPlugin) vueApp.use(localesPlugin)
vueApp.use(genericUtilPlugin) vueApp.use(genericUtilPlugin)
vueApp.use(dialogUtilPlugin) vueApp.use(dialogUtilPlugin)
vueApp.use(cpuArchitectureUtilPlugin)
vueApp.use(extensions) vueApp.use(extensions)
vueApp.use(directives) 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) { if (this.clusterId === null) {
this.form.clusterid = undefined this.form.clusterid = undefined
} }
this.fetchOptions(this.params.hosts, 'hosts') 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) { onSelectHostId (value) {
this.hostId = value this.hostId = value
if (this.hostId === null) { if (this.hostId === null) {
this.form.hostid = undefined 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) { handleSearchFilter (name, options) {
this.params[name].options = { ...this.params[name].options, ...options } this.params[name].options = { ...this.params[name].options, ...options }

View File

@ -216,7 +216,7 @@ export default {
}) })
}, },
fetchData () { fetchData () {
this.fetchArchitectureTypes() this.architectureTypes.opts = this.$fetchCpuArchitectureTypes()
this.fetchZoneData() this.fetchZoneData()
}, },
isValidValueForKey (obj, key) { isValidValueForKey (obj, key) {
@ -225,19 +225,6 @@ export default {
arrayHasItems (array) { arrayHasItems (array) {
return array !== null && array !== undefined && Array.isArray(array) && array.length > 0 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 () { fetchZoneData () {
const params = {} const params = {}
params.showicon = true params.showicon = true

View File

@ -384,7 +384,7 @@ export default {
fetchData () { fetchData () {
this.fetchZoneData() this.fetchZoneData()
this.fetchOsType() this.fetchOsType()
this.fetchArchitectureTypes() this.architectureTypes.opts = this.$fetchCpuArchitectureTypes()
this.fetchUserData() this.fetchUserData()
this.fetchUserdataPolicy() this.fetchUserdataPolicy()
if ('listDomains' in this.$store.getters.apis) { if ('listDomains' in this.$store.getters.apis) {
@ -410,19 +410,6 @@ export default {
this.form.zoneid = (this.zones[0].id ? this.zones[0].id : '') 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 () { fetchOsType () {
this.osTypeLoading = true this.osTypeLoading = true

View File

@ -583,7 +583,7 @@ export default {
this.fetchZone() this.fetchZone()
this.fetchOsTypes() this.fetchOsTypes()
this.fetchTemplateTypes() this.fetchTemplateTypes()
this.fetchArchitectureTypes() this.architectureTypes.opts = this.$fetchCpuArchitectureTypes()
this.fetchUserData() this.fetchUserData()
this.fetchUserdataPolicy() this.fetchUserdataPolicy()
if ('listDomains' in this.$store.getters.apis) { if ('listDomains' in this.$store.getters.apis) {
@ -753,19 +753,6 @@ export default {
} }
this.templateTypes.opts = templatetypes 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 () { fetchUserData () {
const params = {} const params = {}
params.listAll = true params.listAll = true

View File

@ -216,7 +216,7 @@ export default {
}, },
fetchData () { fetchData () {
this.fetchOsTypes() this.fetchOsTypes()
this.fetchArchitectureTypes() this.architectureTypes.opts = this.$fetchCpuArchitectureTypes()
this.fetchUserdata() this.fetchUserdata()
this.fetchUserdataPolicy() this.fetchUserdataPolicy()
}, },
@ -235,19 +235,6 @@ export default {
this.osTypes.loading = false 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 () { fetchUserdataPolicy () {
const userdataPolicy = [] const userdataPolicy = []
userdataPolicy.push({ userdataPolicy.push({

View File

@ -310,7 +310,7 @@ export default {
}, },
fetchData () { fetchData () {
this.fetchOsTypes() this.fetchOsTypes()
this.fetchArchitectureTypes() this.architectureTypes.opts = this.$fetchCpuArchitectureTypes()
this.fetchRootDiskControllerTypes(this.resource.hypervisor) this.fetchRootDiskControllerTypes(this.resource.hypervisor)
this.fetchNicAdapterTypes() this.fetchNicAdapterTypes()
this.fetchKeyboardTypes() this.fetchKeyboardTypes()
@ -335,19 +335,6 @@ export default {
this.osTypes.loading = false 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) { fetchRootDiskControllerTypes (hyperVisor) {
const controller = [] const controller = []
this.rootDisk.opts = [] this.rootDisk.opts = []

View File

@ -213,7 +213,8 @@ export default {
fetchData () { fetchData () {
this.fetchZones() this.fetchZones()
this.fetchHypervisors() 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 this.params = this.$store.getters.apis.addCluster.params
Object.keys(this.placeholder).forEach(item => { this.returnPlaceholder(item) }) Object.keys(this.placeholder).forEach(item => { this.returnPlaceholder(item) })
}, },
@ -240,20 +241,6 @@ export default {
this.loading = false 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 () { fetchPods () {
this.loading = true this.loading = true
api('listPods', { api('listPods', {

View File

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

View File

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

View File

@ -1280,7 +1280,7 @@ export default {
params.clustertype = clusterType params.clustertype = clusterType
params.podId = this.stepData.podReturned.id params.podId = this.stepData.podReturned.id
let clusterName = this.prefillContent?.clusterName || null let clusterName = this.prefillContent?.clusterName || null
if (this.isEdgeZone) { if (!clusterName && this.isEdgeZone) {
clusterName = 'Cluster-' + this.stepData.zoneReturned.name clusterName = 'Cluster-' + this.stepData.zoneReturned.name
} }
params.arch = this.prefillContent?.arch || null 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.Cookie;
import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession; import javax.servlet.http.HttpSession;
import java.io.FileOutputStream;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.Map; import java.util.Map;
public class HttpUtils { 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; package com.cloud.utils;
import org.junit.Test; import static org.junit.Assert.assertArrayEquals;
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.assertFalse; import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull; import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue; 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 { 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 @Test
public void findCookieTest() { public void findCookieTest() {
Cookie[] cookies = null; 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.ParameterOnly));
assertFalse(HttpUtils.validateSessionKey(session, params, cookies, sessionKeyString, HttpUtils.ApiSessionKeyCheckOption.CookieAndParameter)); 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));
}
} }