From a278849507f1a89d242b340e390d2b38fa14e86b Mon Sep 17 00:00:00 2001 From: Wei Zhou Date: Fri, 13 Dec 2024 09:10:54 +0100 Subject: [PATCH 1/8] server: fix typo removeaccessvpn in VirtualRouterElement (#10086) --- .../java/com/cloud/network/element/VirtualRouterElement.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/src/main/java/com/cloud/network/element/VirtualRouterElement.java b/server/src/main/java/com/cloud/network/element/VirtualRouterElement.java index 52f5273dac4..5680111ca1a 100644 --- a/server/src/main/java/com/cloud/network/element/VirtualRouterElement.java +++ b/server/src/main/java/com/cloud/network/element/VirtualRouterElement.java @@ -542,7 +542,7 @@ NetworkMigrationResponder, AggregatedCommandExecutor, RedundantResource, DnsServ // Set capabilities for vpn final Map vpnCapabilities = new HashMap(); vpnCapabilities.put(Capability.SupportedVpnProtocols, "pptp,l2tp,ipsec"); - vpnCapabilities.put(Capability.VpnTypes, "removeaccessvpn"); + vpnCapabilities.put(Capability.VpnTypes, "remoteaccessvpn"); capabilities.put(Service.Vpn, vpnCapabilities); final Map dnsCapabilities = new HashMap(); From 8639ba8b013213faff6451a61f54467615f51389 Mon Sep 17 00:00:00 2001 From: Abhishek Kumar Date: Sun, 15 Dec 2024 00:56:32 +0530 Subject: [PATCH 2/8] server: simplify role change validation (#9173) Signed-off-by: Abhishek Kumar Co-authored-by: dahn --- .../com/cloud/user/AccountManagerImpl.java | 41 ++++-- .../cloud/user/AccountManagerImplTest.java | 118 +++++++++++++++++- 2 files changed, 145 insertions(+), 14 deletions(-) diff --git a/server/src/main/java/com/cloud/user/AccountManagerImpl.java b/server/src/main/java/com/cloud/user/AccountManagerImpl.java index c21d8b830dd..2d7ebf595fd 100644 --- a/server/src/main/java/com/cloud/user/AccountManagerImpl.java +++ b/server/src/main/java/com/cloud/user/AccountManagerImpl.java @@ -120,8 +120,6 @@ import com.cloud.network.IpAddress; import com.cloud.network.IpAddressManager; import com.cloud.network.Network; import com.cloud.network.NetworkModel; -import com.cloud.network.security.SecurityGroupService; -import com.cloud.network.security.SecurityGroupVO; import com.cloud.network.VpnUserVO; import com.cloud.network.as.AutoScaleManager; import com.cloud.network.dao.AccountGuestVlanMapDao; @@ -135,6 +133,8 @@ import com.cloud.network.dao.RemoteAccessVpnVO; import com.cloud.network.dao.VpnUserDao; import com.cloud.network.router.VirtualRouter; import com.cloud.network.security.SecurityGroupManager; +import com.cloud.network.security.SecurityGroupService; +import com.cloud.network.security.SecurityGroupVO; import com.cloud.network.security.dao.SecurityGroupDao; import com.cloud.network.vpc.Vpc; import com.cloud.network.vpc.VpcManager; @@ -1301,16 +1301,33 @@ public class AccountManagerImpl extends ManagerBase implements AccountManager, M return _userAccountDao.findById(userId); } - private boolean isValidRoleChange(Account account, Role role) { - Long currentAccRoleId = account.getRoleId(); - Role currentRole = roleService.findRole(currentAccRoleId); - - if (role.getRoleType().ordinal() < currentRole.getRoleType().ordinal() && ((account.getType() == Account.Type.NORMAL && role.getRoleType().getAccountType().ordinal() > Account.Type.NORMAL.ordinal()) || - account.getType().ordinal() > Account.Type.NORMAL.ordinal() && role.getRoleType().getAccountType().ordinal() < account.getType().ordinal() && role.getRoleType().getAccountType().ordinal() > 0)) { - throw new PermissionDeniedException(String.format("Unable to update account role to %s as you are " + - "attempting to escalate the account %s to account type %s which has higher privileges", role.getName(), account.getAccountName(), role.getRoleType().getAccountType().name())); + /* + Role change should follow the below conditions: + - Caller should not be of Unknown role type + - New role's type should not be Unknown + - Caller should not be able to escalate or de-escalate an account's role which is of higher role type + - New role should not be of type Admin with domain other than ROOT domain + */ + protected void validateRoleChange(Account account, Role role, Account caller) { + Role currentRole = roleService.findRole(account.getRoleId()); + Role callerRole = roleService.findRole(caller.getRoleId()); + String errorMsg = String.format("Unable to update account role to %s, ", role.getName()); + if (RoleType.Unknown.equals(callerRole.getRoleType())) { + throw new PermissionDeniedException(String.format("%s as the caller privileges are unknown", errorMsg)); + } + if (RoleType.Unknown.equals(role.getRoleType())) { + throw new PermissionDeniedException(String.format("%s as the new role privileges are unknown", errorMsg)); + } + if (!callerRole.getRoleType().equals(RoleType.Admin) && + (role.getRoleType().ordinal() < callerRole.getRoleType().ordinal() || + currentRole.getRoleType().ordinal() < callerRole.getRoleType().ordinal())) { + throw new PermissionDeniedException(String.format("%s as either current or new role has higher " + + "privileges than the caller", errorMsg)); + } + if (role.getRoleType().equals(RoleType.Admin) && account.getDomainId() != Domain.ROOT_DOMAIN) { + throw new PermissionDeniedException(String.format("%s as the user does not belong to the ROOT domain", + errorMsg)); } - return true; } /** @@ -2053,7 +2070,7 @@ public class AccountManagerImpl extends ManagerBase implements AccountManager, M } Role role = roleService.findRole(roleId); - isValidRoleChange(account, role); + validateRoleChange(account, role, caller); acctForUpdate.setRoleId(roleId); acctForUpdate.setType(role.getRoleType().getAccountType()); checkRoleEscalation(getCurrentCallingAccount(), acctForUpdate); diff --git a/server/src/test/java/com/cloud/user/AccountManagerImplTest.java b/server/src/test/java/com/cloud/user/AccountManagerImplTest.java index a98d187b5a9..ed0a123d4a3 100644 --- a/server/src/test/java/com/cloud/user/AccountManagerImplTest.java +++ b/server/src/test/java/com/cloud/user/AccountManagerImplTest.java @@ -16,15 +16,20 @@ // under the License. package com.cloud.user; +import static org.mockito.ArgumentMatchers.nullable; + import java.net.InetAddress; import java.net.UnknownHostException; import java.util.ArrayList; import java.util.Arrays; +import java.util.HashMap; import java.util.List; import java.util.Map; -import java.util.HashMap; import org.apache.cloudstack.acl.ControlledEntity; +import org.apache.cloudstack.acl.Role; +import org.apache.cloudstack.acl.RoleService; +import org.apache.cloudstack.acl.RoleType; import org.apache.cloudstack.acl.SecurityChecker.AccessType; import org.apache.cloudstack.api.command.admin.user.GetUserKeysCmd; import org.apache.cloudstack.api.command.admin.user.UpdateUserCmd; @@ -42,7 +47,6 @@ import org.mockito.InOrder; import org.mockito.Mock; import org.mockito.Mockito; import org.mockito.junit.MockitoJUnitRunner; -import static org.mockito.ArgumentMatchers.nullable; import com.cloud.acl.DomainChecker; import com.cloud.api.auth.SetupUserTwoFactorAuthenticationCmd; @@ -102,6 +106,8 @@ public class AccountManagerImplTest extends AccountManagetImplTestBase { @Mock ConfigKey enableUserTwoFactorAuthenticationMock; + @Mock + RoleService roleService; @Before public void setUp() throws Exception { @@ -1086,4 +1092,112 @@ public class AccountManagerImplTest extends AccountManagetImplTestBase { Mockito.verify(_projectAccountDao).removeUserFromProjects(userId); } + + @Test(expected = PermissionDeniedException.class) + public void testValidateRoleChangeUnknownCaller() { + Account account = Mockito.mock(Account.class); + Mockito.when(account.getRoleId()).thenReturn(1L); + Role role = Mockito.mock(Role.class); + Mockito.when(role.getRoleType()).thenReturn(RoleType.Unknown); + Account caller = Mockito.mock(Account.class); + Mockito.when(caller.getRoleId()).thenReturn(2L); + Mockito.when(roleService.findRole(2L)).thenReturn(role); + accountManagerImpl.validateRoleChange(account, Mockito.mock(Role.class), caller); + } + + @Test(expected = PermissionDeniedException.class) + public void testValidateRoleChangeUnknownNewRole() { + Account account = Mockito.mock(Account.class); + Mockito.when(account.getRoleId()).thenReturn(1L); + Role newRole = Mockito.mock(Role.class); + Mockito.when(newRole.getRoleType()).thenReturn(RoleType.Unknown); + Role callerRole = Mockito.mock(Role.class); + Mockito.when(callerRole.getRoleType()).thenReturn(RoleType.DomainAdmin); + Account caller = Mockito.mock(Account.class); + Mockito.when(caller.getRoleId()).thenReturn(2L); + Mockito.when(roleService.findRole(2L)).thenReturn(callerRole); + accountManagerImpl.validateRoleChange(account, newRole, caller); + } + + @Test + public void testValidateRoleNewRoleSameCaller() { + Account account = Mockito.mock(Account.class); + Mockito.when(account.getRoleId()).thenReturn(1L); + Role currentRole = Mockito.mock(Role.class); + Mockito.when(currentRole.getRoleType()).thenReturn(RoleType.User); + Mockito.when(roleService.findRole(1L)).thenReturn(currentRole); + Role newRole = Mockito.mock(Role.class); + Mockito.when(newRole.getRoleType()).thenReturn(RoleType.DomainAdmin); + Role callerRole = Mockito.mock(Role.class); + Mockito.when(callerRole.getRoleType()).thenReturn(RoleType.DomainAdmin); + Account caller = Mockito.mock(Account.class); + Mockito.when(caller.getRoleId()).thenReturn(2L); + Mockito.when(roleService.findRole(2L)).thenReturn(callerRole); + accountManagerImpl.validateRoleChange(account, newRole, caller); + } + + @Test + public void testValidateRoleCurrentRoleSameCaller() { + Account account = Mockito.mock(Account.class); + Mockito.when(account.getRoleId()).thenReturn(1L); + Role accountRole = Mockito.mock(Role.class); + Mockito.when(accountRole.getRoleType()).thenReturn(RoleType.DomainAdmin); + Role newRole = Mockito.mock(Role.class); + Mockito.when(newRole.getRoleType()).thenReturn(RoleType.User); + Role callerRole = Mockito.mock(Role.class); + Mockito.when(callerRole.getRoleType()).thenReturn(RoleType.DomainAdmin); + Account caller = Mockito.mock(Account.class); + Mockito.when(caller.getRoleId()).thenReturn(2L); + Mockito.when(roleService.findRole(1L)).thenReturn(accountRole); + Mockito.when(roleService.findRole(2L)).thenReturn(callerRole); + accountManagerImpl.validateRoleChange(account, newRole, caller); + } + + @Test(expected = PermissionDeniedException.class) + public void testValidateRoleNewRoleHigherCaller() { + Account account = Mockito.mock(Account.class); + Mockito.when(account.getRoleId()).thenReturn(1L); + Role newRole = Mockito.mock(Role.class); + Mockito.when(newRole.getRoleType()).thenReturn(RoleType.Admin); + Role callerRole = Mockito.mock(Role.class); + Mockito.when(callerRole.getRoleType()).thenReturn(RoleType.DomainAdmin); + Account caller = Mockito.mock(Account.class); + Mockito.when(caller.getRoleId()).thenReturn(2L); + Mockito.when(roleService.findRole(2L)).thenReturn(callerRole); + accountManagerImpl.validateRoleChange(account, newRole, caller); + } + + @Test + public void testValidateRoleNewRoleLowerCaller() { + Account account = Mockito.mock(Account.class); + Mockito.when(account.getRoleId()).thenReturn(1L); + Role newRole = Mockito.mock(Role.class); + Mockito.when(newRole.getRoleType()).thenReturn(RoleType.User); + Role accountRole = Mockito.mock(Role.class); + Mockito.when(accountRole.getRoleType()).thenReturn(RoleType.User); + Role callerRole = Mockito.mock(Role.class); + Mockito.when(callerRole.getRoleType()).thenReturn(RoleType.DomainAdmin); + Account caller = Mockito.mock(Account.class); + Mockito.when(caller.getRoleId()).thenReturn(2L); + Mockito.when(roleService.findRole(1L)).thenReturn(accountRole); + Mockito.when(roleService.findRole(2L)).thenReturn(callerRole); + accountManagerImpl.validateRoleChange(account, newRole, caller); + } + + @Test(expected = PermissionDeniedException.class) + public void testValidateRoleAdminCannotEscalateAdminFromNonRootDomain() { + Account account = Mockito.mock(Account.class); + Mockito.when(account.getRoleId()).thenReturn(1L); + Mockito.when(account.getDomainId()).thenReturn(2L); + Role newRole = Mockito.mock(Role.class); + Mockito.when(newRole.getRoleType()).thenReturn(RoleType.Admin); + Role accountRole = Mockito.mock(Role.class); + Role callerRole = Mockito.mock(Role.class); + Mockito.when(callerRole.getRoleType()).thenReturn(RoleType.Admin); + Account caller = Mockito.mock(Account.class); + Mockito.when(caller.getRoleId()).thenReturn(2L); + Mockito.when(roleService.findRole(1L)).thenReturn(accountRole); + Mockito.when(roleService.findRole(2L)).thenReturn(callerRole); + accountManagerImpl.validateRoleChange(account, newRole, caller); + } } From a2f2e87c12095387ff2bc09b0d0fd82615d6d361 Mon Sep 17 00:00:00 2001 From: Rene Peinthor Date: Mon, 16 Dec 2024 09:59:57 +0100 Subject: [PATCH 3/8] linstor: improve heartbeat check with also asking linstor (#10105) If a node doesn't have a DRBD connection to another node, additionally ask Linstor-Controller if the node is alive. Otherwise we would have simply said no and the node might still be alive. This is always the case in a non hyperconverged setup. --- plugins/storage/volume/linstor/CHANGELOG.md | 6 +++ .../kvm/storage/LinstorStorageAdaptor.java | 16 ++++++++ .../kvm/storage/LinstorStoragePool.java | 40 ++++++++++++++++--- 3 files changed, 57 insertions(+), 5 deletions(-) diff --git a/plugins/storage/volume/linstor/CHANGELOG.md b/plugins/storage/volume/linstor/CHANGELOG.md index 930e139870f..e1ff9a5e269 100644 --- a/plugins/storage/volume/linstor/CHANGELOG.md +++ b/plugins/storage/volume/linstor/CHANGELOG.md @@ -5,6 +5,12 @@ All notable changes to Linstor CloudStack plugin will be documented in this file The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [2024-12-13] + +### Fixed + +- Linstor heartbeat check now also ask linstor-controller if there is no connection between nodes + ## [2024-10-28] ### Fixed diff --git a/plugins/storage/volume/linstor/src/main/java/com/cloud/hypervisor/kvm/storage/LinstorStorageAdaptor.java b/plugins/storage/volume/linstor/src/main/java/com/cloud/hypervisor/kvm/storage/LinstorStorageAdaptor.java index 1f71a54a4f3..90b1342c0a2 100644 --- a/plugins/storage/volume/linstor/src/main/java/com/cloud/hypervisor/kvm/storage/LinstorStorageAdaptor.java +++ b/plugins/storage/volume/linstor/src/main/java/com/cloud/hypervisor/kvm/storage/LinstorStorageAdaptor.java @@ -45,6 +45,7 @@ import com.linbit.linstor.api.Configuration; import com.linbit.linstor.api.DevelopersApi; import com.linbit.linstor.api.model.ApiCallRc; import com.linbit.linstor.api.model.ApiCallRcList; +import com.linbit.linstor.api.model.Node; import com.linbit.linstor.api.model.Properties; import com.linbit.linstor.api.model.ProviderKind; import com.linbit.linstor.api.model.Resource; @@ -712,4 +713,19 @@ public class LinstorStorageAdaptor implements StorageAdaptor { throw new CloudRuntimeException(apiEx.getBestMessage(), apiEx); } } + + public boolean isNodeOnline(LinstorStoragePool pool, String nodeName) { + DevelopersApi linstorApi = getLinstorAPI(pool); + try { + List node = linstorApi.nodeList(Collections.singletonList(nodeName), Collections.emptyList(), null, null); + if (node == null || node.isEmpty()) { + return false; + } + + return Node.ConnectionStatusEnum.ONLINE.equals(node.get(0).getConnectionStatus()); + } catch (ApiException apiEx) { + s_logger.error(apiEx.getMessage()); + throw new CloudRuntimeException(apiEx.getBestMessage(), apiEx); + } + } } diff --git a/plugins/storage/volume/linstor/src/main/java/com/cloud/hypervisor/kvm/storage/LinstorStoragePool.java b/plugins/storage/volume/linstor/src/main/java/com/cloud/hypervisor/kvm/storage/LinstorStoragePool.java index 4077d5dadfd..9a9c1a9717a 100644 --- a/plugins/storage/volume/linstor/src/main/java/com/cloud/hypervisor/kvm/storage/LinstorStoragePool.java +++ b/plugins/storage/volume/linstor/src/main/java/com/cloud/hypervisor/kvm/storage/LinstorStoragePool.java @@ -279,22 +279,52 @@ public class LinstorStoragePool implements KVMStoragePool { return sc.execute(parser); } + private boolean checkLinstorNodeOnline(String nodeName) { + return ((LinstorStorageAdaptor)_storageAdaptor).isNodeOnline(this, nodeName); + } + + /** + * Checks output of drbdsetup status output if this node has any valid connection to the specified + * otherNodeName. + * If there is no connection, ask the Linstor controller if the node is seen online and return false if not. + * If there is a connection but not connected(valid) return false. + * @param output Output of the drbdsetup status --json command + * @param otherNodeName Name of the node to check against + * @return true if we could say that this node thinks the node in question is reachable, otherwise false. + */ private boolean checkDrbdSetupStatusOutput(String output, String otherNodeName) { JsonParser jsonParser = new JsonParser(); JsonArray jResources = (JsonArray) jsonParser.parse(output); + boolean connectionFound = false; for (JsonElement jElem : jResources) { JsonObject jRes = (JsonObject) jElem; JsonArray jConnections = jRes.getAsJsonArray("connections"); for (JsonElement jConElem : jConnections) { JsonObject jConn = (JsonObject) jConElem; - if (jConn.getAsJsonPrimitive("name").getAsString().equals(otherNodeName) - && jConn.getAsJsonPrimitive("connection-state").getAsString().equalsIgnoreCase("Connected")) { - return true; + if (jConn.getAsJsonPrimitive("name").getAsString().equals(otherNodeName)) + { + connectionFound = true; + if (jConn.getAsJsonPrimitive("connection-state").getAsString() + .equalsIgnoreCase("Connected")) { + return true; + } } } } - s_logger.warn(String.format("checkDrbdSetupStatusOutput: no resource connected to %s.", otherNodeName)); - return false; + boolean otherNodeOnline = false; + if (connectionFound) { + s_logger.warn(String.format( + "checkingHeartBeat: connection found, but not in state 'Connected' to %s", otherNodeName)); + } else { + s_logger.warn(String.format( + "checkingHeartBeat: no resource connected to %s, checking LINSTOR", otherNodeName)); + otherNodeOnline = checkLinstorNodeOnline(otherNodeName); + } + s_logger.info(String.format( + "checkingHeartBeat: other node %s is %s.", + otherNodeName, + otherNodeOnline ? "online on controller" : "down")); + return otherNodeOnline; } private String executeDrbdEventsNow(OutputInterpreter.AllLinesParser parser) { From b4ad04badfbc35cd11f761866b59eb475fbd479f Mon Sep 17 00:00:00 2001 From: Suresh Kumar Anaparti Date: Wed, 18 Dec 2024 13:42:28 +0530 Subject: [PATCH 4/8] Allow config drive deletion of migrated VM, on host maintenance (#10045) --- .../java/com/cloud/vm/VmDetailConstants.java | 1 + .../com/cloud/agent/manager/AgentAttache.java | 10 ++--- .../resource/LibvirtComputingResource.java | 33 +++++++++------- .../kvm/storage/KVMStoragePoolManager.java | 3 +- .../element/ConfigDriveNetworkElement.java | 39 +++++++++++++++---- 5 files changed, 59 insertions(+), 27 deletions(-) diff --git a/api/src/main/java/com/cloud/vm/VmDetailConstants.java b/api/src/main/java/com/cloud/vm/VmDetailConstants.java index 9338cc11cd4..f7d87ab18f1 100644 --- a/api/src/main/java/com/cloud/vm/VmDetailConstants.java +++ b/api/src/main/java/com/cloud/vm/VmDetailConstants.java @@ -73,6 +73,7 @@ public interface VmDetailConstants { String ENCRYPTED_PASSWORD = "Encrypted.Password"; String CONFIG_DRIVE_LOCATION = "configDriveLocation"; + String LAST_CONFIG_DRIVE_LOCATION = "lastConfigDriveLocation"; String SKIP_DRS = "skipFromDRS"; diff --git a/engine/orchestration/src/main/java/com/cloud/agent/manager/AgentAttache.java b/engine/orchestration/src/main/java/com/cloud/agent/manager/AgentAttache.java index 2d8d6f1c48e..4d9ee0a9ab4 100644 --- a/engine/orchestration/src/main/java/com/cloud/agent/manager/AgentAttache.java +++ b/engine/orchestration/src/main/java/com/cloud/agent/manager/AgentAttache.java @@ -46,6 +46,7 @@ import com.cloud.agent.api.CleanupNetworkRulesCmd; import com.cloud.agent.api.Command; import com.cloud.agent.api.CreateStoragePoolCommand; import com.cloud.agent.api.DeleteStoragePoolCommand; +import com.cloud.agent.api.HandleConfigDriveIsoCommand; import com.cloud.agent.api.MaintainCommand; import com.cloud.agent.api.MigrateCommand; import com.cloud.agent.api.ModifySshKeysCommand; @@ -119,11 +120,10 @@ public abstract class AgentAttache { public final static String[] s_commandsAllowedInMaintenanceMode = new String[] { MaintainCommand.class.toString(), MigrateCommand.class.toString(), StopCommand.class.toString(), CheckVirtualMachineCommand.class.toString(), PingTestCommand.class.toString(), CheckHealthCommand.class.toString(), - ReadyCommand.class.toString(), ShutdownCommand.class.toString(), SetupCommand.class.toString(), - CleanupNetworkRulesCmd.class.toString(), CheckNetworkCommand.class.toString(), PvlanSetupCommand.class.toString(), CheckOnHostCommand.class.toString(), - ModifyTargetsCommand.class.toString(), ModifySshKeysCommand.class.toString(), - CreateStoragePoolCommand.class.toString(), DeleteStoragePoolCommand.class.toString(), ModifyStoragePoolCommand.class.toString(), - SetupMSListCommand.class.toString(), RollingMaintenanceCommand.class.toString(), CleanupPersistentNetworkResourceCommand.class.toString()}; + ReadyCommand.class.toString(), ShutdownCommand.class.toString(), SetupCommand.class.toString(), CleanupNetworkRulesCmd.class.toString(), + CheckNetworkCommand.class.toString(), PvlanSetupCommand.class.toString(), CheckOnHostCommand.class.toString(), ModifyTargetsCommand.class.toString(), + ModifySshKeysCommand.class.toString(), CreateStoragePoolCommand.class.toString(), DeleteStoragePoolCommand.class.toString(), ModifyStoragePoolCommand.class.toString(), + SetupMSListCommand.class.toString(), RollingMaintenanceCommand.class.toString(), CleanupPersistentNetworkResourceCommand.class.toString(), HandleConfigDriveIsoCommand.class.toString()}; protected final static String[] s_commandsNotAllowedInConnectingMode = new String[] { StartCommand.class.toString(), CreateCommand.class.toString() }; static { Arrays.sort(s_commandsAllowedInMaintenanceMode); 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 a4d7bd524ad..f9d56f8301d 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 @@ -2985,9 +2985,10 @@ public class LibvirtComputingResource extends ServerResourceBase implements Serv public static boolean useBLOCKDiskType(KVMPhysicalDisk physicalDisk) { return physicalDisk != null && - physicalDisk.getPool().getType() == StoragePoolType.Linstor && + physicalDisk.getPool() != null && + StoragePoolType.Linstor.equals(physicalDisk.getPool().getType()) && physicalDisk.getFormat() != null && - physicalDisk.getFormat()== PhysicalDiskFormat.RAW; + PhysicalDiskFormat.RAW.equals(physicalDisk.getFormat()); } public static DiskDef.DiskType getDiskType(KVMPhysicalDisk physicalDisk) { @@ -3402,13 +3403,15 @@ public class LibvirtComputingResource extends ServerResourceBase implements Serv } if (configdrive != null) { try { + s_logger.debug(String.format("Detaching ConfigDrive ISO of the VM %s, at path %s", vmName, configdrive.getDiskPath())); String result = attachOrDetachISO(conn, vmName, configdrive.getDiskPath(), false, CONFIG_DRIVE_ISO_DEVICE_ID); if (result != null) { - s_logger.warn("Detach ConfigDrive ISO with result: " + result); + s_logger.warn(String.format("Detach ConfigDrive ISO of the VM %s, at path %s with %s: ", vmName, configdrive.getDiskPath(), result)); } + s_logger.debug(String.format("Attaching ConfigDrive ISO of the VM %s, at path %s", vmName, configdrive.getDiskPath())); result = attachOrDetachISO(conn, vmName, configdrive.getDiskPath(), true, CONFIG_DRIVE_ISO_DEVICE_ID); if (result != null) { - s_logger.warn("Attach ConfigDrive ISO with result: " + result); + s_logger.warn(String.format("Attach ConfigDrive ISO of the VM %s, at path %s with %s: ", vmName, configdrive.getDiskPath(), result)); } } catch (final LibvirtException | InternalErrorException | URISyntaxException e) { final String msg = "Detach and attach ConfigDrive ISO failed due to " + e.toString(); @@ -3420,16 +3423,20 @@ public class LibvirtComputingResource extends ServerResourceBase implements Serv public synchronized String attachOrDetachISO(final Connect conn, final String vmName, String isoPath, final boolean isAttach, final Integer diskSeq) throws LibvirtException, URISyntaxException, InternalErrorException { final DiskDef iso = new DiskDef(); - if (isoPath != null && isAttach) { - final int index = isoPath.lastIndexOf("/"); - final String path = isoPath.substring(0, index); - final String name = isoPath.substring(index + 1); - final KVMStoragePool secondaryPool = storagePoolManager.getStoragePoolByURI(path); - final KVMPhysicalDisk isoVol = secondaryPool.getPhysicalDisk(name); - final DiskDef.DiskType diskType = getDiskType(isoVol); - isoPath = isoVol.getPath(); + if (isAttach && StringUtils.isNotBlank(isoPath) && isoPath.lastIndexOf("/") > 0) { + if (isoPath.startsWith(getConfigPath() + "/" + ConfigDrive.CONFIGDRIVEDIR) && isoPath.contains(vmName)) { + iso.defISODisk(isoPath, diskSeq, DiskDef.DiskType.FILE); + } else { + final int index = isoPath.lastIndexOf("/"); + final String path = isoPath.substring(0, index); + final String name = isoPath.substring(index + 1); + final KVMStoragePool storagePool = storagePoolManager.getStoragePoolByURI(path); + final KVMPhysicalDisk isoVol = storagePool.getPhysicalDisk(name); + final DiskDef.DiskType diskType = getDiskType(isoVol); + isoPath = isoVol.getPath(); - iso.defISODisk(isoPath, diskSeq, diskType); + iso.defISODisk(isoPath, diskSeq, diskType); + } } else { iso.defISODisk(null, diskSeq, DiskDef.DiskType.FILE); } diff --git a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/storage/KVMStoragePoolManager.java b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/storage/KVMStoragePoolManager.java index ee23e489951..da6a192f129 100644 --- a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/storage/KVMStoragePoolManager.java +++ b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/storage/KVMStoragePoolManager.java @@ -287,6 +287,7 @@ public class KVMStoragePoolManager { URI storageUri = null; try { + s_logger.debug("Get storage pool by uri: " + uri); storageUri = new URI(uri); } catch (URISyntaxException e) { throw new CloudRuntimeException(e.toString()); @@ -296,7 +297,7 @@ public class KVMStoragePoolManager { String uuid = null; String sourceHost = ""; StoragePoolType protocol = null; - final String scheme = storageUri.getScheme().toLowerCase(); + final String scheme = (storageUri.getScheme() != null) ? storageUri.getScheme().toLowerCase() : ""; List acceptedSchemes = List.of("nfs", "networkfilesystem", "filesystem"); if (acceptedSchemes.contains(scheme)) { sourcePath = storageUri.getPath(); diff --git a/server/src/main/java/com/cloud/network/element/ConfigDriveNetworkElement.java b/server/src/main/java/com/cloud/network/element/ConfigDriveNetworkElement.java index 83900ff2d43..38d71b9c507 100644 --- a/server/src/main/java/com/cloud/network/element/ConfigDriveNetworkElement.java +++ b/server/src/main/java/com/cloud/network/element/ConfigDriveNetworkElement.java @@ -341,10 +341,10 @@ public class ConfigDriveNetworkElement extends AdapterBase implements NetworkEle try { if (isConfigDriveIsoOnHostCache(vm.getId())) { vm.setConfigDriveLocation(Location.HOST); - configureConfigDriveData(vm, nic, dest); - - // Create the config drive on dest host cache - createConfigDriveIsoOnHostCache(vm, dest.getHost().getId()); + if (configureConfigDriveData(vm, nic, dest)) { + // Create the config drive on dest host cache + createConfigDriveIsoOnHostCache(vm, dest.getHost().getId()); + } } else { vm.setConfigDriveLocation(getConfigDriveLocation(vm.getId())); addPasswordAndUserdata(network, nic, vm, dest, context); @@ -373,7 +373,7 @@ public class ConfigDriveNetworkElement extends AdapterBase implements NetworkEle @Override public void commitMigration(NicProfile nic, Network network, VirtualMachineProfile vm, ReservationContext src, ReservationContext dst) { try { - if (isConfigDriveIsoOnHostCache(vm.getId())) { + if (isLastConfigDriveIsoOnHostCache(vm.getId())) { vm.setConfigDriveLocation(Location.HOST); // Delete the config drive on src host cache deleteConfigDriveIsoOnHostCache(vm.getVirtualMachine(), vm.getHostId()); @@ -530,6 +530,17 @@ public class ConfigDriveNetworkElement extends AdapterBase implements NetworkEle return false; } + private boolean isLastConfigDriveIsoOnHostCache(long vmId) { + final UserVmDetailVO vmDetailLastConfigDriveLocation = _userVmDetailsDao.findDetail(vmId, VmDetailConstants.LAST_CONFIG_DRIVE_LOCATION); + if (vmDetailLastConfigDriveLocation == null) { + return isConfigDriveIsoOnHostCache(vmId); + } + if (Location.HOST.toString().equalsIgnoreCase(vmDetailLastConfigDriveLocation.getValue())) { + return true; + } + return false; + } + private boolean createConfigDriveIsoOnHostCache(VirtualMachineProfile profile, Long hostId) throws ResourceUnavailableException { if (hostId == null) { throw new ResourceUnavailableException("Config drive iso creation failed, dest host not available", @@ -556,7 +567,7 @@ public class ConfigDriveNetworkElement extends AdapterBase implements NetworkEle } profile.setConfigDriveLocation(answer.getConfigDriveLocation()); - _userVmDetailsDao.addDetail(profile.getId(), VmDetailConstants.CONFIG_DRIVE_LOCATION, answer.getConfigDriveLocation().toString(), false); + updateConfigDriveLocationInVMDetails(profile.getId(), answer.getConfigDriveLocation()); addConfigDriveDisk(profile, null); return true; } @@ -618,11 +629,23 @@ public class ConfigDriveNetworkElement extends AdapterBase implements NetworkEle answer.getDetails()), ConfigDriveNetworkElement.class, 0L); } profile.setConfigDriveLocation(answer.getConfigDriveLocation()); - _userVmDetailsDao.addDetail(profile.getId(), VmDetailConstants.CONFIG_DRIVE_LOCATION, answer.getConfigDriveLocation().toString(), false); + updateConfigDriveLocationInVMDetails(profile.getId(), answer.getConfigDriveLocation()); addConfigDriveDisk(profile, dataStore); return true; } + private void updateConfigDriveLocationInVMDetails(long vmId, NetworkElement.Location configDriveLocation) { + final UserVmDetailVO vmDetailConfigDriveLocation = _userVmDetailsDao.findDetail(vmId, VmDetailConstants.CONFIG_DRIVE_LOCATION); + if (vmDetailConfigDriveLocation != null) { + if (!configDriveLocation.toString().equalsIgnoreCase(vmDetailConfigDriveLocation.getValue())) { + _userVmDetailsDao.addDetail(vmId, VmDetailConstants.LAST_CONFIG_DRIVE_LOCATION, vmDetailConfigDriveLocation.getValue(), false); + } else { + _userVmDetailsDao.removeDetail(vmId, VmDetailConstants.LAST_CONFIG_DRIVE_LOCATION); + } + } + _userVmDetailsDao.addDetail(vmId, VmDetailConstants.CONFIG_DRIVE_LOCATION, configDriveLocation.toString(), false); + } + private Map getVMCustomUserdataParamMap(long vmId) { UserVmVO userVm = _userVmDao.findById(vmId); String userDataDetails = userVm.getUserDataDetails(); @@ -737,7 +760,7 @@ public class ConfigDriveNetworkElement extends AdapterBase implements NetworkEle private boolean configureConfigDriveData(final VirtualMachineProfile profile, final NicProfile nic, final DeployDestination dest) { final UserVmVO vm = _userVmDao.findById(profile.getId()); - if (vm.getType() != VirtualMachine.Type.User) { + if (vm == null || vm.getType() != VirtualMachine.Type.User) { return false; } final Nic defaultNic = _networkModel.getDefaultNic(vm.getId()); From a9587bfd2e33c52151216c624aee8f09a4ca2f4c Mon Sep 17 00:00:00 2001 From: Rene Peinthor Date: Wed, 18 Dec 2024 09:13:41 +0100 Subject: [PATCH 5/8] kvm-storage: provide isVMMigrate information to storage plugins (#10093) Particular Linstor needs can use this information to only allow dual volume access for live migration and not enable it in general, which can and will lead to data corruption if for some reason 2 VMs get started on 2 different hosts. --- ...ibvirtPrepareForMigrationCommandWrapper.java | 2 +- .../wrapper/LibvirtStartCommandWrapper.java | 2 +- .../kvm/storage/IscsiAdmStorageAdaptor.java | 2 +- .../kvm/storage/IscsiAdmStoragePool.java | 2 +- .../kvm/storage/KVMStoragePoolManager.java | 6 +++--- .../kvm/storage/LibvirtStorageAdaptor.java | 2 +- .../kvm/storage/ManagedNfsStorageAdaptor.java | 2 +- .../kvm/storage/MultipathSCSIAdapterBase.java | 2 +- .../kvm/storage/MultipathSCSIPool.java | 2 +- .../kvm/storage/ScaleIOStorageAdaptor.java | 4 ++-- .../kvm/storage/ScaleIOStoragePool.java | 2 +- .../hypervisor/kvm/storage/StorageAdaptor.java | 11 +++++++++-- .../resource/LibvirtComputingResourceTest.java | 10 +++++----- .../kvm/storage/ScaleIOStoragePoolTest.java | 2 +- plugins/storage/volume/linstor/CHANGELOG.md | 6 ++++++ .../kvm/storage/LinstorStorageAdaptor.java | 17 ++++++++++------- .../kvm/storage/LinstorStoragePool.java | 2 +- .../kvm/storage/StorPoolStorageAdaptor.java | 2 +- .../kvm/storage/StorPoolStoragePool.java | 2 +- 19 files changed, 48 insertions(+), 32 deletions(-) diff --git a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtPrepareForMigrationCommandWrapper.java b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtPrepareForMigrationCommandWrapper.java index 77333f87140..ebb5a64263e 100644 --- a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtPrepareForMigrationCommandWrapper.java +++ b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtPrepareForMigrationCommandWrapper.java @@ -120,7 +120,7 @@ public final class LibvirtPrepareForMigrationCommandWrapper extends CommandWrapp skipDisconnect = true; - if (!storagePoolMgr.connectPhysicalDisksViaVmSpec(vm)) { + if (!storagePoolMgr.connectPhysicalDisksViaVmSpec(vm, true)) { return new PrepareForMigrationAnswer(command, "failed to connect physical disks to host"); } diff --git a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtStartCommandWrapper.java b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtStartCommandWrapper.java index 7b69993f2e5..92ebf39d396 100644 --- a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtStartCommandWrapper.java +++ b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtStartCommandWrapper.java @@ -77,7 +77,7 @@ public final class LibvirtStartCommandWrapper extends CommandWrapper details) { + public boolean connectPhysicalDisk(String volumeUuid, KVMStoragePool pool, Map details, boolean isVMMigrate) { // ex. sudo iscsiadm -m node -T iqn.2012-03.com.test:volume1 -p 192.168.233.10:3260 -o new Script iScsiAdmCmd = new Script(true, "iscsiadm", 0, s_logger); diff --git a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/storage/IscsiAdmStoragePool.java b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/storage/IscsiAdmStoragePool.java index a89650f6eb6..f5bfd898a4f 100644 --- a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/storage/IscsiAdmStoragePool.java +++ b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/storage/IscsiAdmStoragePool.java @@ -106,7 +106,7 @@ public class IscsiAdmStoragePool implements KVMStoragePool { @Override public boolean connectPhysicalDisk(String name, Map details) { - return this._storageAdaptor.connectPhysicalDisk(name, this, details); + return this._storageAdaptor.connectPhysicalDisk(name, this, details, false); } @Override diff --git a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/storage/KVMStoragePoolManager.java b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/storage/KVMStoragePoolManager.java index da6a192f129..2bd068b6c8f 100644 --- a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/storage/KVMStoragePoolManager.java +++ b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/storage/KVMStoragePoolManager.java @@ -133,10 +133,10 @@ public class KVMStoragePoolManager { StorageAdaptor adaptor = getStorageAdaptor(type); KVMStoragePool pool = adaptor.getStoragePool(poolUuid); - return adaptor.connectPhysicalDisk(volPath, pool, details); + return adaptor.connectPhysicalDisk(volPath, pool, details, false); } - public boolean connectPhysicalDisksViaVmSpec(VirtualMachineTO vmSpec) { + public boolean connectPhysicalDisksViaVmSpec(VirtualMachineTO vmSpec, boolean isVMMigrate) { boolean result = false; final String vmName = vmSpec.getName(); @@ -159,7 +159,7 @@ public class KVMStoragePoolManager { KVMStoragePool pool = getStoragePool(store.getPoolType(), store.getUuid()); StorageAdaptor adaptor = getStorageAdaptor(pool.getType()); - result = adaptor.connectPhysicalDisk(vol.getPath(), pool, disk.getDetails()); + result = adaptor.connectPhysicalDisk(vol.getPath(), pool, disk.getDetails(), isVMMigrate); if (!result) { s_logger.error("Failed to connect disks via vm spec for vm: " + vmName + " volume:" + vol.toString()); diff --git a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/storage/LibvirtStorageAdaptor.java b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/storage/LibvirtStorageAdaptor.java index 11375969b6e..80a810c6313 100644 --- a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/storage/LibvirtStorageAdaptor.java +++ b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/storage/LibvirtStorageAdaptor.java @@ -1011,7 +1011,7 @@ public class LibvirtStorageAdaptor implements StorageAdaptor { } @Override - public boolean connectPhysicalDisk(String name, KVMStoragePool pool, Map details) { + public boolean connectPhysicalDisk(String name, KVMStoragePool pool, Map details, boolean isVMMigrate) { // this is for managed storage that needs to prep disks prior to use return true; } diff --git a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/storage/ManagedNfsStorageAdaptor.java b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/storage/ManagedNfsStorageAdaptor.java index cb285383b9b..48e556fd08c 100644 --- a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/storage/ManagedNfsStorageAdaptor.java +++ b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/storage/ManagedNfsStorageAdaptor.java @@ -93,7 +93,7 @@ public class ManagedNfsStorageAdaptor implements StorageAdaptor { * creates a nfs storage pool using libvirt */ @Override - public boolean connectPhysicalDisk(String volumeUuid, KVMStoragePool pool, Map details) { + public boolean connectPhysicalDisk(String volumeUuid, KVMStoragePool pool, Map details, boolean isVMMigrate) { StoragePool sp = null; Connect conn = null; diff --git a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/storage/MultipathSCSIAdapterBase.java b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/storage/MultipathSCSIAdapterBase.java index 0d58bdaa177..558a5269ef4 100644 --- a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/storage/MultipathSCSIAdapterBase.java +++ b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/storage/MultipathSCSIAdapterBase.java @@ -181,7 +181,7 @@ public abstract class MultipathSCSIAdapterBase implements StorageAdaptor { } @Override - public boolean connectPhysicalDisk(String volumePath, KVMStoragePool pool, Map details) { + public boolean connectPhysicalDisk(String volumePath, KVMStoragePool pool, Map details, boolean isVMMigrate) { LOGGER.info("connectPhysicalDisk called for [" + volumePath + "]"); if (StringUtils.isEmpty(volumePath)) { diff --git a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/storage/MultipathSCSIPool.java b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/storage/MultipathSCSIPool.java index bc2f072f719..229481b1f79 100644 --- a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/storage/MultipathSCSIPool.java +++ b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/storage/MultipathSCSIPool.java @@ -78,7 +78,7 @@ public class MultipathSCSIPool implements KVMStoragePool { @Override public boolean connectPhysicalDisk(String volumeUuid, Map details) { - return storageAdaptor.connectPhysicalDisk(volumeUuid, this, details); + return storageAdaptor.connectPhysicalDisk(volumeUuid, this, details, false); } @Override diff --git a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/storage/ScaleIOStorageAdaptor.java b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/storage/ScaleIOStorageAdaptor.java index bb5ba08a421..ab9533c075e 100644 --- a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/storage/ScaleIOStorageAdaptor.java +++ b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/storage/ScaleIOStorageAdaptor.java @@ -178,7 +178,7 @@ public class ScaleIOStorageAdaptor implements StorageAdaptor { return null; } - if(!connectPhysicalDisk(name, pool, null)) { + if(!connectPhysicalDisk(name, pool, null, false)) { throw new CloudRuntimeException(String.format("Failed to ensure disk %s was present", name)); } @@ -221,7 +221,7 @@ public class ScaleIOStorageAdaptor implements StorageAdaptor { } @Override - public boolean connectPhysicalDisk(String volumePath, KVMStoragePool pool, Map details) { + public boolean connectPhysicalDisk(String volumePath, KVMStoragePool pool, Map details, boolean isMigration) { if (StringUtils.isEmpty(volumePath) || pool == null) { LOGGER.error("Unable to connect physical disk due to insufficient data"); throw new CloudRuntimeException("Unable to connect physical disk due to insufficient data"); diff --git a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/storage/ScaleIOStoragePool.java b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/storage/ScaleIOStoragePool.java index 77f21910da6..e8243c3f7cf 100644 --- a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/storage/ScaleIOStoragePool.java +++ b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/storage/ScaleIOStoragePool.java @@ -89,7 +89,7 @@ public class ScaleIOStoragePool implements KVMStoragePool { @Override public boolean connectPhysicalDisk(String volumeUuid, Map details) { - return storageAdaptor.connectPhysicalDisk(volumeUuid, this, details); + return storageAdaptor.connectPhysicalDisk(volumeUuid, this, details, false); } @Override diff --git a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/storage/StorageAdaptor.java b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/storage/StorageAdaptor.java index d1a1ed9a2ec..12c20c735c1 100644 --- a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/storage/StorageAdaptor.java +++ b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/storage/StorageAdaptor.java @@ -50,8 +50,15 @@ public interface StorageAdaptor { public KVMPhysicalDisk createPhysicalDisk(String name, KVMStoragePool pool, PhysicalDiskFormat format, Storage.ProvisioningType provisioningType, long size, byte[] passphrase); - // given disk path (per database) and pool, prepare disk on host - public boolean connectPhysicalDisk(String volumePath, KVMStoragePool pool, Map details); + /** + * given disk path (per database) and pool, prepare disk on host + * @param volumePath volume path + * @param pool storage pool the disk is part of + * @param details disk details map + * @param isVMMigrate Indicates if request is while VM is migration + * @return true if connect was a success + */ + public boolean connectPhysicalDisk(String volumePath, KVMStoragePool pool, Map details, boolean isVMMigrate); // given disk path (per database) and pool, clean up disk on host public boolean disconnectPhysicalDisk(String volumePath, KVMStoragePool pool); diff --git a/plugins/hypervisors/kvm/src/test/java/com/cloud/hypervisor/kvm/resource/LibvirtComputingResourceTest.java b/plugins/hypervisors/kvm/src/test/java/com/cloud/hypervisor/kvm/resource/LibvirtComputingResourceTest.java index 17bff8325ce..bbd1f8a73f2 100644 --- a/plugins/hypervisors/kvm/src/test/java/com/cloud/hypervisor/kvm/resource/LibvirtComputingResourceTest.java +++ b/plugins/hypervisors/kvm/src/test/java/com/cloud/hypervisor/kvm/resource/LibvirtComputingResourceTest.java @@ -1549,7 +1549,7 @@ public class LibvirtComputingResourceTest { when(libvirtComputingResourceMock.getVifDriver(nicTO.getType(), nicTO.getName())).thenReturn(vifDriver); when(libvirtComputingResourceMock.getStoragePoolMgr()).thenReturn(storagePoolManager); - when(storagePoolManager.connectPhysicalDisksViaVmSpec(vm)).thenReturn(true); + when(storagePoolManager.connectPhysicalDisksViaVmSpec(vm, true)).thenReturn(true); final LibvirtRequestWrapper wrapper = LibvirtRequestWrapper.getInstance(); assertNotNull(wrapper); @@ -5095,7 +5095,7 @@ public class LibvirtComputingResourceTest { fail(e.getMessage()); } - when(storagePoolMgr.connectPhysicalDisksViaVmSpec(vmSpec)).thenReturn(false); + when(storagePoolMgr.connectPhysicalDisksViaVmSpec(vmSpec, false)).thenReturn(false); final LibvirtRequestWrapper wrapper = LibvirtRequestWrapper.getInstance(); assertNotNull(wrapper); @@ -5296,7 +5296,7 @@ public class LibvirtComputingResourceTest { fail(e.getMessage()); } - when(storagePoolMgr.connectPhysicalDisksViaVmSpec(vmSpec)).thenReturn(true); + when(storagePoolMgr.connectPhysicalDisksViaVmSpec(vmSpec, false)).thenReturn(true); try { doNothing().when(libvirtComputingResourceMock).createVifs(vmSpec, vmDef); @@ -5375,7 +5375,7 @@ public class LibvirtComputingResourceTest { fail(e.getMessage()); } - when(storagePoolMgr.connectPhysicalDisksViaVmSpec(vmSpec)).thenReturn(true); + when(storagePoolMgr.connectPhysicalDisksViaVmSpec(vmSpec, false)).thenReturn(true); try { doNothing().when(libvirtComputingResourceMock).createVifs(vmSpec, vmDef); @@ -5452,7 +5452,7 @@ public class LibvirtComputingResourceTest { fail(e.getMessage()); } - when(storagePoolMgr.connectPhysicalDisksViaVmSpec(vmSpec)).thenReturn(true); + when(storagePoolMgr.connectPhysicalDisksViaVmSpec(vmSpec, false)).thenReturn(true); final LibvirtRequestWrapper wrapper = LibvirtRequestWrapper.getInstance(); assertNotNull(wrapper); diff --git a/plugins/hypervisors/kvm/src/test/java/com/cloud/hypervisor/kvm/storage/ScaleIOStoragePoolTest.java b/plugins/hypervisors/kvm/src/test/java/com/cloud/hypervisor/kvm/storage/ScaleIOStoragePoolTest.java index 492bc275a3f..bae8f3ac0ed 100644 --- a/plugins/hypervisors/kvm/src/test/java/com/cloud/hypervisor/kvm/storage/ScaleIOStoragePoolTest.java +++ b/plugins/hypervisors/kvm/src/test/java/com/cloud/hypervisor/kvm/storage/ScaleIOStoragePoolTest.java @@ -195,7 +195,7 @@ public class ScaleIOStoragePoolTest { when(adapter.getPhysicalDisk(volumeId, pool)).thenReturn(disk); - final boolean result = adapter.connectPhysicalDisk(volumePath, pool, null); + final boolean result = adapter.connectPhysicalDisk(volumePath, pool, null, false); assertTrue(result); } } diff --git a/plugins/storage/volume/linstor/CHANGELOG.md b/plugins/storage/volume/linstor/CHANGELOG.md index e1ff9a5e269..957377e2978 100644 --- a/plugins/storage/volume/linstor/CHANGELOG.md +++ b/plugins/storage/volume/linstor/CHANGELOG.md @@ -11,6 +11,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Linstor heartbeat check now also ask linstor-controller if there is no connection between nodes +## [2024-12-11] + +### Fixed + +- Only set allow-two-primaries if a live migration is performed + ## [2024-10-28] ### Fixed diff --git a/plugins/storage/volume/linstor/src/main/java/com/cloud/hypervisor/kvm/storage/LinstorStorageAdaptor.java b/plugins/storage/volume/linstor/src/main/java/com/cloud/hypervisor/kvm/storage/LinstorStorageAdaptor.java index 90b1342c0a2..9b7a376e8f2 100644 --- a/plugins/storage/volume/linstor/src/main/java/com/cloud/hypervisor/kvm/storage/LinstorStorageAdaptor.java +++ b/plugins/storage/volume/linstor/src/main/java/com/cloud/hypervisor/kvm/storage/LinstorStorageAdaptor.java @@ -274,6 +274,7 @@ public class LinstorStorageAdaptor implements StorageAdaptor { * @throws ApiException if any problem connecting to the Linstor controller */ private void allow2PrimariesIfInUse(DevelopersApi api, String rscName) throws ApiException { + s_logger.debug("enabling allow-two-primaries"); String inUseNode = LinstorUtil.isResourceInUse(api, rscName); if (inUseNode != null && !inUseNode.equalsIgnoreCase(localNodeName)) { // allow 2 primaries for live migration, should be removed by disconnect on the other end @@ -289,7 +290,8 @@ public class LinstorStorageAdaptor implements StorageAdaptor { } @Override - public boolean connectPhysicalDisk(String volumePath, KVMStoragePool pool, Map details) + public boolean connectPhysicalDisk( + String volumePath, KVMStoragePool pool, Map details, boolean isVMMigration) { s_logger.debug(String.format("Linstor: connectPhysicalDisk %s:%s -> %s", pool.getUuid(), volumePath, details)); if (volumePath == null) { @@ -312,12 +314,13 @@ public class LinstorStorageAdaptor implements StorageAdaptor { throw new CloudRuntimeException(apiEx.getBestMessage(), apiEx); } - try - { - allow2PrimariesIfInUse(api, rscName); - } catch (ApiException apiEx) { - s_logger.error(apiEx); - // do not fail here as adding allow-two-primaries property is only a problem while live migrating + if (isVMMigration) { + try { + allow2PrimariesIfInUse(api, rscName); + } catch (ApiException apiEx) { + s_logger.error(apiEx); + // do not fail here as adding allow-two-primaries property is only a problem while live migrating + } } return true; } diff --git a/plugins/storage/volume/linstor/src/main/java/com/cloud/hypervisor/kvm/storage/LinstorStoragePool.java b/plugins/storage/volume/linstor/src/main/java/com/cloud/hypervisor/kvm/storage/LinstorStoragePool.java index 9a9c1a9717a..3907bae7f79 100644 --- a/plugins/storage/volume/linstor/src/main/java/com/cloud/hypervisor/kvm/storage/LinstorStoragePool.java +++ b/plugins/storage/volume/linstor/src/main/java/com/cloud/hypervisor/kvm/storage/LinstorStoragePool.java @@ -74,7 +74,7 @@ public class LinstorStoragePool implements KVMStoragePool { @Override public boolean connectPhysicalDisk(String volumeUuid, Map details) { - return _storageAdaptor.connectPhysicalDisk(volumeUuid, this, details); + return _storageAdaptor.connectPhysicalDisk(volumeUuid, this, details, false); } @Override diff --git a/plugins/storage/volume/storpool/src/main/java/com/cloud/hypervisor/kvm/storage/StorPoolStorageAdaptor.java b/plugins/storage/volume/storpool/src/main/java/com/cloud/hypervisor/kvm/storage/StorPoolStorageAdaptor.java index c7711c05ef5..4091be10470 100644 --- a/plugins/storage/volume/storpool/src/main/java/com/cloud/hypervisor/kvm/storage/StorPoolStorageAdaptor.java +++ b/plugins/storage/volume/storpool/src/main/java/com/cloud/hypervisor/kvm/storage/StorPoolStorageAdaptor.java @@ -244,7 +244,7 @@ public class StorPoolStorageAdaptor implements StorageAdaptor { } @Override - public boolean connectPhysicalDisk(String volumeUuid, KVMStoragePool pool, Map details) { + public boolean connectPhysicalDisk(String volumeUuid, KVMStoragePool pool, Map details, boolean isVMMigrate) { SP_LOG("StorPoolStorageAdaptor.connectPhysicalDisk: uuid=%s, pool=%s", volumeUuid, pool); log.debug(String.format("connectPhysicalDisk: uuid=%s, pool=%s", volumeUuid, pool)); diff --git a/plugins/storage/volume/storpool/src/main/java/com/cloud/hypervisor/kvm/storage/StorPoolStoragePool.java b/plugins/storage/volume/storpool/src/main/java/com/cloud/hypervisor/kvm/storage/StorPoolStoragePool.java index 02095503c3b..953b55b6e9a 100644 --- a/plugins/storage/volume/storpool/src/main/java/com/cloud/hypervisor/kvm/storage/StorPoolStoragePool.java +++ b/plugins/storage/volume/storpool/src/main/java/com/cloud/hypervisor/kvm/storage/StorPoolStoragePool.java @@ -131,7 +131,7 @@ public class StorPoolStoragePool implements KVMStoragePool { @Override public boolean connectPhysicalDisk(String name, Map details) { - return _storageAdaptor.connectPhysicalDisk(name, this, details); + return _storageAdaptor.connectPhysicalDisk(name, this, details, false); } @Override From 0944fa1c9cf20e00b21a8568e72624532f137b81 Mon Sep 17 00:00:00 2001 From: dahn Date: Thu, 19 Dec 2024 12:07:21 +0100 Subject: [PATCH 6/8] set ulimit for server according to redhat spec (#10040) --- packaging/centos7/cloud.spec | 3 +++ packaging/centos7/filelimit.conf | 20 ++++++++++++++++++++ packaging/centos8/cloud.spec | 3 +++ packaging/centos8/filelimit.conf | 20 ++++++++++++++++++++ 4 files changed, 46 insertions(+) create mode 100644 packaging/centos7/filelimit.conf create mode 100644 packaging/centos8/filelimit.conf diff --git a/packaging/centos7/cloud.spec b/packaging/centos7/cloud.spec index 25eba6495c1..e42a234bbc7 100644 --- a/packaging/centos7/cloud.spec +++ b/packaging/centos7/cloud.spec @@ -261,6 +261,7 @@ mkdir -p ${RPM_BUILD_ROOT}%{_datadir}/%{name}-management/lib mkdir -p ${RPM_BUILD_ROOT}%{_datadir}/%{name}-management/setup mkdir -p ${RPM_BUILD_ROOT}%{_localstatedir}/log/%{name}/management mkdir -p ${RPM_BUILD_ROOT}%{_sysconfdir}/%{name}/management +mkdir -p ${RPM_BUILD_ROOT}%{_sysconfdir}/systemd/system/%{name}-management.service.d mkdir -p ${RPM_BUILD_ROOT}%{_localstatedir}/run mkdir -p ${RPM_BUILD_ROOT}%{_datadir}/%{name}-management/setup/wheel @@ -309,6 +310,7 @@ install -D utils/target/cloud-utils-%{_maventag}-bundled.jar ${RPM_BUILD_ROOT}%{ install -D packaging/centos7/cloud-ipallocator.rc ${RPM_BUILD_ROOT}%{_initrddir}/%{name}-ipallocator install -D packaging/centos7/cloud.limits ${RPM_BUILD_ROOT}%{_sysconfdir}/security/limits.d/cloud +install -D packaging/centos7/filelimit.conf ${RPM_BUILD_ROOT}%{_sysconfdir}/systemd/system/%{name}-management.service.d install -D packaging/systemd/cloudstack-management.service ${RPM_BUILD_ROOT}%{_unitdir}/%{name}-management.service install -D packaging/systemd/cloudstack-management.default ${RPM_BUILD_ROOT}%{_sysconfdir}/default/%{name}-management install -D server/target/conf/cloudstack-sudoers ${RPM_BUILD_ROOT}%{_sysconfdir}/sudoers.d/%{name}-management @@ -591,6 +593,7 @@ pip3 install --upgrade urllib3 %config(noreplace) %{_sysconfdir}/default/%{name}-management %config(noreplace) %{_sysconfdir}/sudoers.d/%{name}-management %config(noreplace) %{_sysconfdir}/security/limits.d/cloud +%config(noreplace) %{_sysconfdir}/systemd/system/%{name}-management.service.d %config(noreplace) %attr(0640,root,cloud) %{_sysconfdir}/%{name}/management/db.properties %config(noreplace) %attr(0640,root,cloud) %{_sysconfdir}/%{name}/management/server.properties %config(noreplace) %attr(0640,root,cloud) %{_sysconfdir}/%{name}/management/config.json diff --git a/packaging/centos7/filelimit.conf b/packaging/centos7/filelimit.conf new file mode 100644 index 00000000000..7a8a835e3fe --- /dev/null +++ b/packaging/centos7/filelimit.conf @@ -0,0 +1,20 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +# should go in /etc/systemd/system/cloudstack-management.service.d/ +[Service] +LimitNPROC=infinity diff --git a/packaging/centos8/cloud.spec b/packaging/centos8/cloud.spec index c1277823b2c..f890d303a72 100644 --- a/packaging/centos8/cloud.spec +++ b/packaging/centos8/cloud.spec @@ -243,6 +243,7 @@ mkdir -p ${RPM_BUILD_ROOT}%{_datadir}/%{name}-management/lib mkdir -p ${RPM_BUILD_ROOT}%{_datadir}/%{name}-management/setup mkdir -p ${RPM_BUILD_ROOT}%{_localstatedir}/log/%{name}/management mkdir -p ${RPM_BUILD_ROOT}%{_sysconfdir}/%{name}/management +mkdir -p ${RPM_BUILD_ROOT}%{_sysconfdir}/systemd/system/%{name}-management.service.d mkdir -p ${RPM_BUILD_ROOT}%{_localstatedir}/run mkdir -p ${RPM_BUILD_ROOT}%{_datadir}/%{name}-management/setup/wheel @@ -291,6 +292,7 @@ install -D utils/target/cloud-utils-%{_maventag}-bundled.jar ${RPM_BUILD_ROOT}%{ install -D packaging/centos8/cloud-ipallocator.rc ${RPM_BUILD_ROOT}%{_initrddir}/%{name}-ipallocator install -D packaging/centos8/cloud.limits ${RPM_BUILD_ROOT}%{_sysconfdir}/security/limits.d/cloud +install -D packaging/centos8/filelimit.conf ${RPM_BUILD_ROOT}%{_sysconfdir}/systemd/system/%{name}-management.service.d install -D packaging/systemd/cloudstack-management.service ${RPM_BUILD_ROOT}%{_unitdir}/%{name}-management.service install -D packaging/systemd/cloudstack-management.default ${RPM_BUILD_ROOT}%{_sysconfdir}/default/%{name}-management install -D server/target/conf/cloudstack-sudoers ${RPM_BUILD_ROOT}%{_sysconfdir}/sudoers.d/%{name}-management @@ -570,6 +572,7 @@ pip install --upgrade /usr/share/cloudstack-marvin/Marvin-*.tar.gz %config(noreplace) %{_sysconfdir}/default/%{name}-management %config(noreplace) %{_sysconfdir}/sudoers.d/%{name}-management %config(noreplace) %{_sysconfdir}/security/limits.d/cloud +%config(noreplace) %{_sysconfdir}/systemd/system/%{name}-management.service.d %config(noreplace) %attr(0640,root,cloud) %{_sysconfdir}/%{name}/management/db.properties %config(noreplace) %attr(0640,root,cloud) %{_sysconfdir}/%{name}/management/server.properties %config(noreplace) %attr(0640,root,cloud) %{_sysconfdir}/%{name}/management/config.json diff --git a/packaging/centos8/filelimit.conf b/packaging/centos8/filelimit.conf new file mode 100644 index 00000000000..7a8a835e3fe --- /dev/null +++ b/packaging/centos8/filelimit.conf @@ -0,0 +1,20 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +# should go in /etc/systemd/system/cloudstack-management.service.d/ +[Service] +LimitNPROC=infinity From 188eacd9eb165a86dd5328c34c44a424092819a0 Mon Sep 17 00:00:00 2001 From: Suresh Kumar Anaparti Date: Fri, 20 Dec 2024 10:18:11 +0530 Subject: [PATCH 7/8] Certificate and VM hostname validation improvements (#10051) * Certificate and VM hostname validation improvements * Improve certificate name validation and some code/log improvements --- .../LibvirtGetVmIpAddressCommandWrapper.java | 3 +++ ...pDirectDownloadCertificateCommandWrapper.java | 4 ++++ .../CitrixGetVmIpAddressCommandWrapper.java | 16 +++++++--------- .../java/com/cloud/vm/UserVmManagerImpl.java | 12 +++++------- .../download/DirectDownloadManagerImpl.java | 11 ++++++++++- .../main/java/com/cloud/utils/net/NetUtils.java | 10 +++++++--- 6 files changed, 36 insertions(+), 20 deletions(-) diff --git a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtGetVmIpAddressCommandWrapper.java b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtGetVmIpAddressCommandWrapper.java index da2839d9cee..d65b6907eeb 100644 --- a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtGetVmIpAddressCommandWrapper.java +++ b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtGetVmIpAddressCommandWrapper.java @@ -42,6 +42,9 @@ public final class LibvirtGetVmIpAddressCommandWrapper extends CommandWrapper commands = new ArrayList<>(); diff --git a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtSetupDirectDownloadCertificateCommandWrapper.java b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtSetupDirectDownloadCertificateCommandWrapper.java index 0774d306b8a..d2b69412a72 100644 --- a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtSetupDirectDownloadCertificateCommandWrapper.java +++ b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtSetupDirectDownloadCertificateCommandWrapper.java @@ -37,6 +37,7 @@ import com.cloud.resource.ResourceWrapper; import com.cloud.utils.FileUtil; import com.cloud.utils.PropertiesUtil; import com.cloud.utils.exception.CloudRuntimeException; +import com.cloud.utils.net.NetUtils; import com.cloud.utils.script.Script; @ResourceWrapper(handles = SetupDirectDownloadCertificateCommand.class) @@ -132,6 +133,9 @@ public class LibvirtSetupDirectDownloadCertificateCommandWrapper extends Command public Answer execute(SetupDirectDownloadCertificateCommand cmd, LibvirtComputingResource serverResource) { String certificate = cmd.getCertificate(); String certificateName = cmd.getCertificateName(); + if (!NetUtils.verifyDomainNameLabel(certificateName, false)) { + return new Answer(cmd, false, "The provided certificate name is invalid"); + } try { File agentFile = getAgentPropertiesFile(); diff --git a/plugins/hypervisors/xenserver/src/main/java/com/cloud/hypervisor/xenserver/resource/wrapper/xenbase/CitrixGetVmIpAddressCommandWrapper.java b/plugins/hypervisors/xenserver/src/main/java/com/cloud/hypervisor/xenserver/resource/wrapper/xenbase/CitrixGetVmIpAddressCommandWrapper.java index b67ef0850ba..e03708faf86 100644 --- a/plugins/hypervisors/xenserver/src/main/java/com/cloud/hypervisor/xenserver/resource/wrapper/xenbase/CitrixGetVmIpAddressCommandWrapper.java +++ b/plugins/hypervisors/xenserver/src/main/java/com/cloud/hypervisor/xenserver/resource/wrapper/xenbase/CitrixGetVmIpAddressCommandWrapper.java @@ -63,20 +63,18 @@ public final class CitrixGetVmIpAddressCommandWrapper extends CommandWrapper> uploadCertificateToHosts( String certificateCer, String alias, String hypervisor, Long zoneId, Long hostId) { - if (alias != null && (alias.equalsIgnoreCase("cloud") || alias.startsWith("cloudca"))) { + if (StringUtils.isBlank(alias)) { + throw new CloudRuntimeException("Certificate name not provided, please provide a valid name"); + } + + if (alias.equalsIgnoreCase("cloud") || alias.startsWith("cloudca")) { throw new CloudRuntimeException("Please provide a different alias name for the certificate"); } + if (!NetUtils.verifyDomainNameLabel(alias, false)) { + throw new CloudRuntimeException("The provided certificate name is invalid, please provide a valid name"); + } + List hosts; DirectDownloadCertificateVO certificateVO; HypervisorType hypervisorType = HypervisorType.getType(hypervisor); diff --git a/utils/src/main/java/com/cloud/utils/net/NetUtils.java b/utils/src/main/java/com/cloud/utils/net/NetUtils.java index 1b4ebcccf94..2703deaad64 100644 --- a/utils/src/main/java/com/cloud/utils/net/NetUtils.java +++ b/utils/src/main/java/com/cloud/utils/net/NetUtils.java @@ -99,6 +99,10 @@ public class NetUtils { public final static int IPV6_EUI64_11TH_BYTE = -1; public final static int IPV6_EUI64_12TH_BYTE = -2; + // Regex + public final static Pattern HOSTNAME_PATTERN = Pattern.compile("[a-zA-Z0-9-]+"); + public final static Pattern START_HOSTNAME_PATTERN = Pattern.compile("^[0-9-].*"); + public static String extractHost(String uri) throws URISyntaxException { return (new URI(uri)).getHost(); } @@ -1061,13 +1065,13 @@ public class NetUtils { if (hostName.length() > 63 || hostName.length() < 1) { s_logger.warn("Domain name label must be between 1 and 63 characters long"); return false; - } else if (!hostName.toLowerCase().matches("[a-z0-9-]*")) { + } else if (!HOSTNAME_PATTERN.matcher(hostName).matches()) { s_logger.warn("Domain name label may contain only the ASCII letters 'a' through 'z' (in a case-insensitive manner)"); return false; } else if (hostName.startsWith("-") || hostName.endsWith("-")) { - s_logger.warn("Domain name label can not start with a hyphen and digit, and must not end with a hyphen"); + s_logger.warn("Domain name label can not start or end with a hyphen"); return false; - } else if (isHostName && hostName.matches("^[0-9-].*")) { + } else if (isHostName && START_HOSTNAME_PATTERN.matcher(hostName).matches()) { s_logger.warn("Host name can't start with digit"); return false; } From ed1b145a3a06ab58488a01614bc1ea3c614036d0 Mon Sep 17 00:00:00 2001 From: Wei Zhou Date: Fri, 20 Dec 2024 11:35:22 +0100 Subject: [PATCH 8/8] VR: apply iptables rules when add/remove static routes (#10064) --- systemvm/debian/opt/cloud/bin/configure.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/systemvm/debian/opt/cloud/bin/configure.py b/systemvm/debian/opt/cloud/bin/configure.py index fd421fc268f..c75df98754f 100755 --- a/systemvm/debian/opt/cloud/bin/configure.py +++ b/systemvm/debian/opt/cloud/bin/configure.py @@ -1382,7 +1382,7 @@ def main(argv): ("dhcp", {"process_iptables": False, "executor": [CsDhcp("dhcpentry", config)]}), ("load_balancer", {"process_iptables": True, "executor": []}), ("monitor_service", {"process_iptables": False, "executor": [CsMonitor("monitorservice", config)]}), - ("static_routes", {"process_iptables": False, "executor": [CsStaticRoutes("staticroutes", config)]}) + ("static_routes", {"process_iptables": True, "executor": [CsStaticRoutes("staticroutes", config)]}) ]) if not config.is_vpc():