-
Notifications
You must be signed in to change notification settings - Fork 1.4k
realtime re-balance remote agents #14001
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -824,7 +824,7 @@ public void startRebalanceAgents() { | |
| final List<ManagementServerHostVO> allMS = _mshostDao.listBy(ManagementServerHost.State.Up); | ||
| final QueryBuilder<HostVO> sc = QueryBuilder.create(HostVO.class); | ||
| sc.and(sc.entity().getManagementServerId(), Op.NNULL); | ||
| sc.and(sc.entity().getType(), Op.EQ, Host.Type.Routing); | ||
| sc.and(sc.entity().getType(), Op.IN, (Object[]) AgentLoadBalancerPlanner.REBALANCEABLE_HOST_TYPES); | ||
| final List<HostVO> allManagedAgents = sc.list(); | ||
|
|
||
| int avLoad; | ||
|
|
@@ -1037,6 +1037,20 @@ protected boolean rebalanceHost(final long hostId, final long currentOwnerId, fi | |
| protected boolean rebalanceHost(final long hostId, final long currentOwnerId, final long futureOwnerId, final boolean isConnectionTransfer) throws AgentUnavailableException { | ||
| boolean result = true; | ||
| if (currentOwnerId == _nodeId) { | ||
| final AgentAttache attache = findAttache(hostId); | ||
| if (attache != null && !(attache instanceof ClusteredDirectAgentAttache)) { | ||
| // Indirectly connected agents (KVM hosts, SSVM, CPVM) dial in to a management server rather | ||
| // than being loaded directly by it, so this management server can't hand the host to a | ||
| // specific future owner the way it can for direct agents. Disconnect it instead: the agent | ||
| // reconnects on its own using its indirect agent LB configuration (the "host" global setting | ||
| // and indirect.agent.lb.algorithm), which is what actually determines its next owner. | ||
| logger.debug("Host id={} ({}) is an indirectly connected agent; disconnecting it so it reconnects and picks a management server " + | ||
| "using its own load balancing configuration", hostId, attache); | ||
| result = handleDisconnectWithoutInvestigation(attache, Event.AgentDisconnected, true, true); | ||
| finishRebalance(hostId, futureOwnerId, result ? Event.RebalanceCompleted : Event.RebalanceFailed); | ||
| return result; | ||
| } | ||
|
|
||
|
Comment on lines
+1040
to
+1053
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. new method, move comment to javadoc |
||
| if (!startRebalance(hostId)) { | ||
| logger.debug("Failed to start agent rebalancing"); | ||
| finishRebalance(hostId, futureOwnerId, Event.RebalanceFailed); | ||
|
|
@@ -1577,11 +1591,11 @@ protected void runInContext() { | |
| if (!_agentLbHappened) { | ||
| QueryBuilder<HostVO> sc = QueryBuilder.create(HostVO.class); | ||
| sc.and(sc.entity().getManagementServerId(), Op.NNULL); | ||
| sc.and(sc.entity().getType(), Op.EQ, Host.Type.Routing); | ||
| sc.and(sc.entity().getType(), Op.IN, (Object[]) AgentLoadBalancerPlanner.REBALANCEABLE_HOST_TYPES); | ||
| final List<HostVO> allManagedRoutingAgents = sc.list(); | ||
|
|
||
| sc = QueryBuilder.create(HostVO.class); | ||
| sc.and(sc.entity().getType(), Op.EQ, Host.Type.Routing); | ||
| sc.and(sc.entity().getType(), Op.IN, (Object[]) AgentLoadBalancerPlanner.REBALANCEABLE_HOST_TYPES); | ||
| final List<HostVO> allAgents = sc.list(); | ||
| final double allHostsCount = allAgents.size(); | ||
| final double managedHostsCount = allManagedRoutingAgents.size(); | ||
|
|
||
| Original file line number | Diff line number | Diff line change | ||||||
|---|---|---|---|---|---|---|---|---|
|
|
@@ -17,10 +17,13 @@ | |||||||
|
|
||||||||
| package com.cloud.agent.manager; | ||||||||
|
|
||||||||
| import com.cloud.cluster.agentlb.dao.HostTransferMapDao; | ||||||||
| import com.cloud.configuration.ManagementServiceConfiguration; | ||||||||
| import com.cloud.exception.AgentUnavailableException; | ||||||||
| import com.cloud.ha.HighAvailabilityManagerImpl; | ||||||||
| import com.cloud.host.HostVO; | ||||||||
| import com.cloud.host.Status; | ||||||||
| import com.cloud.host.Status.Event; | ||||||||
| import com.cloud.host.dao.HostDao; | ||||||||
| import com.cloud.resource.ResourceManagerImpl; | ||||||||
| import org.junit.Before; | ||||||||
|
|
@@ -33,9 +36,12 @@ | |||||||
| import java.util.ArrayList; | ||||||||
| import java.util.List; | ||||||||
|
|
||||||||
| import static org.junit.Assert.assertFalse; | ||||||||
| import static org.junit.Assert.assertTrue; | ||||||||
| import static org.mockito.ArgumentMatchers.any; | ||||||||
| import static org.mockito.ArgumentMatchers.anyBoolean; | ||||||||
| import static org.mockito.ArgumentMatchers.anyLong; | ||||||||
| import static org.mockito.Mockito.doNothing; | ||||||||
| import static org.mockito.Mockito.doReturn; | ||||||||
| import static org.mockito.Mockito.mock; | ||||||||
| import static org.mockito.Mockito.never; | ||||||||
|
|
@@ -147,4 +153,45 @@ public void scanDirectAgentToLoadHostWithNonForwardAttacheAndDisconnectedTest() | |||||||
| verify(clusteredAgentManagerImpl).investigate(agentAttache); | ||||||||
| verify(clusteredAgentManagerImpl).loadDirectlyConnectedHost(hostVO, false); | ||||||||
| } | ||||||||
|
|
||||||||
| // https://github.com/apache/cloudstack/issues/9640 | ||||||||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||
| // Indirectly connected agents (KVM hosts, SSVM, CPVM) dial in to a management server rather than | ||||||||
| // being loaded directly by it, so they must be disconnected (and left to reconnect on their own) | ||||||||
| // instead of going through the direct-agent rebalance dance that expects a ClusteredDirectAgentAttache. | ||||||||
|
Comment on lines
+158
to
+160
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||
| @Test | ||||||||
| public void rebalanceHostDisconnectsIndirectAgentInsteadOfDirectRebalanceTest() throws AgentUnavailableException { | ||||||||
| ClusteredAgentManagerImpl clusteredAgentManagerImpl = Mockito.spy(new ClusteredAgentManagerImpl()); | ||||||||
| clusteredAgentManagerImpl._nodeId = 1L; | ||||||||
| clusteredAgentManagerImpl._hostTransferDao = mock(HostTransferMapDao.class); | ||||||||
|
|
||||||||
| long hostId = 10L; | ||||||||
| AgentAttache indirectAttache = mock(ClusteredAgentAttache.class); | ||||||||
| when(clusteredAgentManagerImpl.findAttache(hostId)).thenReturn(indirectAttache); | ||||||||
| doReturn(true).when(clusteredAgentManagerImpl).handleDisconnectWithoutInvestigation(indirectAttache, Event.AgentDisconnected, true, true); | ||||||||
| doNothing().when(clusteredAgentManagerImpl).finishRebalance(hostId, 2L, Event.RebalanceCompleted); | ||||||||
|
|
||||||||
| boolean result = clusteredAgentManagerImpl.rebalanceHost(hostId, 1L, 2L, false); | ||||||||
|
|
||||||||
| assertTrue(result); | ||||||||
| verify(clusteredAgentManagerImpl).handleDisconnectWithoutInvestigation(indirectAttache, Event.AgentDisconnected, true, true); | ||||||||
| verify(clusteredAgentManagerImpl, never()).startRebalance(hostId); | ||||||||
| } | ||||||||
|
|
||||||||
| @Test | ||||||||
| public void rebalanceHostStillUsesDirectRebalanceForDirectAgentTest() throws AgentUnavailableException { | ||||||||
| ClusteredAgentManagerImpl clusteredAgentManagerImpl = Mockito.spy(new ClusteredAgentManagerImpl()); | ||||||||
| clusteredAgentManagerImpl._nodeId = 1L; | ||||||||
|
|
||||||||
| long hostId = 11L; | ||||||||
| AgentAttache directAttache = mock(ClusteredDirectAgentAttache.class); | ||||||||
| when(clusteredAgentManagerImpl.findAttache(hostId)).thenReturn(directAttache); | ||||||||
| doReturn(false).when(clusteredAgentManagerImpl).startRebalance(hostId); | ||||||||
| doNothing().when(clusteredAgentManagerImpl).finishRebalance(hostId, 2L, Event.RebalanceFailed); | ||||||||
|
|
||||||||
| boolean result = clusteredAgentManagerImpl.rebalanceHost(hostId, 1L, 2L, false); | ||||||||
|
|
||||||||
| assertFalse(result); | ||||||||
| verify(clusteredAgentManagerImpl).startRebalance(hostId); | ||||||||
| verify(clusteredAgentManagerImpl, never()).handleDisconnectWithoutInvestigation(any(), any(), anyBoolean(), anyBoolean()); | ||||||||
| } | ||||||||
| } | ||||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
evaluate and redact comment