Merge remote-tracking branch 'apache/4.20'
diff --git a/api/src/main/java/com/cloud/hypervisor/Hypervisor.java b/api/src/main/java/com/cloud/hypervisor/Hypervisor.java
index 5baac48..1f8741d 100644
--- a/api/src/main/java/com/cloud/hypervisor/Hypervisor.java
+++ b/api/src/main/java/com/cloud/hypervisor/Hypervisor.java
@@ -31,20 +31,22 @@
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");
diff --git a/api/src/main/java/com/cloud/kubernetes/cluster/KubernetesCluster.java b/api/src/main/java/com/cloud/kubernetes/cluster/KubernetesCluster.java
index 8b5551d..571d993 100644
--- a/api/src/main/java/com/cloud/kubernetes/cluster/KubernetesCluster.java
+++ b/api/src/main/java/com/cloud/kubernetes/cluster/KubernetesCluster.java
@@ -60,6 +60,7 @@
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 @@
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);
diff --git a/engine/orchestration/src/main/java/com/cloud/agent/manager/AgentManagerImpl.java b/engine/orchestration/src/main/java/com/cloud/agent/manager/AgentManagerImpl.java
index 5e10312..5483331 100644
--- a/engine/orchestration/src/main/java/com/cloud/agent/manager/AgentManagerImpl.java
+++ b/engine/orchestration/src/main/java/com/cloud/agent/manager/AgentManagerImpl.java
@@ -809,7 +809,7 @@
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;
}
diff --git a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/LibvirtComputingResource.java b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/LibvirtComputingResource.java
index 096ec26..42aa113 100644
--- a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/LibvirtComputingResource.java
+++ b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/LibvirtComputingResource.java
@@ -1902,6 +1902,20 @@
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",
diff --git a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtGetUnmanagedInstancesCommandWrapper.java b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtGetUnmanagedInstancesCommandWrapper.java
index e807bf3..b382613 100644
--- a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtGetUnmanagedInstancesCommandWrapper.java
+++ b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtGetUnmanagedInstancesCommandWrapper.java
@@ -132,7 +132,7 @@
}
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 @@
}
}
- 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 @@
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;
diff --git a/plugins/integrations/kubernetes-service/src/main/java/com/cloud/kubernetes/cluster/actionworkers/KubernetesClusterScaleWorker.java b/plugins/integrations/kubernetes-service/src/main/java/com/cloud/kubernetes/cluster/actionworkers/KubernetesClusterScaleWorker.java
index 3461d5c..0938fd4 100644
--- a/plugins/integrations/kubernetes-service/src/main/java/com/cloud/kubernetes/cluster/actionworkers/KubernetesClusterScaleWorker.java
+++ b/plugins/integrations/kubernetes-service/src/main/java/com/cloud/kubernetes/cluster/actionworkers/KubernetesClusterScaleWorker.java
@@ -358,7 +358,8 @@
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 @@
scaleKubernetesClusterOffering(nodeType, scalingServiceOffering, updateNodeOffering, updateClusterOffering);
} else if (clusterSizeScalingNeeded) {
scaleKubernetesClusterSize(nodeType);
+ } else {
+ return true;
}
}
diff --git a/server/src/main/java/com/cloud/vm/UserVmManagerImpl.java b/server/src/main/java/com/cloud/vm/UserVmManagerImpl.java
index cd7d198..2607297 100644
--- a/server/src/main/java/com/cloud/vm/UserVmManagerImpl.java
+++ b/server/src/main/java/com/cloud/vm/UserVmManagerImpl.java
@@ -7712,7 +7712,8 @@
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");
}
diff --git a/services/secondary-storage/server/src/main/java/org/apache/cloudstack/storage/resource/NfsSecondaryStorageResource.java b/services/secondary-storage/server/src/main/java/org/apache/cloudstack/storage/resource/NfsSecondaryStorageResource.java
index 500e6bf..916773c 100644
--- a/services/secondary-storage/server/src/main/java/org/apache/cloudstack/storage/resource/NfsSecondaryStorageResource.java
+++ b/services/secondary-storage/server/src/main/java/org/apache/cloudstack/storage/resource/NfsSecondaryStorageResource.java
@@ -85,9 +85,11 @@
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 @@
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 @@
_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;
diff --git a/services/secondary-storage/server/src/test/java/org/apache/cloudstack/storage/resource/NfsSecondaryStorageResourceTest.java b/services/secondary-storage/server/src/test/java/org/apache/cloudstack/storage/resource/NfsSecondaryStorageResourceTest.java
index 6d474dd..17414c6 100644
--- a/services/secondary-storage/server/src/test/java/org/apache/cloudstack/storage/resource/NfsSecondaryStorageResourceTest.java
+++ b/services/secondary-storage/server/src/test/java/org/apache/cloudstack/storage/resource/NfsSecondaryStorageResourceTest.java
@@ -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 @@
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"));
+ }
}
diff --git a/systemvm/debian/opt/cloud/bin/cs/CsNetfilter.py b/systemvm/debian/opt/cloud/bin/cs/CsNetfilter.py
index bf7aaf5..80d64e8 100755
--- a/systemvm/debian/opt/cloud/bin/cs/CsNetfilter.py
+++ b/systemvm/debian/opt/cloud/bin/cs/CsNetfilter.py
@@ -232,6 +232,8 @@
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 = ""
diff --git a/ui/src/views/compute/wizard/MultiNetworkSelection.vue b/ui/src/views/compute/wizard/MultiNetworkSelection.vue
index 5c24106..f593d0f 100644
--- a/ui/src/views/compute/wizard/MultiNetworkSelection.vue
+++ b/ui/src/views/compute/wizard/MultiNetworkSelection.vue
@@ -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 @@
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 @@
}
this.sendValuesTimed()
},
+ getDefaultNetwork (record) {
+ return this.values[record.id] || this.validNetworks[record.id]?.[0]?.id
+ },
sendValuesTimed () {
clearTimeout(this.sendValuesTimer)
this.sendValuesTimer = setTimeout(() => {