From e3681a04bb8deb03a801716252c12f496640d919 Mon Sep 17 00:00:00 2001 From: Abhishek Kumar Date: Wed, 10 Sep 2025 15:55:16 +0530 Subject: [PATCH 1/6] server: allow migration of vm with snapshots for vmware (#9305) Fixes #9061 Signed-off-by: Abhishek Kumar --- api/src/main/java/com/cloud/hypervisor/Hypervisor.java | 6 ++++-- server/src/main/java/com/cloud/vm/UserVmManagerImpl.java | 3 ++- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/api/src/main/java/com/cloud/hypervisor/Hypervisor.java b/api/src/main/java/com/cloud/hypervisor/Hypervisor.java index 27ffef1c370..5739daf98f7 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/server/src/main/java/com/cloud/vm/UserVmManagerImpl.java b/server/src/main/java/com/cloud/vm/UserVmManagerImpl.java index 0abab9b149d..81b652169bf 100644 --- a/server/src/main/java/com/cloud/vm/UserVmManagerImpl.java +++ b/server/src/main/java/com/cloud/vm/UserVmManagerImpl.java @@ -7338,7 +7338,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"); } From 5d32492676a401804b2bec1f138c170057f56f2a Mon Sep 17 00:00:00 2001 From: Fabricio Duarte Date: Wed, 10 Sep 2025 11:37:17 -0300 Subject: [PATCH 2/6] IPv6 firewall: accept packets from related and established connections (#10970) * IPv6 firewall: accept related packets and packets from established connections * Remove rule from input chain --- systemvm/debian/opt/cloud/bin/cs/CsNetfilter.py | 2 ++ 1 file changed, 2 insertions(+) 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 = "" From e64e94ad3f7555f8f66edf2e25408e5032b2731f Mon Sep 17 00:00:00 2001 From: Abhisar Sinha <63767682+abh1sar@users.noreply.github.com> Date: Thu, 11 Sep 2025 14:09:09 +0530 Subject: [PATCH 3/6] Import KVM VM: Autodetect vlan id from bridge name (#11507) * Get vlan from bridge name while importing kvm instances * Fix LibvirtReplugNicCommandWrapperTest * Cleanup MultiNetworkSelection.vue * getting the vlanid from the /proc/net/vlan/ file instead of the bridge name * Update plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtGetUnmanagedInstancesCommandWrapper.java Co-authored-by: dahn * Don't call sendValuesTimed from getDefaultNetwork as it's causing an infinte loop * set default network values in setDefaultValues instead of getDefaultNetwork --------- Co-authored-by: Wei Zhou Co-authored-by: dahn --- .../kvm/resource/LibvirtComputingResource.java | 14 ++++++++++++++ ...bvirtGetUnmanagedInstancesCommandWrapper.java | 8 ++++++-- .../compute/wizard/MultiNetworkSelection.vue | 16 ++++++++++++++-- 3 files changed, 34 insertions(+), 4 deletions(-) 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 19c6e7145a6..2d63a669661 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 @@ -1797,6 +1797,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/ui/src/views/compute/wizard/MultiNetworkSelection.vue b/ui/src/views/compute/wizard/MultiNetworkSelection.vue index 92d4a437599..0e9983bc356 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(() => { From 036fd0017056345407b14307c6a214518741b844 Mon Sep 17 00:00:00 2001 From: Nicolas Vazquez Date: Thu, 11 Sep 2025 05:40:08 -0300 Subject: [PATCH 4/6] kvm: Fix NPE in case host UEFI detail is not set on agent connection (#11610) --- .../src/main/java/com/cloud/agent/manager/AgentManagerImpl.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 aff528efede..6bca59bf980 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 @@ -710,7 +710,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; } From 7c727a344061d73f8ccdc6febbbfa019e1bd1007 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bernardo=20De=20Marco=20Gon=C3=A7alves?= Date: Thu, 11 Sep 2025 05:42:41 -0300 Subject: [PATCH 5/6] CKS: Fix transition exception when scaling Stopped k8s clusters (#11598) * add new k8s cluster transition * apply suggestion * apply suggestion --- .../java/com/cloud/kubernetes/cluster/KubernetesCluster.java | 4 ++++ .../cluster/actionworkers/KubernetesClusterScaleWorker.java | 5 ++++- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/plugins/integrations/kubernetes-service/src/main/java/com/cloud/kubernetes/cluster/KubernetesCluster.java b/plugins/integrations/kubernetes-service/src/main/java/com/cloud/kubernetes/cluster/KubernetesCluster.java index 591da077aec..379a0db2ccd 100644 --- a/plugins/integrations/kubernetes-service/src/main/java/com/cloud/kubernetes/cluster/KubernetesCluster.java +++ b/plugins/integrations/kubernetes-service/src/main/java/com/cloud/kubernetes/cluster/KubernetesCluster.java @@ -58,6 +58,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"), Alert("State to represent Kubernetes clusters which are not in expected desired state (operationally in active control place, stopped cluster VM's etc)."), Recovering("State in which Kubernetes cluster is recovering from alert state"), @@ -89,8 +90,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/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 4d50ef7e1f8..f6828e3b203 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 @@ -284,7 +284,8 @@ public class KubernetesClusterScaleWorker extends KubernetesClusterResourceModif private void scaleKubernetesClusterOffering() 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)) { @@ -475,6 +476,8 @@ public class KubernetesClusterScaleWorker extends KubernetesClusterResourceModif scaleKubernetesClusterOffering(); } else if (clusterSizeScalingNeeded) { scaleKubernetesClusterSize(); + } else { + return true; } stateTransitTo(kubernetesCluster.getId(), KubernetesCluster.Event.OperationSucceeded); return true; From 38006b2e03db4b7f067361ff49f7fd9795dad23c Mon Sep 17 00:00:00 2001 From: Abhishek Kumar Date: Thu, 11 Sep 2025 14:23:54 +0530 Subject: [PATCH 6/6] ssvm: use mgmt network if no storage network (#10735) * ssvm: use mgmt network if no storage network Fixes #10163 Based on https://github.com/apache/cloudstack/issues/10163#issuecomment-2589197648 Signed-off-by: Abhishek Kumar * update Signed-off-by: Abhishek Kumar * fix Signed-off-by: Abhishek Kumar --------- Signed-off-by: Abhishek Kumar --- .../resource/NfsSecondaryStorageResource.java | 22 ++++++-- .../NfsSecondaryStorageResourceTest.java | 56 +++++++++++++++++-- 2 files changed, 68 insertions(+), 10 deletions(-) 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 4cd4e8caa30..739802d427a 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 @@ -84,9 +84,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; @@ -2717,6 +2719,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"); @@ -2739,12 +2755,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 37a0697c137..9f510f25b12 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,36 +18,39 @@ */ 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.spy; +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 { @@ -242,4 +245,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")); + } }