diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/snapshot/CreateSnapshotFromVMSnapshotCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/snapshot/CreateSnapshotFromVMSnapshotCmd.java index 75089cece8e..c99373688e5 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/snapshot/CreateSnapshotFromVMSnapshotCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/snapshot/CreateSnapshotFromVMSnapshotCmd.java @@ -209,4 +209,9 @@ public class CreateSnapshotFromVMSnapshotCmd extends BaseAsyncCreateCmd { } return null; } + + @Override + public Long getApiResourceId() { + return getEntityId(); + } } diff --git a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/BridgeVifDriver.java b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/BridgeVifDriver.java index 98107c8609e..1635ab03837 100644 --- a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/BridgeVifDriver.java +++ b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/BridgeVifDriver.java @@ -188,6 +188,21 @@ public class BridgeVifDriver extends VifDriverBase { return vNetId != null && protocol != null && !vNetId.equalsIgnoreCase("untagged"); } + protected String createStorageVnetBridgeIfNeeded(NicTO nic, String trafficLabel, + String storageBrName) throws InternalErrorException { + if (!Networks.BroadcastDomainType.Storage.equals(nic.getBroadcastType()) || nic.getBroadcastUri() == null) { + return storageBrName; + } + String vNetId = Networks.BroadcastDomainType.getValue(nic.getBroadcastUri()); + String protocol = Networks.BroadcastDomainType.Vlan.scheme(); + if (!isValidProtocolAndVnetId(vNetId, protocol)) { + return storageBrName; + } + logger.debug(String.format("creating a vNet dev and bridge for %s traffic per traffic label %s", + Networks.TrafficType.Storage.name(), trafficLabel)); + return createVnetBr(vNetId, storageBrName, protocol); + } + @Override public LibvirtVMDef.InterfaceDef plug(NicTO nic, String guestOsType, String nicAdapter, Map extraConfig) throws InternalErrorException, LibvirtException { @@ -254,15 +269,7 @@ public class BridgeVifDriver extends VifDriverBase { intf.defBridgeNet(_bridges.get("private"), null, nic.getMac(), getGuestNicModel(guestOsType, nicAdapter)); } else if (nic.getType() == Networks.TrafficType.Storage) { String storageBrName = nic.getName() == null ? _bridges.get("private") : nic.getName(); - if (nic.getBroadcastType() == Networks.BroadcastDomainType.Storage) { - vNetId = Networks.BroadcastDomainType.getValue(nic.getBroadcastUri()); - protocol = Networks.BroadcastDomainType.Vlan.scheme(); - } - if (isValidProtocolAndVnetId(vNetId, protocol)) { - logger.debug(String.format("creating a vNet dev and bridge for %s traffic per traffic label %s", - Networks.TrafficType.Storage.name(), trafficLabel)); - storageBrName = createVnetBr(vNetId, storageBrName, protocol); - } + storageBrName = createStorageVnetBridgeIfNeeded(nic, trafficLabel, storageBrName); intf.defBridgeNet(storageBrName, null, nic.getMac(), getGuestNicModel(guestOsType, nicAdapter)); } if (nic.getPxeDisable()) { @@ -295,7 +302,7 @@ public class BridgeVifDriver extends VifDriverBase { return "brvx-" + vnetId; } - private String createVnetBr(String vNetId, String pifKey, String protocol) throws InternalErrorException { + protected String createVnetBr(String vNetId, String pifKey, String protocol) throws InternalErrorException { String nic = _pifs.get(pifKey); if (nic == null || isVxlanOrNetris(protocol)) { // if not found in bridge map, maybe traffic label refers to pif already? diff --git a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/LibvirtDomainXMLParser.java b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/LibvirtDomainXMLParser.java index c4d5ac48f6f..8b26bc94e21 100644 --- a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/LibvirtDomainXMLParser.java +++ b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/LibvirtDomainXMLParser.java @@ -48,6 +48,7 @@ import com.cloud.hypervisor.kvm.resource.LibvirtVMDef.RngDef.RngBackendModel; import com.cloud.hypervisor.kvm.resource.LibvirtVMDef.WatchDogDef; import com.cloud.hypervisor.kvm.resource.LibvirtVMDef.WatchDogDef.WatchDogAction; import com.cloud.hypervisor.kvm.resource.LibvirtVMDef.WatchDogDef.WatchDogModel; +import com.cloud.hypervisor.kvm.resource.LibvirtVMDef.GuestDef; public class LibvirtDomainXMLParser { protected Logger logger = LogManager.getLogger(getClass()); @@ -63,6 +64,8 @@ public class LibvirtDomainXMLParser { private LibvirtVMDef.CpuTuneDef cpuTuneDef; private LibvirtVMDef.CpuModeDef cpuModeDef; private String name; + private GuestDef.BootType bootType; + private GuestDef.BootMode bootMode; public boolean parseDomainXML(String domXML) { DocumentBuilder builder; @@ -388,6 +391,7 @@ public class LibvirtDomainXMLParser { } extractCpuTuneDef(rootElement); extractCpuModeDef(rootElement); + extractBootDef(rootElement); return true; } catch (ParserConfigurationException e) { logger.debug(e.toString()); @@ -516,6 +520,14 @@ public class LibvirtDomainXMLParser { return cpuModeDef; } + public GuestDef.BootType getBootType() { + return bootType; + } + + public GuestDef.BootMode getBootMode() { + return bootMode; + } + private void extractCpuTuneDef(final Element rootElement) { NodeList cpuTunesList = rootElement.getElementsByTagName("cputune"); if (cpuTunesList.getLength() > 0) { @@ -569,4 +581,26 @@ public class LibvirtDomainXMLParser { } } } + + protected void extractBootDef(final Element rootElement) { + bootType = GuestDef.BootType.BIOS; + bootMode = GuestDef.BootMode.LEGACY; + Element osElement = (Element) rootElement.getElementsByTagName("os").item(0); + if (osElement == null) { + return; + } + NodeList loaderList = osElement.getElementsByTagName("loader"); + if (loaderList.getLength() == 0) { + return; + } + Element loader = (Element) loaderList.item(0); + String type = loader.getAttribute("type"); + String secure = loader.getAttribute("secure"); + if ("pflash".equalsIgnoreCase(type) || loader.getTextContent().toLowerCase().contains("uefi")) { + bootType = GuestDef.BootType.UEFI; + } + if ("yes".equalsIgnoreCase(secure)) { + bootMode = GuestDef.BootMode.SECURE; + } + } } diff --git a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/LibvirtVMDef.java b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/LibvirtVMDef.java index ec4a06ae022..46a5024bf15 100644 --- a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/LibvirtVMDef.java +++ b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/LibvirtVMDef.java @@ -65,7 +65,7 @@ public class LibvirtVMDef { } } - enum BootType { + public enum BootType { UEFI("UEFI"), BIOS("BIOS"); String _type; @@ -80,7 +80,7 @@ public class LibvirtVMDef { } } - enum BootMode { + public enum BootMode { LEGACY("LEGACY"), SECURE("SECURE"); String _mode; 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 70b1715cc20..e807bf3c821 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 @@ -135,6 +135,12 @@ public final class LibvirtGetUnmanagedInstancesCommandWrapper extends CommandWra instance.setNics(getUnmanagedInstanceNics(parser.getInterfaces())); instance.setDisks(getUnmanagedInstanceDisks(parser.getDisks(),libvirtComputingResource, conn, domain.getName())); instance.setVncPassword(getFormattedVncPassword(parser.getVncPasswd())); + if (parser.getBootType() != null) { + instance.setBootType(parser.getBootType().toString()); + } + if (parser.getBootMode() != null) { + instance.setBootMode(parser.getBootMode().toString()); + } return instance; } catch (Exception e) { diff --git a/plugins/hypervisors/kvm/src/test/java/com/cloud/hypervisor/kvm/resource/BridgeVifDriverTest.java b/plugins/hypervisors/kvm/src/test/java/com/cloud/hypervisor/kvm/resource/BridgeVifDriverTest.java index 48bba2b78ed..00364948f82 100644 --- a/plugins/hypervisors/kvm/src/test/java/com/cloud/hypervisor/kvm/resource/BridgeVifDriverTest.java +++ b/plugins/hypervisors/kvm/src/test/java/com/cloud/hypervisor/kvm/resource/BridgeVifDriverTest.java @@ -16,24 +16,29 @@ // under the License. package com.cloud.hypervisor.kvm.resource; +import java.net.URI; +import java.net.URISyntaxException; + import org.junit.Assert; -import org.junit.Before; import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.InjectMocks; +import org.mockito.Mockito; +import org.mockito.Spy; +import org.mockito.junit.MockitoJUnitRunner; import com.cloud.agent.api.to.NicTO; +import com.cloud.exception.InternalErrorException; import com.cloud.network.Networks; -import org.junit.runner.RunWith; -import org.mockito.junit.MockitoJUnitRunner; @RunWith(MockitoJUnitRunner.class) public class BridgeVifDriverTest { - private BridgeVifDriver driver; + private static final String BRIDGE_NAME = "cloudbr1"; - @Before - public void setUp() throws Exception { - driver = new BridgeVifDriver(); - } + @Spy + @InjectMocks + private BridgeVifDriver driver = new BridgeVifDriver(); @Test public void isBroadcastTypeVlanOrVxlan() { @@ -58,4 +63,41 @@ public class BridgeVifDriverTest { Assert.assertTrue(driver.isValidProtocolAndVnetId("123", "vlan")); Assert.assertTrue(driver.isValidProtocolAndVnetId("456", "vxlan")); } + + @Test + public void createStorageVnetBridgeIfNeededReturnsStorageBrNameWhenBroadcastTypeIsNotStorageButValidValues() throws InternalErrorException { + NicTO nic = new NicTO(); + nic.setBroadcastType(Networks.BroadcastDomainType.Storage); + int vlan = 123; + String newBridge = "br-" + vlan; + nic.setBroadcastUri(Networks.BroadcastDomainType.Storage.toUri(vlan)); + Mockito.doReturn(newBridge).when(driver).createVnetBr(Mockito.anyString(), Mockito.anyString(), Mockito.anyString()); + String result = driver.createStorageVnetBridgeIfNeeded(nic, "trafficLabel", BRIDGE_NAME); + Assert.assertEquals(newBridge, result); + } + + @Test + public void createStorageVnetBridgeIfNeededReturnsStorageBrNameWhenBroadcastTypeIsNotStorage() throws InternalErrorException { + NicTO nic = new NicTO(); + nic.setBroadcastType(Networks.BroadcastDomainType.Vlan); + String result = driver.createStorageVnetBridgeIfNeeded(nic, "trafficLabel", BRIDGE_NAME); + Assert.assertEquals(BRIDGE_NAME, result); + } + + @Test + public void createStorageVnetBridgeIfNeededReturnsStorageBrNameWhenBroadcastUriIsNull() throws InternalErrorException { + NicTO nic = new NicTO(); + nic.setBroadcastType(Networks.BroadcastDomainType.Storage); + String result = driver.createStorageVnetBridgeIfNeeded(nic, "trafficLabel", BRIDGE_NAME); + Assert.assertEquals(BRIDGE_NAME, result); + } + + @Test + public void createStorageVnetBridgeIfNeededCreatesVnetBridgeWhenUntaggedVlan() throws InternalErrorException, URISyntaxException { + NicTO nic = new NicTO(); + nic.setBroadcastType(Networks.BroadcastDomainType.Storage); + nic.setBroadcastUri(new URI(Networks.BroadcastDomainType.Storage.scheme() + "://untagged")); + String result = driver.createStorageVnetBridgeIfNeeded(nic, "trafficLabel", BRIDGE_NAME); + Assert.assertEquals(BRIDGE_NAME, result); + } } diff --git a/plugins/hypervisors/kvm/src/test/java/com/cloud/hypervisor/kvm/resource/LibvirtDomainXMLParserTest.java b/plugins/hypervisors/kvm/src/test/java/com/cloud/hypervisor/kvm/resource/LibvirtDomainXMLParserTest.java index 921bbf0c9a8..de50cf34202 100644 --- a/plugins/hypervisors/kvm/src/test/java/com/cloud/hypervisor/kvm/resource/LibvirtDomainXMLParserTest.java +++ b/plugins/hypervisors/kvm/src/test/java/com/cloud/hypervisor/kvm/resource/LibvirtDomainXMLParserTest.java @@ -20,8 +20,13 @@ package com.cloud.hypervisor.kvm.resource; import java.io.File; +import java.io.IOException; +import java.io.StringReader; import java.util.List; +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.ParserConfigurationException; + import com.cloud.hypervisor.kvm.resource.LibvirtVMDef.ChannelDef; import com.cloud.hypervisor.kvm.resource.LibvirtVMDef.DiskDef; import com.cloud.hypervisor.kvm.resource.LibvirtVMDef.InterfaceDef; @@ -31,10 +36,15 @@ import com.cloud.hypervisor.kvm.resource.LibvirtVMDef.WatchDogDef; import junit.framework.TestCase; import org.apache.cloudstack.utils.qemu.QemuObject; +import org.apache.cloudstack.utils.security.ParserUtils; import org.junit.Assert; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.junit.MockitoJUnitRunner; +import org.w3c.dom.Document; +import org.w3c.dom.Element; +import org.xml.sax.InputSource; +import org.xml.sax.SAXException; @RunWith(MockitoJUnitRunner.class) public class LibvirtDomainXMLParserTest extends TestCase { @@ -386,4 +396,84 @@ public class LibvirtDomainXMLParserTest extends TestCase { Assert.assertEquals("CPU cores count is parsed", 4, libvirtDomainXMLParser.getCpuModeDef().getCoresPerSocket()); Assert.assertEquals("CPU threads count is parsed", 2, libvirtDomainXMLParser.getCpuModeDef().getThreadsPerCore()); } + + private LibvirtDomainXMLParser parseElementFromXML(String xml) { + LibvirtDomainXMLParser parser = new LibvirtDomainXMLParser(); + DocumentBuilder builder; + try { + builder = ParserUtils.getSaferDocumentBuilderFactory().newDocumentBuilder(); + InputSource is = new InputSource(); + is.setCharacterStream(new StringReader(xml)); + Document doc = builder.parse(is); + Element element = doc.getDocumentElement(); + parser.extractBootDef(element); + } catch (ParserConfigurationException | IOException | SAXException e) { + Assert.fail("Failed to parse XML: " + e.getMessage()); + } + return parser; + } + + @Test + public void extractBootDefParsesUEFISecureBootCorrectly() { + String xml = "" + + "" + + "/path/to/uefi/loader" + + "" + + ""; + + LibvirtDomainXMLParser parser = parseElementFromXML(xml); + + assertEquals(LibvirtVMDef.GuestDef.BootType.UEFI, parser.getBootType()); + assertEquals(LibvirtVMDef.GuestDef.BootMode.SECURE, parser.getBootMode()); + } + + @Test + public void extractBootDefParsesUEFILegacyBootCorrectly() { + String xml = "" + + "" + + "/path/to/uefi/loader" + + "" + + ""; + + LibvirtDomainXMLParser parser = parseElementFromXML(xml); + + assertEquals(LibvirtVMDef.GuestDef.BootType.UEFI, parser.getBootType()); + assertEquals(LibvirtVMDef.GuestDef.BootMode.LEGACY, parser.getBootMode()); + } + + @Test + public void extractBootDefDefaultsToBIOSLegacyWhenNoLoaderPresent() { + String xml = "" + + "" + + "hvm" + + "" + + ""; + + LibvirtDomainXMLParser parser = parseElementFromXML(xml); + + assertEquals(LibvirtVMDef.GuestDef.BootType.BIOS, parser.getBootType()); + assertEquals(LibvirtVMDef.GuestDef.BootMode.LEGACY, parser.getBootMode()); + } + + @Test + public void extractBootDefHandlesEmptyOSSection() { + String xml = "" + + "" + + ""; + + LibvirtDomainXMLParser parser = parseElementFromXML(xml); + + assertEquals(LibvirtVMDef.GuestDef.BootType.BIOS, parser.getBootType()); + assertEquals(LibvirtVMDef.GuestDef.BootMode.LEGACY, parser.getBootMode()); + } + + @Test + public void extractBootDefHandlesMissingOSSection() { + String xml = ""; + + LibvirtDomainXMLParser parser = parseElementFromXML(xml); + + assertEquals(LibvirtVMDef.GuestDef.BootType.BIOS, parser.getBootType()); + assertEquals(LibvirtVMDef.GuestDef.BootMode.LEGACY, parser.getBootMode()); + } } diff --git a/plugins/integrations/kubernetes-service/src/main/java/com/cloud/kubernetes/cluster/KubernetesClusterManagerImpl.java b/plugins/integrations/kubernetes-service/src/main/java/com/cloud/kubernetes/cluster/KubernetesClusterManagerImpl.java index dc092608ff3..1466fdf8782 100644 --- a/plugins/integrations/kubernetes-service/src/main/java/com/cloud/kubernetes/cluster/KubernetesClusterManagerImpl.java +++ b/plugins/integrations/kubernetes-service/src/main/java/com/cloud/kubernetes/cluster/KubernetesClusterManagerImpl.java @@ -416,18 +416,33 @@ public class KubernetesClusterManagerImpl extends ManagerBase implements Kuberne logTransitStateAndThrow(logLevel, message, null, null, ex); } - private boolean isKubernetesServiceNetworkOfferingConfigured(DataCenter zone) { + private boolean isKubernetesServiceNetworkOfferingConfigured(DataCenter zone, Long networkId) { // Check network offering String networkOfferingName = KubernetesClusterNetworkOffering.value(); - if (networkOfferingName == null || networkOfferingName.isEmpty()) { - logger.warn(String.format("Global setting %s is empty. Admin has not yet specified the network offering to be used for provisioning isolated network for the cluster", KubernetesClusterNetworkOffering.key())); + if (StringUtils.isEmpty(networkOfferingName) && networkId == null) { + logger.warn("Global setting: {} is empty. Admin has not yet specified the network offering to be used for provisioning isolated network for the cluster nor has a pre-created network been passed", KubernetesClusterNetworkOffering.key()); return false; } - NetworkOfferingVO networkOffering = networkOfferingDao.findByUniqueName(networkOfferingName); - if (networkOffering == null) { - logger.warn(String.format("Unable to find the network offering %s to be used for provisioning Kubernetes cluster", networkOfferingName)); - return false; + NetworkOfferingVO networkOffering = null; + if (networkId != null) { + NetworkVO network = networkDao.findById(networkId); + if (network == null) { + logger.warn("Unable to find the network with ID: {} passed for the Kubernetes cluster", networkId); + return false; + } + networkOffering = networkOfferingDao.findById(network.getNetworkOfferingId()); + if (networkOffering == null) { + logger.warn("Unable to find the network offering of the network: {} ({}) to be used for provisioning Kubernetes cluster", network.getName(), network.getUuid()); + return false; + } + } else if (StringUtils.isNotEmpty(networkOfferingName)) { + networkOffering = networkOfferingDao.findByUniqueName(networkOfferingName); + if (networkOffering == null) { + logger.warn("Unable to find the network offering: {} to be used for provisioning Kubernetes cluster", networkOfferingName); + return false; + } } + if (networkOffering.getState() == NetworkOffering.State.Disabled) { logger.warn("Network offering: {} is not enabled", networkOffering); return false; @@ -462,8 +477,8 @@ public class KubernetesClusterManagerImpl extends ManagerBase implements Kuberne return true; } - private boolean isKubernetesServiceConfigured(DataCenter zone) { - if (!isKubernetesServiceNetworkOfferingConfigured(zone)) { + private boolean isKubernetesServiceConfigured(DataCenter zone, Long networkId) { + if (!isKubernetesServiceNetworkOfferingConfigured(zone, networkId)) { return false; } return true; @@ -1018,7 +1033,7 @@ public class KubernetesClusterManagerImpl extends ManagerBase implements Kuberne DataCenter zone = validateAndGetZoneForKubernetesCreateParameters(zoneId, networkId); - if (!isKubernetesServiceConfigured(zone)) { + if (!isKubernetesServiceConfigured(zone, networkId)) { throw new CloudRuntimeException("Kubernetes service has not been configured properly to provision Kubernetes clusters"); } diff --git a/plugins/network-elements/internal-loadbalancer/src/main/java/org/apache/cloudstack/network/lb/InternalLoadBalancerVMManagerImpl.java b/plugins/network-elements/internal-loadbalancer/src/main/java/org/apache/cloudstack/network/lb/InternalLoadBalancerVMManagerImpl.java index 02dfb2a179e..4512b908556 100644 --- a/plugins/network-elements/internal-loadbalancer/src/main/java/org/apache/cloudstack/network/lb/InternalLoadBalancerVMManagerImpl.java +++ b/plugins/network-elements/internal-loadbalancer/src/main/java/org/apache/cloudstack/network/lb/InternalLoadBalancerVMManagerImpl.java @@ -32,6 +32,8 @@ import java.util.Map; import javax.inject.Inject; import javax.naming.ConfigurationException; +import com.cloud.event.ActionEvent; +import com.cloud.event.EventTypes; import org.apache.cloudstack.context.CallContext; import org.apache.cloudstack.engine.orchestration.service.NetworkOrchestrationService; import org.apache.cloudstack.framework.config.dao.ConfigurationDao; @@ -545,6 +547,7 @@ public class InternalLoadBalancerVMManagerImpl extends ManagerBase implements In } @Override + @ActionEvent(eventType = EventTypes.EVENT_INTERNAL_LB_VM_STOP, eventDescription = "stopping internal LB VM", async = true) public VirtualRouter stopInternalLbVm(final long vmId, final boolean forced, final Account caller, final long callerUserId) throws ConcurrentOperationException, ResourceUnavailableException { final DomainRouterVO internalLbVm = _internalLbVmDao.findById(vmId); if (internalLbVm == null || internalLbVm.getRole() != Role.INTERNAL_LB_VM) { @@ -974,6 +977,7 @@ public class InternalLoadBalancerVMManagerImpl extends ManagerBase implements In } @Override + @ActionEvent(eventType = EventTypes.EVENT_INTERNAL_LB_VM_START, eventDescription = "starting internal LB VM", async = true) public VirtualRouter startInternalLbVm(final long internalLbVmId, final Account caller, final long callerUserId) throws StorageUnavailableException, InsufficientCapacityException, ConcurrentOperationException, ResourceUnavailableException { diff --git a/plugins/network-elements/internal-loadbalancer/src/test/java/org/apache/cloudstack/internallbvmmgr/InternalLBVMServiceTest.java b/plugins/network-elements/internal-loadbalancer/src/test/java/org/apache/cloudstack/internallbvmmgr/InternalLBVMServiceTest.java index afd75da50e1..fd44dcd981c 100644 --- a/plugins/network-elements/internal-loadbalancer/src/test/java/org/apache/cloudstack/internallbvmmgr/InternalLBVMServiceTest.java +++ b/plugins/network-elements/internal-loadbalancer/src/test/java/org/apache/cloudstack/internallbvmmgr/InternalLBVMServiceTest.java @@ -16,6 +16,9 @@ // under the License. package org.apache.cloudstack.internallbvmmgr; +import static org.mockito.ArgumentMatchers.anyBoolean; +import static org.mockito.ArgumentMatchers.anyLong; +import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.nullable; import java.lang.reflect.Field; @@ -24,13 +27,15 @@ import java.util.List; import javax.inject.Inject; +import com.cloud.event.ActionEventUtils; import org.apache.cloudstack.context.CallContext; import org.apache.cloudstack.network.lb.InternalLoadBalancerVMService; import org.junit.After; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; -import org.mockito.ArgumentMatchers; +import org.mockito.BDDMockito; +import org.mockito.MockedStatic; import org.mockito.Mockito; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; @@ -82,6 +87,8 @@ public class InternalLBVMServiceTest extends TestCase { @Inject AccountDao _accountDao; + private MockedStatic actionEventUtilsMocked; + long validVmId = 1L; long nonExistingVmId = 2L; long nonInternalLbVmId = 3L; @@ -105,7 +112,7 @@ public class InternalLBVMServiceTest extends TestCase { Mockito.when(_accountMgr.getSystemUser()).thenReturn(new UserVO(1)); Mockito.when(_accountMgr.getSystemAccount()).thenReturn(new AccountVO(2)); - Mockito.when(_accountDao.findByIdIncludingRemoved(ArgumentMatchers.anyLong())).thenReturn(new AccountVO(2)); + Mockito.when(_accountDao.findByIdIncludingRemoved(anyLong())).thenReturn(new AccountVO(2)); CallContext.register(_accountMgr.getSystemUser(), _accountMgr.getSystemAccount()); final DomainRouterVO validVm = @@ -120,11 +127,16 @@ public class InternalLBVMServiceTest extends TestCase { Mockito.when(_domainRouterDao.findById(validVmId)).thenReturn(validVm); Mockito.when(_domainRouterDao.findById(nonExistingVmId)).thenReturn(null); Mockito.when(_domainRouterDao.findById(nonInternalLbVmId)).thenReturn(nonInternalLbVm); + + actionEventUtilsMocked = Mockito.mockStatic(ActionEventUtils.class); + BDDMockito.given(ActionEventUtils.onStartedActionEvent(anyLong(), anyLong(), anyString(), anyString(), anyLong(), anyString(), anyBoolean(), anyLong())) + .willReturn(1L); } @Override @After public void tearDown() { + actionEventUtilsMocked.close(); CallContext.unregister(); } diff --git a/plugins/storage/volume/primera/src/main/java/org/apache/cloudstack/storage/datastore/adapter/primera/PrimeraAdapter.java b/plugins/storage/volume/primera/src/main/java/org/apache/cloudstack/storage/datastore/adapter/primera/PrimeraAdapter.java index ee6b710efa6..b574ccdb164 100644 --- a/plugins/storage/volume/primera/src/main/java/org/apache/cloudstack/storage/datastore/adapter/primera/PrimeraAdapter.java +++ b/plugins/storage/volume/primera/src/main/java/org/apache/cloudstack/storage/datastore/adapter/primera/PrimeraAdapter.java @@ -121,6 +121,10 @@ public class PrimeraAdapter implements ProviderAdapter { public void disconnect() { logger.info("PrimeraAdapter:disconnect(): closing session"); try { + //Delete session safely without triggering refreshSession + if (key != null && _client != null) { + logout(); + } _client.close(); } catch (IOException e) { logger.warn("PrimeraAdapter:refreshSession(): Error closing client connection", e); @@ -130,6 +134,40 @@ public class PrimeraAdapter implements ProviderAdapter { } return; } + /** + * Delete session directly without going through refreshSession to avoid infinite recursion + */ + private void logout() { + CloseableHttpResponse response = null; + try { + logger.debug("PrimeraAdapter:logout(): Delete session directly"); + HttpDelete request = new HttpDelete(url + "/credentials/" + key); + request.addHeader("Content-Type", "application/json"); + request.addHeader("Accept", "application/json"); + request.addHeader("X-HP3PAR-WSAPI-SessionKey", key); + + response = (CloseableHttpResponse) _client.execute(request); + final int statusCode = response.getStatusLine().getStatusCode(); + + if (statusCode == 200 || statusCode == 404) { + logger.debug("PrimeraAdapter:logout(): Session deleted successfully or was already expired"); + } else if (statusCode == 401 || statusCode == 403) { + logger.warn("PrimeraAdapter:logout(): Session already invalid or expired during deletion"); + } else { + logger.warn("PrimeraAdapter:logout(): Unexpected response when deleting session: {}", statusCode); + } + } catch (IOException e) { + logger.warn("PrimeraAdapter:logout(): Error deleting session: {}", e.getMessage()); + } finally { + if (response != null) { + try { + response.close(); + } catch (IOException e) { + logger.debug("PrimeraAdapter:logout(): Error closing response from session deletion", e); + } + } + } + } @Override public ProviderVolume create(ProviderAdapterContext context, ProviderAdapterDataObject dataIn, diff --git a/plugins/user-two-factor-authenticators/totp/src/main/java/org/apache/cloudstack/auth/TotpUserTwoFactorAuthenticator.java b/plugins/user-two-factor-authenticators/totp/src/main/java/org/apache/cloudstack/auth/TotpUserTwoFactorAuthenticator.java index c7c4997f189..b722bd14393 100644 --- a/plugins/user-two-factor-authenticators/totp/src/main/java/org/apache/cloudstack/auth/TotpUserTwoFactorAuthenticator.java +++ b/plugins/user-two-factor-authenticators/totp/src/main/java/org/apache/cloudstack/auth/TotpUserTwoFactorAuthenticator.java @@ -54,7 +54,9 @@ public class TotpUserTwoFactorAuthenticator extends AdapterBase implements UserT logger.info("2FA matches user's input"); return; } - throw new CloudTwoFactorAuthenticationException("two-factor authentication code provided is invalid"); + String msg = "two-factor authentication code provided is invalid"; + logger.error(msg); + throw new CloudTwoFactorAuthenticationException(msg); } private String get2FAKey(UserAccount userAccount) { diff --git a/server/src/main/java/com/cloud/api/query/dao/TemplateJoinDaoImpl.java b/server/src/main/java/com/cloud/api/query/dao/TemplateJoinDaoImpl.java index 2fc3056af3e..6fe3d408186 100644 --- a/server/src/main/java/com/cloud/api/query/dao/TemplateJoinDaoImpl.java +++ b/server/src/main/java/com/cloud/api/query/dao/TemplateJoinDaoImpl.java @@ -172,10 +172,10 @@ public class TemplateJoinDaoImpl extends GenericDaoBaseWithTagInformation