diff --git a/api/src/main/java/com/cloud/hypervisor/Hypervisor.java b/api/src/main/java/com/cloud/hypervisor/Hypervisor.java index 5baac484772..1f8741d3b7b 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 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 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 8b5551d6a8e..571d993c7a1 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 @@ 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); 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 5e10312741f..5483331cc33 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 @@ 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; } 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 096ec26e878..42aa1130c52 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 @@ 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", 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 e807bf3c821..b382613f8ab 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 @@ 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 getUnmanagedInstanceNics(List interfaces) { + private List getUnmanagedInstanceNics(LibvirtComputingResource libvirtComputingResource, List interfaces) { final ArrayList 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; 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 3461d5c0634..0938fd44554 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 @@ 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 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; } } diff --git a/server/src/main/java/com/cloud/vm/UserVmManagerImpl.java b/server/src/main/java/com/cloud/vm/UserVmManagerImpl.java index cd7d198eb61..26072971f43 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 @@ 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"); } 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 500e6bf47df..916773cc5a4 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.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()); } + protected void configureStorageNetwork(Map 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 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; 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 6d474ddcc92..17414c60f20 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 @@ public class NfsSecondaryStorageResourceTest { Assert.assertEquals(NetUtils.HTTP_PROTO, result); } + + @Test + public void configureStorageNetworkSetsStorageNetworkWhenParamsContainValues() { + Map 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 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 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 bf7aaf5ddd6..80d64e8f2d9 100755 --- a/systemvm/debian/opt/cloud/bin/cs/CsNetfilter.py +++ b/systemvm/debian/opt/cloud/bin/cs/CsNetfilter.py @@ -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 = "" diff --git a/ui/src/views/compute/wizard/MultiNetworkSelection.vue b/ui/src/views/compute/wizard/MultiNetworkSelection.vue index 5c241061c0d..f593d0f3d91 100644 --- a/ui/src/views/compute/wizard/MultiNetworkSelection.vue +++ b/ui/src/views/compute/wizard/MultiNetworkSelection.vue @@ -47,7 +47,7 @@ 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(() => {