Merge remote-tracking branch 'apache/4.20'

This commit is contained in:
Wei Zhou 2025-09-11 14:04:52 +02:00
commit 70a4503ea1
No known key found for this signature in database
GPG Key ID: 1503DFE7C8226103
11 changed files with 119 additions and 19 deletions

View File

@ -31,20 +31,22 @@ import java.util.stream.Collectors;
import static com.cloud.hypervisor.Hypervisor.HypervisorType.Functionality.DirectDownloadTemplate;
import static com.cloud.hypervisor.Hypervisor.HypervisorType.Functionality.RootDiskSizeOverride;
import static com.cloud.hypervisor.Hypervisor.HypervisorType.Functionality.VmStorageMigration;
import static com.cloud.hypervisor.Hypervisor.HypervisorType.Functionality.VmStorageMigrationWithSnapshots;
public class Hypervisor {
public static class HypervisorType {
public enum Functionality {
DirectDownloadTemplate,
RootDiskSizeOverride,
VmStorageMigration
VmStorageMigration,
VmStorageMigrationWithSnapshots
}
private static final Map<String, HypervisorType> hypervisorTypeMap = new LinkedHashMap<>();
public static final HypervisorType None = new HypervisorType("None"); //for storage hosts
public static final HypervisorType XenServer = new HypervisorType("XenServer", ImageFormat.VHD, EnumSet.of(RootDiskSizeOverride, VmStorageMigration));
public static final HypervisorType KVM = new HypervisorType("KVM", ImageFormat.QCOW2, EnumSet.of(DirectDownloadTemplate, RootDiskSizeOverride, VmStorageMigration));
public static final HypervisorType VMware = new HypervisorType("VMware", ImageFormat.OVA, EnumSet.of(RootDiskSizeOverride, VmStorageMigration));
public static final HypervisorType VMware = new HypervisorType("VMware", ImageFormat.OVA, EnumSet.of(RootDiskSizeOverride, VmStorageMigration, VmStorageMigrationWithSnapshots));
public static final HypervisorType Hyperv = new HypervisorType("Hyperv");
public static final HypervisorType VirtualBox = new HypervisorType("VirtualBox");
public static final HypervisorType Parralels = new HypervisorType("Parralels");

View File

@ -60,6 +60,7 @@ public interface KubernetesCluster extends ControlledEntity, com.cloud.utils.fsm
Stopping("Resources for the Kubernetes cluster are being destroyed"),
Stopped("All resources for the Kubernetes cluster are destroyed, Kubernetes cluster may still have ephemeral resource like persistent volumes provisioned"),
Scaling("Transient state in which resources are either getting scaled up/down"),
ScalingStoppedCluster("Transient state in which the service offerings of stopped clusters are getting scaled"),
Upgrading("Transient state in which cluster is getting upgraded"),
Importing("Transient state in which additional nodes are added as worker nodes to a cluster"),
RemovingNodes("Transient state in which additional nodes are removed from a cluster"),
@ -93,8 +94,11 @@ public interface KubernetesCluster extends ControlledEntity, com.cloud.utils.fsm
s_fsm.addTransition(State.Running, Event.AutoscaleRequested, State.Scaling);
s_fsm.addTransition(State.Running, Event.ScaleUpRequested, State.Scaling);
s_fsm.addTransition(State.Running, Event.ScaleDownRequested, State.Scaling);
s_fsm.addTransition(State.Stopped, Event.ScaleUpRequested, State.ScalingStoppedCluster);
s_fsm.addTransition(State.Scaling, Event.OperationSucceeded, State.Running);
s_fsm.addTransition(State.Scaling, Event.OperationFailed, State.Alert);
s_fsm.addTransition(State.ScalingStoppedCluster, Event.OperationSucceeded, State.Stopped);
s_fsm.addTransition(State.ScalingStoppedCluster, Event.OperationFailed, State.Alert);
s_fsm.addTransition(State.Running, Event.UpgradeRequested, State.Upgrading);
s_fsm.addTransition(State.Upgrading, Event.OperationSucceeded, State.Running);

View File

@ -809,7 +809,7 @@ public class AgentManagerImpl extends ManagerBase implements AgentManager, Handl
if (ObjectUtils.anyNotNull(uefiEnabled, virtv2vVersion, ovftoolVersion)) {
_hostDao.loadDetails(host);
boolean updateNeeded = false;
if (!uefiEnabled.equals(host.getDetails().get(Host.HOST_UEFI_ENABLE))) {
if (StringUtils.isNotBlank(uefiEnabled) && !uefiEnabled.equals(host.getDetails().get(Host.HOST_UEFI_ENABLE))) {
host.getDetails().put(Host.HOST_UEFI_ENABLE, uefiEnabled);
updateNeeded = true;
}

View File

@ -1902,6 +1902,20 @@ public class LibvirtComputingResource extends ServerResourceBase implements Serv
return "";
}
public Integer getVlanIdForBridge(final String bridge) {
String pif = matchPifFileInDirectory(bridge);
final File vlanfile = new File("/proc/net/vlan/" + pif);
if (vlanfile.isFile()) {
String vlan = Script.runSimpleBashScript("awk '/VID:/ {print $3}' /proc/net/vlan/" + pif);
try {
return Integer.parseInt(vlan);
} catch (final NumberFormatException e) {
return null;
}
}
return null;
}
static String [] ifNamePatterns = {
"^eth",
"^bond",

View File

@ -132,7 +132,7 @@ public final class LibvirtGetUnmanagedInstancesCommandWrapper extends CommandWra
}
instance.setPowerState(getPowerState(libvirtComputingResource.getVmState(conn,domain.getName())));
instance.setMemory((int) LibvirtComputingResource.getDomainMemory(domain) / 1024);
instance.setNics(getUnmanagedInstanceNics(parser.getInterfaces()));
instance.setNics(getUnmanagedInstanceNics(libvirtComputingResource, parser.getInterfaces()));
instance.setDisks(getUnmanagedInstanceDisks(parser.getDisks(),libvirtComputingResource, conn, domain.getName()));
instance.setVncPassword(getFormattedVncPassword(parser.getVncPasswd()));
if (parser.getBootType() != null) {
@ -169,7 +169,7 @@ public final class LibvirtGetUnmanagedInstancesCommandWrapper extends CommandWra
}
}
private List<UnmanagedInstanceTO.Nic> getUnmanagedInstanceNics(List<LibvirtVMDef.InterfaceDef> interfaces) {
private List<UnmanagedInstanceTO.Nic> getUnmanagedInstanceNics(LibvirtComputingResource libvirtComputingResource, List<LibvirtVMDef.InterfaceDef> interfaces) {
final ArrayList<UnmanagedInstanceTO.Nic> nics = new ArrayList<>(interfaces.size());
int counter = 0;
for (LibvirtVMDef.InterfaceDef interfaceDef : interfaces) {
@ -180,6 +180,10 @@ public final class LibvirtGetUnmanagedInstancesCommandWrapper extends CommandWra
nic.setNetwork(interfaceDef.getDevName());
nic.setPciSlot(interfaceDef.getSlot().toString());
nic.setVlan(interfaceDef.getVlanTag());
if (nic.getVlan() == -1
&& LibvirtVMDef.InterfaceDef.GuestNetType.BRIDGE.equals(interfaceDef.getNetType())) {
nic.setVlan(libvirtComputingResource.getVlanIdForBridge(interfaceDef.getBrName()));
}
nics.add(nic);
}
return nics;

View File

@ -358,7 +358,8 @@ public class KubernetesClusterScaleWorker extends KubernetesClusterResourceModif
private void scaleKubernetesClusterOffering(KubernetesClusterNodeType nodeType, ServiceOffering serviceOffering,
boolean updateNodeOffering, boolean updateClusterOffering) throws CloudRuntimeException {
validateKubernetesClusterScaleOfferingParameters();
if (!kubernetesCluster.getState().equals(KubernetesCluster.State.Scaling)) {
List<KubernetesCluster.State> scalingStates = List.of(KubernetesCluster.State.Scaling, KubernetesCluster.State.ScalingStoppedCluster);
if (!scalingStates.contains(kubernetesCluster.getState())) {
stateTransitTo(kubernetesCluster.getId(), KubernetesCluster.Event.ScaleUpRequested);
}
if (KubernetesCluster.State.Created.equals(originalState)) {
@ -581,6 +582,8 @@ public class KubernetesClusterScaleWorker extends KubernetesClusterResourceModif
scaleKubernetesClusterOffering(nodeType, scalingServiceOffering, updateNodeOffering, updateClusterOffering);
} else if (clusterSizeScalingNeeded) {
scaleKubernetesClusterSize(nodeType);
} else {
return true;
}
}

View File

@ -7712,7 +7712,8 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir
HypervisorType.getListOfHypervisorsSupportingFunctionality(Functionality.VmStorageMigration)));
}
if (_vmSnapshotDao.findByVm(vmId).size() > 0) {
if (!vm.getHypervisorType().isFunctionalitySupported(Functionality.VmStorageMigrationWithSnapshots) &&
CollectionUtils.isNotEmpty(_vmSnapshotDao.findByVm(vmId))) {
throw new InvalidParameterValueException("VM with VM Snapshots cannot be migrated with storage, please remove all VM snapshots");
}

View File

@ -85,9 +85,11 @@ import org.apache.cloudstack.utils.imagestore.ImageStoreUtil;
import org.apache.cloudstack.utils.reflectiontostringbuilderutils.ReflectionToStringBuilderUtils;
import org.apache.cloudstack.utils.security.DigestHelper;
import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.collections.MapUtils;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.FilenameUtils;
import org.apache.commons.lang3.BooleanUtils;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.NameValuePair;
@ -2742,6 +2744,20 @@ public class NfsSecondaryStorageResource extends ServerResourceBase implements S
return new PingStorageCommand(Host.Type.Storage, id, new HashMap<String, Boolean>());
}
protected void configureStorageNetwork(Map<String, Object> params) {
_storageIp = MapUtils.getString(params, "storageip");
_storageNetmask = (String) params.get("storagenetmask");
_storageGateway = (String) params.get("storagegateway");
if (_storageIp == null && _inSystemVM && _eth1ip != null) {
String eth1Gateway = ObjectUtils.firstNonNull(_localgw, MapUtils.getString(params, "localgw"));
logger.info("Storage network not configured, using management network[ip: {}, netmask: {}, gateway: {}] for storage traffic",
_eth1ip, _eth1mask, eth1Gateway);
_storageIp = _eth1ip;
_storageNetmask = _eth1mask;
_storageGateway = eth1Gateway;
}
}
@Override
public boolean configure(String name, Map<String, Object> params) throws ConfigurationException {
_eth1ip = (String)params.get("eth1ip");
@ -2764,12 +2780,10 @@ public class NfsSecondaryStorageResource extends ServerResourceBase implements S
_inSystemVM = true;
}
_storageIp = (String)params.get("storageip");
configureStorageNetwork(params);
if (_storageIp == null && _inSystemVM) {
logger.warn("There is no storageip in /proc/cmdline, something wrong!");
logger.warn("No storageip in /proc/cmdline, something wrong! Even fallback to management network did not resolve storage IP.");
}
_storageNetmask = (String)params.get("storagenetmask");
_storageGateway = (String)params.get("storagegateway");
super.configure(name, params);
_params = params;

View File

@ -18,35 +18,38 @@
*/
package org.apache.cloudstack.storage.resource;
import org.apache.logging.log4j.Logger;
import static org.mockito.ArgumentMatchers.any;
import org.mockito.Mock;
import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.times;
import java.io.File;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Stream;
import com.cloud.exception.InvalidParameterValueException;
import com.cloud.utils.EncryptionUtil;
import com.cloud.utils.net.NetUtils;
import org.apache.cloudstack.storage.command.DeleteCommand;
import org.apache.cloudstack.storage.command.QuerySnapshotZoneCopyAnswer;
import org.apache.cloudstack.storage.command.QuerySnapshotZoneCopyCommand;
import org.apache.cloudstack.storage.to.SnapshotObjectTO;
import org.apache.cloudstack.storage.to.TemplateObjectTO;
import org.apache.logging.log4j.Logger;
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockedStatic;
import org.mockito.Mockito;
import static org.mockito.Mockito.times;
import org.mockito.Spy;
import org.mockito.junit.MockitoJUnitRunner;
import org.springframework.test.util.ReflectionTestUtils;
import com.cloud.agent.api.to.DataStoreTO;
import com.cloud.exception.InvalidParameterValueException;
import com.cloud.utils.EncryptionUtil;
import com.cloud.utils.net.NetUtils;
@RunWith(MockitoJUnitRunner.class)
public class NfsSecondaryStorageResourceTest {
@ -241,4 +244,45 @@ public class NfsSecondaryStorageResourceTest {
Assert.assertEquals(NetUtils.HTTP_PROTO, result);
}
@Test
public void configureStorageNetworkSetsStorageNetworkWhenParamsContainValues() {
Map<String, Object> params = new HashMap<>();
String ip = "192.168.1.10";
String netmask = "255.255.255.0";
String gateway = "192.168.1.1";
params.put("storageip", ip);
params.put("storagenetmask", netmask);
params.put("storagegateway", gateway);
resource.configureStorageNetwork(params);
Assert.assertEquals(ip, ReflectionTestUtils.getField(resource, "_storageIp"));
Assert.assertEquals(netmask, ReflectionTestUtils.getField(resource, "_storageNetmask"));
Assert.assertEquals(gateway, ReflectionTestUtils.getField(resource, "_storageGateway"));
}
@Test
public void configureStorageNetworkUsesManagementNetworkWhenStorageIpIsNullAndInSystemVM() {
Map<String, Object> params = new HashMap<>();
resource._inSystemVM = true;
String ip = "10.0.0.10";
String netmask = "255.255.255.0";
String gateway = "10.0.0.1";
ReflectionTestUtils.setField(resource, "_eth1ip", ip);
ReflectionTestUtils.setField(resource, "_eth1mask", netmask);
ReflectionTestUtils.setField(resource, "_localgw", gateway);
resource.configureStorageNetwork(params);
Assert.assertEquals(ip, ReflectionTestUtils.getField(resource, "_storageIp"));
Assert.assertEquals(netmask, ReflectionTestUtils.getField(resource, "_storageNetmask"));
Assert.assertEquals(gateway, ReflectionTestUtils.getField(resource, "_storageGateway"));
}
@Test
public void configureStorageNetworkDoesNotSetStorageNetworkWhenNotInSystemVMAndStorageIpIsNull() {
Map<String, Object> params = new HashMap<>();
resource._inSystemVM = false;
resource.configureStorageNetwork(params);
Assert.assertNull(ReflectionTestUtils.getField(resource, "_storageIp"));
Assert.assertNull(ReflectionTestUtils.getField(resource, "_storageNetmask"));
Assert.assertNull(ReflectionTestUtils.getField(resource, "_storageGateway"));
}
}

View File

@ -232,6 +232,8 @@ class CsNetfilters(object):
if hook == "input" or hook == "output":
CsHelper.execute("nft add rule %s %s %s icmpv6 type { echo-request, echo-reply, \
nd-neighbor-solicit, nd-router-advert, nd-neighbor-advert } accept" % (address_family, table, chain))
elif hook == "forward":
CsHelper.execute("nft add rule %s %s %s ct state established,related accept" % (address_family, table, chain))
def add_ip4_chain(self, address_family, table, chain, hook, action):
chain_policy = ""

View File

@ -47,7 +47,7 @@
<a-select
style="width: 100%"
v-if="validNetworks[record.id] && validNetworks[record.id].length > 0"
:defaultValue="validNetworks[record.id][0].id"
:defaultValue="getDefaultNetwork(record)"
@change="val => handleNetworkChange(record, val)"
showSearch
optionFilterProp="label"
@ -265,7 +265,16 @@ export default {
this.values = {}
this.ipAddresses = {}
for (const item of this.items) {
var network = this.validNetworks[item.id]?.[0] || null
let network = null
if (item.vlanid && item.vlanid !== -1) {
const matched = this.validNetworks[item.id].filter(x => Number(x.vlan) === item.vlanid)
if (matched.length > 0) {
network = matched[0]
}
}
if (!network) {
network = this.validNetworks[item.id]?.[0] || null
}
this.values[item.id] = network ? network.id : ''
this.ipAddresses[item.id] = (!network || network.type === 'L2') ? null : 'auto'
this.setIpAddressEnabled(item, network)
@ -280,6 +289,9 @@ export default {
}
this.sendValuesTimed()
},
getDefaultNetwork (record) {
return this.values[record.id] || this.validNetworks[record.id]?.[0]?.id
},
sendValuesTimed () {
clearTimeout(this.sendValuesTimer)
this.sendValuesTimer = setTimeout(() => {