You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cloudstack.apache.org by ro...@apache.org on 2018/03/15 11:04:08 UTC

[cloudstack] branch 4.11 updated: CLOUDSTACK-10132: Extend support for management servers LB for agents (#2469)

This is an automated email from the ASF dual-hosted git repository.

rohit pushed a commit to branch 4.11
in repository https://gitbox.apache.org/repos/asf/cloudstack.git


The following commit(s) were added to refs/heads/4.11 by this push:
     new 30175d6  CLOUDSTACK-10132: Extend support for management servers LB for agents (#2469)
30175d6 is described below

commit 30175d6879ff323f0e1abac71a9fe3a7232f8b61
Author: Rohit Yadav <ro...@apache.org>
AuthorDate: Thu Mar 15 16:34:03 2018 +0530

    CLOUDSTACK-10132: Extend support for management servers LB for agents (#2469)
    
    The new CA framework introduced basic support for comma-separated
    list of management servers for agent, which makes an external LB
    unnecessary.
    
    This extends that feature to implement LB sorting algorithms that
    sorts the management server list before they are sent to the agents.
    This adds a central intelligence in the management server and adds
    additional enhancements to Agent class to be algorithm aware and
    have a background mechanism to check/fallback to preferred management
    server (assumed as the first in the list). This is support for any
    indirect agent such as the KVM, CPVM and SSVM agent, and would
    provide support for management server host migration during upgrade
    (when instead of in-place, new hosts are used to setup new mgmt server).
    
    This FR introduces two new global settings:
    
    - `indirect.agent.lb.algorithm`: The algorithm for the indirect agent LB.
    - `indirect.agent.lb.check.interval`: The preferred host check interval
      for the agent's background task that checks and switches to agent's
      preferred host.
    
    The indirect.agent.lb.algorithm supports following algorithm options:
    
    - static: use the list as provided.
    - roundrobin: evenly spreads hosts across management servers based on
      host's id.
    - shuffle: (pseudo) randomly sorts the list (not recommended for production).
    
    Any changes to the global settings - `indirect.agent.lb.algorithm` and
    `host` does not require restarting of the mangement server(s) and the
    agents. A message bus based system dynamically reacts to change in these
    global settings and propagates them to all connected agents.
    
    Comma-separated management server list is propagated to agents on
    following cases:
    - Addition of a host (including ssvm, cpvm systevms).
    - Connection or reconnection by the agents to a management server.
    - After admin changes the 'host' and/or the
      'indirect.agent.lb.algorithm' global settings.
    
    On the agent side, the 'host' setting is saved in its properties file as:
    `host=<comma separated addresses>@<algorithm name>`.
    
    First the agent connects to the management server and sends its current
    management server list, which is compared by the management server and
    in case of failure a new/update list is sent for the agent to persist.
    
    From the agent's perspective, the first address in the propagated list
    will be considered the preferred host. A new background task can be
    activated by configuring the `indirect.agent.lb.check.interval` which is
    a cluster level global setting from CloudStack and admins can also
    override this by configuring the 'host.lb.check.interval' in the
    `agent.properties` file.
    
    Every time agent gets a ms-host list and the algorithm, the host specific
    background check interval is also sent and it dynamically reconfigures
    the background task without need to restart agents.
    
    Note: The 'static' and 'roundrobin' algorithms, strictly checks for the
    order as expected by them, however, the 'shuffle' algorithm just checks
    for content and not the order of the comma separate ms host addresses.
    
    Signed-off-by: Rohit Yadav <ro...@shapeblue.com>
---
 agent/conf/agent.properties                        |  11 +
 agent/src/com/cloud/agent/Agent.java               | 119 +++++++++--
 agent/src/com/cloud/agent/AgentShell.java          |  58 +++++-
 agent/src/com/cloud/agent/IAgentShell.java         |  45 ++--
 agent/test/com/cloud/agent/AgentShellTest.java     |   8 +-
 .../cloudstack/config/ApiServiceConfiguration.java |   4 +-
 client/pom.xml                                     |   5 +
 core/src/com/cloud/agent/api/ReadyCommand.java     |  31 ++-
 core/src/com/cloud/agent/api/StartupCommand.java   |   9 +
 .../cloudstack/agent/lb/SetupMSListAnswer.java}    |  46 +---
 .../cloudstack/agent/lb/SetupMSListCommand.java}   |  46 ++--
 engine/orchestration/pom.xml                       |   5 +
 .../com/cloud/agent/manager/AgentManagerImpl.java  |  25 ++-
 framework/agent-lb/pom.xml                         |  32 +++
 .../cloudstack/agent/lb/IndirectAgentLB.java       |  53 +++++
 .../agent/lb/IndirectAgentLBAlgorithm.java         |  45 ++++
 framework/pom.xml                                  |   3 +-
 .../network/lb/ElasticLoadBalancerManagerImpl.java |   2 +-
 server/pom.xml                                     |   5 +
 .../core/spring-server-core-managers-context.xml   |   2 +
 .../configuration/ConfigurationManagerImpl.java    |   5 +
 .../consoleproxy/ConsoleProxyManagerImpl.java      |   6 +-
 .../kvm/discoverer/LibvirtServerDiscoverer.java    |   6 +-
 .../router/VirtualNetworkApplianceManagerImpl.java |   4 +-
 .../com/cloud/server/ConfigurationServerImpl.java  |   4 +-
 .../agent/lb/IndirectAgentLBServiceImpl.java       | 231 +++++++++++++++++++++
 .../IndirectAgentLBRoundRobinAlgorithm.java        |  59 ++++++
 .../algorithm/IndirectAgentLBShuffleAlgorithm.java |  44 ++++
 .../algorithm/IndirectAgentLBStaticAlgorithm.java  |  50 ++---
 .../agent/lb/IndirectAgentLBServiceImplTest.java   | 208 +++++++++++++++++++
 .../IndirectAgentLBRoundRobinAlgorithmTest.java    |  76 +++++++
 .../IndirectAgentLBShuffleAlgorithmTest.java       |  60 ++++++
 .../IndirectAgentLBStaticAlgorithmTest.java        |  49 +++++
 server/test/resources/createNetworkOffering.xml    |   1 +
 server/test/resources/testContext.xml              |   7 +-
 .../SecondaryStorageManagerImpl.java               |   9 +-
 .../src/main/java/com/cloud/utils/StringUtils.java |   8 +-
 .../test/java/com/cloud/utils/StringUtilsTest.java |  11 +-
 38 files changed, 1218 insertions(+), 174 deletions(-)

diff --git a/agent/conf/agent.properties b/agent/conf/agent.properties
index 0354ace..ad35b96 100644
--- a/agent/conf/agent.properties
+++ b/agent/conf/agent.properties
@@ -30,6 +30,17 @@ workers=5
 #host= The IP address of management server
 host=localhost
 
+# The time interval in seconds after which agent will check if connected host
+# is the preferred host (the first host in the comma-separated list is preferred
+# one) and will attempt to reconnect to the preferred host when it's connected
+# to one of the secondary/backup hosts. The timer task is scheduled after agent
+# connects to a management server. On connection, it receives admin configured
+# cluster-level 'indirect.agent.lb.check.interval' setting that will be used by
+# the agent as the preferred host check interval however the following setting
+# if defined overrides the received value. The value 0 and lb algorithm 'shuffle'
+# disables this background task.
+#host.lb.check.interval=0
+
 #port = The port management server listening on, default is 8250
 port=8250
 
diff --git a/agent/src/com/cloud/agent/Agent.java b/agent/src/com/cloud/agent/Agent.java
index 1c5417b..3211254 100644
--- a/agent/src/com/cloud/agent/Agent.java
+++ b/agent/src/com/cloud/agent/Agent.java
@@ -21,6 +21,8 @@ import java.io.IOException;
 import java.io.PrintWriter;
 import java.io.StringWriter;
 import java.net.InetAddress;
+import java.net.InetSocketAddress;
+import java.net.Socket;
 import java.net.UnknownHostException;
 import java.nio.channels.ClosedChannelException;
 import java.nio.charset.Charset;
@@ -38,12 +40,15 @@ import java.util.concurrent.atomic.AtomicInteger;
 import javax.naming.ConfigurationException;
 
 import org.apache.cloudstack.agent.directdownload.SetupDirectDownloadCertificate;
+import org.apache.cloudstack.agent.lb.SetupMSListAnswer;
+import org.apache.cloudstack.agent.lb.SetupMSListCommand;
 import org.apache.cloudstack.ca.SetupCertificateAnswer;
 import org.apache.cloudstack.ca.SetupCertificateCommand;
 import org.apache.cloudstack.ca.SetupKeyStoreCommand;
 import org.apache.cloudstack.ca.SetupKeystoreAnswer;
 import org.apache.cloudstack.managed.context.ManagedContextTimerTask;
 import org.apache.cloudstack.utils.security.KeyStoreUtils;
+import org.apache.commons.collections.CollectionUtils;
 import org.apache.commons.io.FileUtils;
 import org.apache.log4j.Logger;
 import org.slf4j.MDC;
@@ -65,6 +70,7 @@ import com.cloud.agent.transport.Response;
 import com.cloud.exception.AgentControlChannelException;
 import com.cloud.resource.ServerResource;
 import com.cloud.utils.PropertiesUtil;
+import com.cloud.utils.StringUtils;
 import com.cloud.utils.backoff.BackoffAlgorithm;
 import com.cloud.utils.concurrency.NamedThreadFactory;
 import com.cloud.utils.exception.CloudRuntimeException;
@@ -121,6 +127,7 @@ public class Agent implements HandlerFactory, IAgentControl {
     Long _id;
 
     Timer _timer = new Timer("Agent Timer");
+    Timer hostLBTimer;
 
     List<WatchTask> _watchList = new ArrayList<WatchTask>();
     long _sequence = 0;
@@ -144,7 +151,7 @@ public class Agent implements HandlerFactory, IAgentControl {
         _shell = shell;
         _link = null;
 
-        _connection = new NioClient("Agent", _shell.getHost(), _shell.getPort(), _shell.getWorkers(), this);
+        _connection = new NioClient("Agent", _shell.getNextHost(), _shell.getPort(), _shell.getWorkers(), this);
 
         Runtime.getRuntime().addShutdownHook(new ShutdownThread(this));
 
@@ -179,7 +186,7 @@ public class Agent implements HandlerFactory, IAgentControl {
             throw new ConfigurationException("Unable to configure " + _resource.getName());
         }
 
-        final String host = _shell.getHost();
+        final String host = _shell.getNextHost();
         _connection = new NioClient("Agent", host, _shell.getPort(), _shell.getWorkers(), this);
 
         // ((NioClient)_connection).setBindAddress(_shell.getPrivateIp());
@@ -255,7 +262,7 @@ public class Agent implements HandlerFactory, IAgentControl {
             s_logger.info("Attempted to connect to the server, but received an unexpected exception, trying again...");
         }
         while (!_connection.isStartup()) {
-            final String host = _shell.getHost();
+            final String host = _shell.getNextHost();
             _shell.getBackoffAlgorithm().waitBeforeRetry();
             _connection = new NioClient("Agent", host, _shell.getPort(), _shell.getWorkers(), this);
             s_logger.info("Connecting to host:" + host);
@@ -266,6 +273,7 @@ public class Agent implements HandlerFactory, IAgentControl {
                 s_logger.info("Attempted to connect to the server, but received an unexpected exception, trying again...");
             }
         }
+        _shell.updateConnectedHost();
     }
 
     public void stop(final String reason, final String detail) {
@@ -310,6 +318,17 @@ public class Agent implements HandlerFactory, IAgentControl {
         _shell.setPersistentProperty(getResourceName(), "id", Long.toString(id));
     }
 
+    private synchronized void scheduleHostLBCheckerTask(final long checkInterval) {
+        if (hostLBTimer != null) {
+            hostLBTimer.cancel();
+        }
+        if (checkInterval > 0L) {
+            s_logger.info("Scheduling preferred host timer task with host.lb.interval=" + checkInterval + "ms");
+            hostLBTimer = new Timer("Host LB Timer");
+            hostLBTimer.scheduleAtFixedRate(new PreferredHostCheckerTask(), checkInterval, checkInterval);
+        }
+    }
+
     public void scheduleWatch(final Link link, final Request request, final long delay, final long period) {
         synchronized (_watchList) {
             if (s_logger.isDebugEnabled()) {
@@ -332,8 +351,8 @@ public class Agent implements HandlerFactory, IAgentControl {
             _watchList.clear();
         }
     }
-    public synchronized void lockStartupTask(final Link link)
-    {
+
+    public synchronized void lockStartupTask(final Link link) {
         _startup = new StartupTask(link);
         _timer.schedule(_startup, _startupWait);
     }
@@ -341,9 +360,11 @@ public class Agent implements HandlerFactory, IAgentControl {
     public void sendStartup(final Link link) {
         final StartupCommand[] startup = _resource.initialize();
         if (startup != null) {
+            final String msHostList = _shell.getPersistentProperty(null, "host");
             final Command[] commands = new Command[startup.length];
             for (int i = 0; i < startup.length; i++) {
                 setupStartupCommand(startup[i]);
+                startup[i].setMSHostList(msHostList);
                 commands[i] = startup[i];
             }
             final Request request = new Request(_id != null ? _id : -1, -1, commands, false, false);
@@ -402,19 +423,23 @@ public class Agent implements HandlerFactory, IAgentControl {
             }
         }
 
-        link.close();
-        link.terminated();
+        if (link != null) {
+            link.close();
+            link.terminated();
+        }
 
         setLink(null);
         cancelTasks();
 
         _resource.disconnected();
 
+        final String lastConnectedHost = _shell.getConnectedHost();
+
         int inProgress = 0;
         do {
             _shell.getBackoffAlgorithm().waitBeforeRetry();
 
-            s_logger.info("Lost connection to the server. Dealing with the remaining commands...");
+            s_logger.info("Lost connection to host: " + lastConnectedHost + ". Dealing with the remaining commands...");
 
             inProgress = _inProgress.get();
             if (inProgress > 0) {
@@ -434,7 +459,7 @@ public class Agent implements HandlerFactory, IAgentControl {
             _shell.getBackoffAlgorithm().waitBeforeRetry();
         }
 
-        final String host = _shell.getHost();
+        final String host = _shell.getNextHost();
         do {
             _connection = new NioClient("Agent", host, _shell.getPort(), _shell.getWorkers(), this);
             s_logger.info("Reconnecting to host:" + host);
@@ -452,7 +477,8 @@ public class Agent implements HandlerFactory, IAgentControl {
             }
             _shell.getBackoffAlgorithm().waitBeforeRetry();
         } while (!_connection.isStartup());
-        s_logger.info("Connected to the server");
+        _shell.updateConnectedHost();
+        s_logger.info("Connected to the host: " + _shell.getConnectedHost());
     }
 
     public void processStartupAnswer(final Answer answer, final Response response, final Link link) {
@@ -554,6 +580,8 @@ public class Agent implements HandlerFactory, IAgentControl {
                         answer = setupAgentCertificate((SetupCertificateCommand) cmd);
                     } else if (cmd instanceof SetupDirectDownloadCertificate) {
                         answer = setupDirectDownloadCertificate((SetupDirectDownloadCertificate) cmd);
+                    } else if (cmd instanceof SetupMSListCommand) {
+                        answer = setupManagementServerList((SetupMSListCommand) cmd);
                     } else {
                         if (cmd instanceof ReadyCommand) {
                             processReadyCommand(cmd);
@@ -708,6 +736,30 @@ public class Agent implements HandlerFactory, IAgentControl {
         return new SetupCertificateAnswer(true);
     }
 
+    private void processManagementServerList(final List<String> msList, final String lbAlgorithm, final Long lbCheckInterval) {
+        if (CollectionUtils.isNotEmpty(msList) && !Strings.isNullOrEmpty(lbAlgorithm)) {
+            try {
+                final String newMSHosts = String.format("%s%s%s", StringUtils.toCSVList(msList), IAgentShell.hostLbAlgorithmSeparator, lbAlgorithm);
+                _shell.setPersistentProperty(null, "host", newMSHosts);
+                _shell.setHosts(newMSHosts);
+                _shell.resetHostCounter();
+                s_logger.info("Processed new management server list: " + newMSHosts);
+            } catch (final Exception e) {
+                throw new CloudRuntimeException("Could not persist received management servers list", e);
+            }
+        }
+        if ("shuffle".equals(lbAlgorithm)) {
+            scheduleHostLBCheckerTask(0);
+        } else {
+            scheduleHostLBCheckerTask(_shell.getLbCheckerInterval(lbCheckInterval));
+        }
+    }
+
+    private Answer setupManagementServerList(final SetupMSListCommand cmd) {
+        processManagementServerList(cmd.getMsList(), cmd.getLbAlgorithm(), cmd.getLbCheckInterval());
+        return new SetupMSListAnswer(true);
+    }
+
     public void processResponse(final Response response, final Link link) {
         final Answer answer = response.getAnswer();
         if (s_logger.isDebugEnabled()) {
@@ -728,15 +780,16 @@ public class Agent implements HandlerFactory, IAgentControl {
     }
 
     public void processReadyCommand(final Command cmd) {
-
         final ReadyCommand ready = (ReadyCommand)cmd;
 
-        s_logger.info("Proccess agent ready command, agent id = " + ready.getHostId());
+        s_logger.info("Processing agent ready command, agent id = " + ready.getHostId());
         if (ready.getHostId() != null) {
             setId(ready.getHostId());
         }
-        s_logger.info("Ready command is processed: agent id = " + getId());
 
+        processManagementServerList(ready.getMsHostList(), ready.getLbAlgorithm(), ready.getLbCheckInterval());
+
+        s_logger.info("Ready command is processed for agent id = " + getId());
     }
 
     public void processOtherTask(final Task task) {
@@ -1018,4 +1071,44 @@ public class Agent implements HandlerFactory, IAgentControl {
             }
         }
     }
+
+    public class PreferredHostCheckerTask extends ManagedContextTimerTask {
+
+        @Override
+        protected void runInContext() {
+            try {
+                final String[] msList = _shell.getHosts();
+                if (msList == null || msList.length < 1) {
+                    return;
+                }
+                final String preferredHost  = msList[0];
+                final String connectedHost = _shell.getConnectedHost();
+                if (s_logger.isTraceEnabled()) {
+                    s_logger.trace("Running preferred host checker task, connected host=" + connectedHost + ", preferred host=" + preferredHost);
+                }
+                if (preferredHost != null && !preferredHost.equals(connectedHost) && _link != null) {
+                    boolean isHostUp = true;
+                    try (final Socket socket = new Socket()) {
+                        socket.connect(new InetSocketAddress(preferredHost, _shell.getPort()), 5000);
+                    } catch (final IOException e) {
+                        isHostUp = false;
+                        if (s_logger.isTraceEnabled()) {
+                            s_logger.trace("Host: " + preferredHost + " is not reachable");
+                        }
+                    }
+                    if (isHostUp && _link != null && _inProgress.get() == 0) {
+                        if (s_logger.isDebugEnabled()) {
+                            s_logger.debug("Preferred host " + preferredHost + " is found to be reachable, trying to reconnect");
+                        }
+                        _shell.resetHostCounter();
+                        reconnect(_link);
+                    }
+                }
+            } catch (Throwable t) {
+                s_logger.error("Error caught while attempting to connect to preferred host", t);
+            }
+        }
+
+    }
+
 }
diff --git a/agent/src/com/cloud/agent/AgentShell.java b/agent/src/com/cloud/agent/AgentShell.java
index 5950bc7..13b6c65 100644
--- a/agent/src/com/cloud/agent/AgentShell.java
+++ b/agent/src/com/cloud/agent/AgentShell.java
@@ -50,6 +50,7 @@ import com.cloud.utils.PropertiesUtil;
 import com.cloud.utils.backoff.BackoffAlgorithm;
 import com.cloud.utils.backoff.impl.ConstantTimeBackoff;
 import com.cloud.utils.exception.CloudRuntimeException;
+import com.google.common.base.Strings;
 
 public class AgentShell implements IAgentShell, Daemon {
     private static final Logger s_logger = Logger.getLogger(AgentShell.class.getName());
@@ -72,6 +73,9 @@ public class AgentShell implements IAgentShell, Daemon {
     private volatile boolean _exit = false;
     private int _pingRetries;
     private final List<Agent> _agents = new ArrayList<Agent>();
+    private String hostToConnect;
+    private String connectedHost;
+    private Long preferredHostCheckInterval;
 
     public AgentShell() {
     }
@@ -107,18 +111,54 @@ public class AgentShell implements IAgentShell, Daemon {
     }
 
     @Override
-    public String getHost() {
-        final String[] hosts = _host.split(",");
+    public String getNextHost() {
+        final String[] hosts = getHosts();
         if (_hostCounter >= hosts.length) {
             _hostCounter = 0;
         }
-        final String host = hosts[_hostCounter % hosts.length];
+        hostToConnect = hosts[_hostCounter % hosts.length];
         _hostCounter++;
-        return host;
+        return hostToConnect;
     }
 
-    public void setHost(final String host) {
-        _host = host;
+    @Override
+    public String getConnectedHost() {
+        return connectedHost;
+    }
+
+    @Override
+    public long getLbCheckerInterval(final Long receivedLbInterval) {
+        if (preferredHostCheckInterval != null) {
+            return preferredHostCheckInterval * 1000L;
+        }
+        if (receivedLbInterval != null) {
+            return receivedLbInterval * 1000L;
+        }
+        return 0L;
+    }
+
+    @Override
+    public void updateConnectedHost() {
+        connectedHost = hostToConnect;
+    }
+
+
+    @Override
+    public void resetHostCounter() {
+        _hostCounter = 0;
+    }
+
+    @Override
+    public String[] getHosts() {
+        return _host.split(",");
+    }
+
+    @Override
+    public void setHosts(final String host) {
+        if (!Strings.isNullOrEmpty(host)) {
+            _host = host.split(hostLbAlgorithmSeparator)[0];
+            resetHostCounter();
+        }
     }
 
     @Override
@@ -251,7 +291,8 @@ public class AgentShell implements IAgentShell, Daemon {
         if (host == null) {
             host = "localhost";
         }
-        _host = host;
+
+        setHosts(host);
 
         if (zone != null)
             _zone = zone;
@@ -291,6 +332,9 @@ public class AgentShell implements IAgentShell, Daemon {
             _properties.setProperty("guid", _guid);
         }
 
+        String val = getProperty(null, preferredHostIntervalKey);
+        preferredHostCheckInterval = (Strings.isNullOrEmpty(val) ? null : Long.valueOf(val));
+
         return true;
     }
 
diff --git a/agent/src/com/cloud/agent/IAgentShell.java b/agent/src/com/cloud/agent/IAgentShell.java
index dde6738..5b52cee 100644
--- a/agent/src/com/cloud/agent/IAgentShell.java
+++ b/agent/src/com/cloud/agent/IAgentShell.java
@@ -22,33 +22,48 @@ import java.util.Properties;
 import com.cloud.utils.backoff.BackoffAlgorithm;
 
 public interface IAgentShell {
-    public Map<String, Object> getCmdLineProperties();
+    String hostLbAlgorithmSeparator = "@";
+    String preferredHostIntervalKey = "host.lb.check.interval";
 
-    public Properties getProperties();
+    Map<String, Object> getCmdLineProperties();
 
-    public String getPersistentProperty(String prefix, String name);
+    Properties getProperties();
 
-    public void setPersistentProperty(String prefix, String name, String value);
+    String getPersistentProperty(String prefix, String name);
 
-    public String getHost();
+    void setPersistentProperty(String prefix, String name, String value);
 
-    public String getPrivateIp();
+    String getNextHost();
 
-    public int getPort();
+    String getPrivateIp();
 
-    public int getWorkers();
+    int getPort();
 
-    public int getProxyPort();
+    int getWorkers();
 
-    public String getGuid();
+    int getProxyPort();
 
-    public String getZone();
+    String getGuid();
 
-    public String getPod();
+    String getZone();
 
-    public BackoffAlgorithm getBackoffAlgorithm();
+    String getPod();
 
-    public int getPingRetries();
+    BackoffAlgorithm getBackoffAlgorithm();
 
-    public String getVersion();
+    int getPingRetries();
+
+    String getVersion();
+
+    void setHosts(String hosts);
+
+    void resetHostCounter();
+
+    String[] getHosts();
+
+    long getLbCheckerInterval(Long receivedLbInterval);
+
+    void updateConnectedHost();
+
+    String getConnectedHost();
 }
diff --git a/agent/test/com/cloud/agent/AgentShellTest.java b/agent/test/com/cloud/agent/AgentShellTest.java
index 8ceba45..868293c 100644
--- a/agent/test/com/cloud/agent/AgentShellTest.java
+++ b/agent/test/com/cloud/agent/AgentShellTest.java
@@ -35,7 +35,7 @@ public class AgentShellTest {
         shell.parseCommand(new String[] {"port=55555", "threads=4", "host=localhost", "pod=pod1", "guid=" + anyUuid, "zone=zone1"});
         Assert.assertEquals(55555, shell.getPort());
         Assert.assertEquals(4, shell.getWorkers());
-        Assert.assertEquals("localhost", shell.getHost());
+        Assert.assertEquals("localhost", shell.getNextHost());
         Assert.assertEquals(anyUuid.toString(), shell.getGuid());
         Assert.assertEquals("pod1", shell.getPod());
         Assert.assertEquals("zone1", shell.getZone());
@@ -53,10 +53,10 @@ public class AgentShellTest {
     public void testGetHost() {
         AgentShell shell = new AgentShell();
         List<String> hosts = Arrays.asList("10.1.1.1", "20.2.2.2", "30.3.3.3", "2001:db8::1");
-        shell.setHost(StringUtils.listToCsvTags(hosts));
+        shell.setHosts(StringUtils.listToCsvTags(hosts));
         for (String host : hosts) {
-            Assert.assertEquals(host, shell.getHost());
+            Assert.assertEquals(host, shell.getNextHost());
         }
-        Assert.assertEquals(shell.getHost(), hosts.get(0));
+        Assert.assertEquals(shell.getNextHost(), hosts.get(0));
     }
 }
diff --git a/api/src/org/apache/cloudstack/config/ApiServiceConfiguration.java b/api/src/org/apache/cloudstack/config/ApiServiceConfiguration.java
index 485688a..55e8202 100644
--- a/api/src/org/apache/cloudstack/config/ApiServiceConfiguration.java
+++ b/api/src/org/apache/cloudstack/config/ApiServiceConfiguration.java
@@ -20,7 +20,7 @@ import org.apache.cloudstack.framework.config.ConfigKey;
 import org.apache.cloudstack.framework.config.Configurable;
 
 public class ApiServiceConfiguration implements Configurable {
-    public static final ConfigKey<String> ManagementHostIPAdr = new ConfigKey<String>("Advanced", String.class, "host", "localhost", "The ip address of management server", true);
+    public static final ConfigKey<String> ManagementServerAddresses = new ConfigKey<String>("Advanced", String.class, "host", "localhost", "The ip address of management server. This can also accept comma separated addresses.", true);
     public static final ConfigKey<String> ApiServletPath = new ConfigKey<String>("Advanced", String.class, "endpointe.url", "http://localhost:8080/client/api",
             "API end point. Can be used by CS components/services deployed remotely, for sending CS API requests", true);
     public static final ConfigKey<Long> DefaultUIPageSize = new ConfigKey<Long>("Advanced", Long.class, "default.ui.page.size", "20",
@@ -36,7 +36,7 @@ public class ApiServiceConfiguration implements Configurable {
 
     @Override
     public ConfigKey<?>[] getConfigKeys() {
-        return new ConfigKey<?>[] {ManagementHostIPAdr, ApiServletPath, DefaultUIPageSize, ApiSourceCidrChecksEnabled, ApiAllowedSourceCidrList};
+        return new ConfigKey<?>[] {ManagementServerAddresses, ApiServletPath, DefaultUIPageSize, ApiSourceCidrChecksEnabled, ApiAllowedSourceCidrList};
     }
 
 }
diff --git a/client/pom.xml b/client/pom.xml
index ba3ba5d..9907d8c 100644
--- a/client/pom.xml
+++ b/client/pom.xml
@@ -315,6 +315,11 @@
     </dependency>
     <dependency>
       <groupId>org.apache.cloudstack</groupId>
+      <artifactId>cloud-framework-agent-lb</artifactId>
+     <version>${project.version}</version>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.cloudstack</groupId>
       <artifactId>cloud-framework-ca</artifactId>
       <version>${project.version}</version>
     </dependency>
diff --git a/core/src/com/cloud/agent/api/ReadyCommand.java b/core/src/com/cloud/agent/api/ReadyCommand.java
index b02b004..06a435a 100644
--- a/core/src/com/cloud/agent/api/ReadyCommand.java
+++ b/core/src/com/cloud/agent/api/ReadyCommand.java
@@ -19,6 +19,8 @@
 
 package com.cloud.agent.api;
 
+import java.util.List;
+
 public class ReadyCommand extends Command {
     private String _details;
 
@@ -28,13 +30,16 @@ public class ReadyCommand extends Command {
 
     private Long dcId;
     private Long hostId;
+    private List<String> msHostList;
+    private String lbAlgorithm;
+    private Long lbCheckInterval;
 
     public ReadyCommand(Long dcId) {
         super();
         this.dcId = dcId;
     }
 
-    public ReadyCommand(Long dcId, Long hostId) {
+    public ReadyCommand(final Long dcId, final Long hostId) {
         this(dcId);
         this.hostId = hostId;
     }
@@ -59,4 +64,28 @@ public class ReadyCommand extends Command {
     public Long getHostId() {
         return hostId;
     }
+
+    public List<String> getMsHostList() {
+        return msHostList;
+    }
+
+    public void setMsHostList(List<String> msHostList) {
+        this.msHostList = msHostList;
+    }
+
+    public String getLbAlgorithm() {
+        return lbAlgorithm;
+    }
+
+    public void setLbAlgorithm(String lbAlgorithm) {
+        this.lbAlgorithm = lbAlgorithm;
+    }
+
+    public Long getLbCheckInterval() {
+        return lbCheckInterval;
+    }
+
+    public void setLbCheckInterval(Long lbCheckInterval) {
+        this.lbCheckInterval = lbCheckInterval;
+    }
 }
diff --git a/core/src/com/cloud/agent/api/StartupCommand.java b/core/src/com/cloud/agent/api/StartupCommand.java
index 1de51ad..5f2c00d 100644
--- a/core/src/com/cloud/agent/api/StartupCommand.java
+++ b/core/src/com/cloud/agent/api/StartupCommand.java
@@ -46,6 +46,7 @@ public class StartupCommand extends Command {
     String agentTag;
     String resourceName;
     String gatewayIpAddress;
+    String msHostList;
 
     public StartupCommand(Host.Type type) {
         this.type = type;
@@ -281,6 +282,14 @@ public class StartupCommand extends Command {
         this.gatewayIpAddress = gatewayIpAddress;
     }
 
+    public String getMsHostList() {
+        return msHostList;
+    }
+
+    public void setMSHostList(String msHostList) {
+        this.msHostList = msHostList;
+    }
+
     @Override
     public boolean executeInSequence() {
         return false;
diff --git a/core/src/com/cloud/agent/api/ReadyCommand.java b/core/src/org/apache/cloudstack/agent/lb/SetupMSListAnswer.java
similarity index 52%
copy from core/src/com/cloud/agent/api/ReadyCommand.java
copy to core/src/org/apache/cloudstack/agent/lb/SetupMSListAnswer.java
index b02b004..e73d8e5 100644
--- a/core/src/com/cloud/agent/api/ReadyCommand.java
+++ b/core/src/org/apache/cloudstack/agent/lb/SetupMSListAnswer.java
@@ -17,46 +17,14 @@
 // under the License.
 //
 
-package com.cloud.agent.api;
+package org.apache.cloudstack.agent.lb;
 
-public class ReadyCommand extends Command {
-    private String _details;
+import com.cloud.agent.api.Answer;
 
-    public ReadyCommand() {
-        super();
-    }
-
-    private Long dcId;
-    private Long hostId;
-
-    public ReadyCommand(Long dcId) {
-        super();
-        this.dcId = dcId;
-    }
-
-    public ReadyCommand(Long dcId, Long hostId) {
-        this(dcId);
-        this.hostId = hostId;
-    }
-
-    public void setDetails(String details) {
-        _details = details;
-    }
-
-    public String getDetails() {
-        return _details;
-    }
-
-    public Long getDataCenterId() {
-        return dcId;
-    }
-
-    @Override
-    public boolean executeInSequence() {
-        return true;
-    }
+public class SetupMSListAnswer extends Answer {
 
-    public Long getHostId() {
-        return hostId;
+    public SetupMSListAnswer(final boolean result) {
+        super(null);
+        this.result = result;
     }
-}
+}
\ No newline at end of file
diff --git a/core/src/com/cloud/agent/api/ReadyCommand.java b/core/src/org/apache/cloudstack/agent/lb/SetupMSListCommand.java
similarity index 56%
copy from core/src/com/cloud/agent/api/ReadyCommand.java
copy to core/src/org/apache/cloudstack/agent/lb/SetupMSListCommand.java
index b02b004..abc739f 100644
--- a/core/src/com/cloud/agent/api/ReadyCommand.java
+++ b/core/src/org/apache/cloudstack/agent/lb/SetupMSListCommand.java
@@ -17,46 +17,40 @@
 // under the License.
 //
 
-package com.cloud.agent.api;
+package org.apache.cloudstack.agent.lb;
 
-public class ReadyCommand extends Command {
-    private String _details;
+import java.util.List;
 
-    public ReadyCommand() {
-        super();
-    }
+import com.cloud.agent.api.Command;
 
-    private Long dcId;
-    private Long hostId;
+public class SetupMSListCommand extends Command {
 
-    public ReadyCommand(Long dcId) {
-        super();
-        this.dcId = dcId;
-    }
+    private List<String> msList;
+    private String lbAlgorithm;
+    private Long lbCheckInterval;
 
-    public ReadyCommand(Long dcId, Long hostId) {
-        this(dcId);
-        this.hostId = hostId;
+    public SetupMSListCommand(final List<String> msList, final String lbAlgorithm, final Long lbCheckInterval) {
+        super();
+        this.msList = msList;
+        this.lbAlgorithm = lbAlgorithm;
+        this.lbCheckInterval = lbCheckInterval;
     }
 
-    public void setDetails(String details) {
-        _details = details;
+    public List<String> getMsList() {
+        return msList;
     }
 
-    public String getDetails() {
-        return _details;
+    public String getLbAlgorithm() {
+        return lbAlgorithm;
     }
 
-    public Long getDataCenterId() {
-        return dcId;
+    public Long getLbCheckInterval() {
+        return lbCheckInterval;
     }
 
     @Override
     public boolean executeInSequence() {
-        return true;
+        return false;
     }
 
-    public Long getHostId() {
-        return hostId;
-    }
-}
+}
\ No newline at end of file
diff --git a/engine/orchestration/pom.xml b/engine/orchestration/pom.xml
index 6ad6806..5bc8697 100755
--- a/engine/orchestration/pom.xml
+++ b/engine/orchestration/pom.xml
@@ -60,6 +60,11 @@
     </dependency>
     <dependency>
       <groupId>org.apache.cloudstack</groupId>
+      <artifactId>cloud-framework-agent-lb</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.cloudstack</groupId>
       <artifactId>cloud-server</artifactId>
       <version>${project.version}</version>
     </dependency>
diff --git a/engine/orchestration/src/com/cloud/agent/manager/AgentManagerImpl.java b/engine/orchestration/src/com/cloud/agent/manager/AgentManagerImpl.java
index b735775..9626385 100644
--- a/engine/orchestration/src/com/cloud/agent/manager/AgentManagerImpl.java
+++ b/engine/orchestration/src/com/cloud/agent/manager/AgentManagerImpl.java
@@ -20,6 +20,7 @@ import java.lang.reflect.Constructor;
 import java.lang.reflect.InvocationTargetException;
 import java.nio.channels.ClosedChannelException;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Date;
 import java.util.HashMap;
 import java.util.List;
@@ -37,6 +38,7 @@ import java.util.concurrent.locks.ReentrantLock;
 import javax.inject.Inject;
 import javax.naming.ConfigurationException;
 
+import org.apache.cloudstack.agent.lb.IndirectAgentLB;
 import org.apache.cloudstack.ca.CAManager;
 import com.cloud.configuration.ManagementServiceConfiguration;
 import org.apache.cloudstack.framework.config.ConfigKey;
@@ -116,6 +118,7 @@ import com.cloud.utils.nio.Link;
 import com.cloud.utils.nio.NioServer;
 import com.cloud.utils.nio.Task;
 import com.cloud.utils.time.InaccurateClock;
+import com.google.common.base.Strings;
 
 /**
  * Implementation of the Agent Manager. This class controls the connection to the agents.
@@ -163,6 +166,9 @@ public class AgentManagerImpl extends ManagerBase implements AgentManager, Handl
     @Inject
     protected HypervisorGuruManager _hvGuruMgr;
 
+    @Inject
+    protected IndirectAgentLB indirectAgentLB;
+
     protected int _retry = 2;
 
     protected long _nodeId = -1;
@@ -1081,14 +1087,31 @@ public class AgentManagerImpl extends ManagerBase implements AgentManager, Handl
         AgentAttache attache = null;
         ReadyCommand ready = null;
         try {
+            final List<String> agentMSHostList = new ArrayList<>();
+            if (startup != null && startup.length > 0) {
+                final String agentMSHosts = startup[0].getMsHostList();
+                if (!Strings.isNullOrEmpty(agentMSHosts)) {
+                    agentMSHostList.addAll(Arrays.asList(agentMSHosts.split(",")));
+                }
+            }
+
             final HostVO host = _resourceMgr.createHostVOForConnectedAgent(startup);
             if (host != null) {
                 ready = new ReadyCommand(host.getDataCenterId(), host.getId());
+
+                if (!indirectAgentLB.compareManagementServerList(host.getId(), host.getDataCenterId(), agentMSHostList)) {
+                    final List<String> newMSList = indirectAgentLB.getManagementServerList(host.getId(), host.getDataCenterId(), null);
+                    ready.setMsHostList(newMSList);
+                    ready.setLbAlgorithm(indirectAgentLB.getLBAlgorithmName());
+                    ready.setLbCheckInterval(indirectAgentLB.getLBPreferredHostCheckInterval(host.getClusterId()));
+                    s_logger.debug("Agent's management server host list is not up to date, sending list update:" + newMSList);
+                }
+
                 attache = createAttacheForConnect(host, link);
                 attache = notifyMonitorsOfConnection(attache, startup, false);
             }
         } catch (final Exception e) {
-            s_logger.debug("Failed to handle host connection: " + e.toString());
+            s_logger.debug("Failed to handle host connection: ", e);
             ready = new ReadyCommand(null);
             ready.setDetails(e.toString());
         } finally {
diff --git a/framework/agent-lb/pom.xml b/framework/agent-lb/pom.xml
new file mode 100644
index 0000000..7d14a3a
--- /dev/null
+++ b/framework/agent-lb/pom.xml
@@ -0,0 +1,32 @@
+<!--
+  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.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+    <name>Apache CloudStack Agent Management Servers Load Balancer</name>
+    <artifactId>cloud-framework-agent-lb</artifactId>
+    <parent>
+        <artifactId>cloudstack-framework</artifactId>
+        <groupId>org.apache.cloudstack</groupId>
+        <version>4.11.1.0-SNAPSHOT</version>
+        <relativePath>../pom.xml</relativePath>
+    </parent>
+
+</project>
diff --git a/framework/agent-lb/src/org/apache/cloudstack/agent/lb/IndirectAgentLB.java b/framework/agent-lb/src/org/apache/cloudstack/agent/lb/IndirectAgentLB.java
new file mode 100644
index 0000000..627a5ee
--- /dev/null
+++ b/framework/agent-lb/src/org/apache/cloudstack/agent/lb/IndirectAgentLB.java
@@ -0,0 +1,53 @@
+// 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.
+package org.apache.cloudstack.agent.lb;
+
+import java.util.List;
+
+public interface IndirectAgentLB {
+
+    /**
+     * Return list of management server addresses after applying configured lb algorithm
+     * for a host in a zone.
+     * @param hostId host id (if present)
+     * @param dcId zone id
+     * @param orderedHostIdList (optional) list of ordered host id list
+     * @return management servers string list
+     */
+    List<String> getManagementServerList(Long hostId, Long dcId, List<Long> orderedHostIdList);
+
+    /**
+     * Compares received management server list against expected list for a host in a zone.
+     * @param hostId host id
+     * @param dcId zone id
+     * @param receivedMSHosts received management server list
+     * @return true if mgmtHosts is up to date, false if not
+     */
+    boolean compareManagementServerList(Long hostId, Long dcId, List<String> receivedMSHosts);
+
+    /**
+     * Returns the configure LB algorithm
+     * @return returns algorithm name
+     */
+    String getLBAlgorithmName();
+
+    /**
+     * Returns the configured LB preferred host check interval (if applicable at cluster scope)
+     * @return returns interval in seconds
+     */
+    Long getLBPreferredHostCheckInterval(Long clusterId);
+}
\ No newline at end of file
diff --git a/framework/agent-lb/src/org/apache/cloudstack/agent/lb/IndirectAgentLBAlgorithm.java b/framework/agent-lb/src/org/apache/cloudstack/agent/lb/IndirectAgentLBAlgorithm.java
new file mode 100644
index 0000000..a4a622f
--- /dev/null
+++ b/framework/agent-lb/src/org/apache/cloudstack/agent/lb/IndirectAgentLBAlgorithm.java
@@ -0,0 +1,45 @@
+// 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.
+
+package org.apache.cloudstack.agent.lb;
+
+import java.util.List;
+
+public interface IndirectAgentLBAlgorithm {
+    /**
+     * Returns a sorted management server list to send to host after applying the algorithm
+     * @param msList management server list
+     * @param orderedHostList ordered host list
+     * @param hostId host id
+     * @return returns the list of management server addresses which will be sent to host id
+     */
+    List<String> sort(final List<String> msList, final List<Long> orderedHostList, final Long hostId);
+
+    /**
+     * Gets the unique name of the algorithm
+     * @return returns the name of the Agent MSLB algorithm
+     */
+    String getName();
+
+    /**
+     * Compares and return if received mgmt server list is equal to the actual mgmt server lists
+     * @param msList current mgmt server list
+     * @param receivedMsList received mgmt server list
+     * @return true if the lists are equal, false if not
+     */
+    boolean compare(final List<String> msList, final List<String> receivedMsList);
+}
\ No newline at end of file
diff --git a/framework/pom.xml b/framework/pom.xml
index b09acd3..4ac1423 100644
--- a/framework/pom.xml
+++ b/framework/pom.xml
@@ -55,7 +55,8 @@
     <module>managed-context</module>
     <module>spring/lifecycle</module>
     <module>spring/module</module>
-	<module>security</module>
+    <module>security</module>
+    <module>agent-lb</module>
     <module>direct-download</module>
   </modules>
 </project>
diff --git a/plugins/network-elements/elastic-loadbalancer/src/com/cloud/network/lb/ElasticLoadBalancerManagerImpl.java b/plugins/network-elements/elastic-loadbalancer/src/com/cloud/network/lb/ElasticLoadBalancerManagerImpl.java
index d917554..78e04b5 100644
--- a/plugins/network-elements/elastic-loadbalancer/src/com/cloud/network/lb/ElasticLoadBalancerManagerImpl.java
+++ b/plugins/network-elements/elastic-loadbalancer/src/com/cloud/network/lb/ElasticLoadBalancerManagerImpl.java
@@ -447,7 +447,7 @@ public class ElasticLoadBalancerManagerImpl extends ManagerBase implements Elast
                     if (s_logger.isInfoEnabled()) {
                         s_logger.info("Check if we need to add management server explicit route to ELB vm. pod cidr: " + dest.getPod().getCidrAddress() + "/"
                                 + dest.getPod().getCidrSize() + ", pod gateway: " + dest.getPod().getGateway() + ", management host: "
-                                + ApiServiceConfiguration.ManagementHostIPAdr.value());
+                                + ApiServiceConfiguration.ManagementServerAddresses.value());
                     }
 
                     if (s_logger.isDebugEnabled()) {
diff --git a/server/pom.xml b/server/pom.xml
index fa3f75c..534b1a3 100644
--- a/server/pom.xml
+++ b/server/pom.xml
@@ -137,6 +137,11 @@
       <version>${project.version}</version>
     </dependency>
     <dependency>
+      <groupId>org.apache.cloudstack</groupId>
+      <artifactId>cloud-framework-agent-lb</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+    <dependency>
       <groupId>org.opensaml</groupId>
       <artifactId>opensaml</artifactId>
       <version>${cs.opensaml.version}</version>
diff --git a/server/resources/META-INF/cloudstack/core/spring-server-core-managers-context.xml b/server/resources/META-INF/cloudstack/core/spring-server-core-managers-context.xml
index 7d19b1e..c7715a8 100644
--- a/server/resources/META-INF/cloudstack/core/spring-server-core-managers-context.xml
+++ b/server/resources/META-INF/cloudstack/core/spring-server-core-managers-context.xml
@@ -295,5 +295,7 @@
 
     <bean id="annotationService" class="org.apache.cloudstack.annotation.AnnotationManagerImpl" />
 
+    <bean id="indirectAgentLBService" class="org.apache.cloudstack.agent.lb.IndirectAgentLBServiceImpl" />
+
     <bean id="directDownloadManager" class="org.apache.cloudstack.direct.download.DirectDownloadManagerImpl" />
 </beans>
diff --git a/server/src/com/cloud/configuration/ConfigurationManagerImpl.java b/server/src/com/cloud/configuration/ConfigurationManagerImpl.java
index 1632da9..80642f5 100755
--- a/server/src/com/cloud/configuration/ConfigurationManagerImpl.java
+++ b/server/src/com/cloud/configuration/ConfigurationManagerImpl.java
@@ -76,6 +76,8 @@ import org.apache.cloudstack.framework.config.ConfigKey;
 import org.apache.cloudstack.framework.config.Configurable;
 import org.apache.cloudstack.framework.config.dao.ConfigurationDao;
 import org.apache.cloudstack.framework.config.impl.ConfigurationVO;
+import org.apache.cloudstack.framework.messagebus.MessageBus;
+import org.apache.cloudstack.framework.messagebus.PublishScope;
 import org.apache.cloudstack.region.PortableIp;
 import org.apache.cloudstack.region.PortableIpDao;
 import org.apache.cloudstack.region.PortableIpRange;
@@ -352,6 +354,8 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati
     ImageStoreDao _imageStoreDao;
     @Inject
     ImageStoreDetailsDao _imageStoreDetailsDao;
+    @Inject
+    MessageBus messageBus;
 
 
     // FIXME - why don't we have interface for DataCenterLinkLocalIpAddressDao?
@@ -660,6 +664,7 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati
         }
 
         txn.commit();
+        messageBus.publish(_name, EventTypes.EVENT_CONFIGURATION_VALUE_EDIT, PublishScope.GLOBAL, name);
         return _configDao.getValue(name);
     }
 
diff --git a/server/src/com/cloud/consoleproxy/ConsoleProxyManagerImpl.java b/server/src/com/cloud/consoleproxy/ConsoleProxyManagerImpl.java
index 28fff3c..f131714 100644
--- a/server/src/com/cloud/consoleproxy/ConsoleProxyManagerImpl.java
+++ b/server/src/com/cloud/consoleproxy/ConsoleProxyManagerImpl.java
@@ -29,7 +29,7 @@ import java.util.Map;
 import javax.inject.Inject;
 import javax.naming.ConfigurationException;
 
-import org.apache.cloudstack.config.ApiServiceConfiguration;
+import org.apache.cloudstack.agent.lb.IndirectAgentLB;
 import org.apache.cloudstack.context.CallContext;
 import org.apache.cloudstack.engine.orchestration.service.NetworkOrchestrationService;
 import org.apache.cloudstack.framework.config.dao.ConfigurationDao;
@@ -211,6 +211,8 @@ public class ConsoleProxyManagerImpl extends ManagerBase implements ConsoleProxy
     private KeysManager _keysMgr;
     @Inject
     private VirtualMachineManager _itMgr;
+    @Inject
+    private IndirectAgentLB indirectAgentLB;
 
     private ConsoleProxyListener _listener;
 
@@ -1355,7 +1357,7 @@ public class ConsoleProxyManagerImpl extends ManagerBase implements ConsoleProxy
 
         StringBuilder buf = profile.getBootArgsBuilder();
         buf.append(" template=domP type=consoleproxy");
-        buf.append(" host=").append(StringUtils.shuffleCSVList(ApiServiceConfiguration.ManagementHostIPAdr.value()));
+        buf.append(" host=").append(StringUtils.toCSVList(indirectAgentLB.getManagementServerList(dest.getHost().getId(), dest.getDataCenter().getId(), null)));
         buf.append(" port=").append(_mgmtPort);
         buf.append(" name=").append(profile.getVirtualMachine().getHostName());
         if (_sslEnabled) {
diff --git a/server/src/com/cloud/hypervisor/kvm/discoverer/LibvirtServerDiscoverer.java b/server/src/com/cloud/hypervisor/kvm/discoverer/LibvirtServerDiscoverer.java
index c1afc9a..ef69fdc 100644
--- a/server/src/com/cloud/hypervisor/kvm/discoverer/LibvirtServerDiscoverer.java
+++ b/server/src/com/cloud/hypervisor/kvm/discoverer/LibvirtServerDiscoverer.java
@@ -27,9 +27,9 @@ import java.util.UUID;
 import javax.inject.Inject;
 import javax.naming.ConfigurationException;
 
+import org.apache.cloudstack.agent.lb.IndirectAgentLB;
 import org.apache.cloudstack.ca.CAManager;
 import org.apache.cloudstack.ca.SetupCertificateCommand;
-import org.apache.cloudstack.config.ApiServiceConfiguration;
 import org.apache.cloudstack.framework.ca.Certificate;
 import org.apache.cloudstack.utils.security.KeyStoreUtils;
 import org.apache.log4j.Logger;
@@ -76,6 +76,8 @@ public abstract class LibvirtServerDiscoverer extends DiscovererBase implements
     private AgentManager agentMgr;
     @Inject
     private CAManager caManager;
+    @Inject
+    private IndirectAgentLB indirectAgentLB;
 
     @Override
     public abstract Hypervisor.HypervisorType getHypervisorType();
@@ -288,7 +290,7 @@ public abstract class LibvirtServerDiscoverer extends DiscovererBase implements
 
             setupAgentSecurity(sshConnection, agentIp, hostname);
 
-            String parameters = " -m " + StringUtils.shuffleCSVList(ApiServiceConfiguration.ManagementHostIPAdr.value()) + " -z " + dcId + " -p " + podId + " -c " + clusterId + " -g " + guid + " -a";
+            String parameters = " -m " + StringUtils.toCSVList(indirectAgentLB.getManagementServerList(null, dcId, null)) + " -z " + dcId + " -p " + podId     + " -c " + clusterId + " -g " + guid + " -a";
 
             parameters += " --pubNic=" + kvmPublicNic;
             parameters += " --prvNic=" + kvmPrivateNic;
diff --git a/server/src/com/cloud/network/router/VirtualNetworkApplianceManagerImpl.java b/server/src/com/cloud/network/router/VirtualNetworkApplianceManagerImpl.java
index f3035d0..1985dea 100644
--- a/server/src/com/cloud/network/router/VirtualNetworkApplianceManagerImpl.java
+++ b/server/src/com/cloud/network/router/VirtualNetworkApplianceManagerImpl.java
@@ -1373,7 +1373,7 @@ Configurable, StateListener<VirtualMachine.State, VirtualMachine.Event, VirtualM
                 if (dest.getHost().getHypervisorType() == HypervisorType.VMware || dest.getHost().getHypervisorType() == HypervisorType.Hyperv) {
                     s_logger.info("Check if we need to add management server explicit route to DomR. pod cidr: " + dest.getPod().getCidrAddress() + "/"
                             + dest.getPod().getCidrSize() + ", pod gateway: " + dest.getPod().getGateway() + ", management host: "
-                            + ApiServiceConfiguration.ManagementHostIPAdr.value());
+                            + ApiServiceConfiguration.ManagementServerAddresses.value());
 
                     if (s_logger.isInfoEnabled()) {
                         s_logger.info("Add management server explicit route to DomR.");
@@ -1484,7 +1484,7 @@ Configurable, StateListener<VirtualMachine.State, VirtualMachine.Event, VirtualM
             } else {
                 buf.append(String.format(" baremetalnotificationsecuritykey=%s", user.getSecretKey()));
                 buf.append(String.format(" baremetalnotificationapikey=%s", user.getApiKey()));
-                buf.append(" host=").append(ApiServiceConfiguration.ManagementHostIPAdr.value());
+                buf.append(" host=").append(ApiServiceConfiguration.ManagementServerAddresses.value());
                 buf.append(" port=").append(_configDao.getValue(Config.BaremetalProvisionDoneNotificationPort.key()));
             }
         }
diff --git a/server/src/com/cloud/server/ConfigurationServerImpl.java b/server/src/com/cloud/server/ConfigurationServerImpl.java
index e348051..197ebed 100644
--- a/server/src/com/cloud/server/ConfigurationServerImpl.java
+++ b/server/src/com/cloud/server/ConfigurationServerImpl.java
@@ -246,14 +246,14 @@ public class ConfigurationServerImpl extends ManagerBase implements Configuratio
             if (hostIpAdr != null) {
                 Boolean devel = Boolean.valueOf(_configDao.getValue("developer"));
                 if (devel) {
-                    String value = _configDao.getValue(ApiServiceConfiguration.ManagementHostIPAdr.key());
+                    String value = _configDao.getValue(ApiServiceConfiguration.ManagementServerAddresses.key());
                     if (value != null && !value.equals("localhost")) {
                         needUpdateHostIp = false;
                     }
                 }
 
                 if (needUpdateHostIp) {
-                    _configDepot.createOrUpdateConfigObject(ApiServiceConfiguration.class.getSimpleName(), ApiServiceConfiguration.ManagementHostIPAdr, hostIpAdr);
+                    _configDepot.createOrUpdateConfigObject(ApiServiceConfiguration.class.getSimpleName(), ApiServiceConfiguration.ManagementServerAddresses, hostIpAdr);
                     s_logger.debug("ConfigurationServer saved \"" + hostIpAdr + "\" as host.");
                 }
             }
diff --git a/server/src/org/apache/cloudstack/agent/lb/IndirectAgentLBServiceImpl.java b/server/src/org/apache/cloudstack/agent/lb/IndirectAgentLBServiceImpl.java
new file mode 100644
index 0000000..19eb97d
--- /dev/null
+++ b/server/src/org/apache/cloudstack/agent/lb/IndirectAgentLBServiceImpl.java
@@ -0,0 +1,231 @@
+// 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.
+package org.apache.cloudstack.agent.lb;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import javax.inject.Inject;
+import javax.naming.ConfigurationException;
+
+import org.apache.cloudstack.agent.lb.algorithm.IndirectAgentLBRoundRobinAlgorithm;
+import org.apache.cloudstack.agent.lb.algorithm.IndirectAgentLBShuffleAlgorithm;
+import org.apache.cloudstack.agent.lb.algorithm.IndirectAgentLBStaticAlgorithm;
+import org.apache.cloudstack.config.ApiServiceConfiguration;
+import org.apache.cloudstack.framework.config.ConfigKey;
+import org.apache.cloudstack.framework.config.Configurable;
+import org.apache.cloudstack.framework.messagebus.MessageBus;
+import org.apache.cloudstack.framework.messagebus.MessageSubscriber;
+import org.apache.log4j.Logger;
+
+import com.cloud.agent.AgentManager;
+import com.cloud.agent.api.Answer;
+import com.cloud.event.EventTypes;
+import com.cloud.host.Host;
+import com.cloud.host.HostVO;
+import com.cloud.host.dao.HostDao;
+import com.cloud.hypervisor.Hypervisor;
+import com.cloud.resource.ResourceState;
+import com.cloud.utils.component.ComponentLifecycleBase;
+import com.cloud.utils.exception.CloudRuntimeException;
+import com.google.common.base.Strings;
+
+public class IndirectAgentLBServiceImpl extends ComponentLifecycleBase implements IndirectAgentLB, Configurable {
+    public static final Logger LOG = Logger.getLogger(IndirectAgentLBServiceImpl.class);
+
+    public static final ConfigKey<String> IndirectAgentLBAlgorithm = new ConfigKey<>("Advanced", String.class,
+            "indirect.agent.lb.algorithm", "static",
+            "The algorithm to be applied on the provided 'host' management server list that is sent to indirect agents. Allowed values are: static, roundrobin and shuffle.",
+            true, ConfigKey.Scope.Global);
+
+    public static final ConfigKey<Long> IndirectAgentLBCheckInterval = new ConfigKey<>("Advanced", Long.class,
+            "indirect.agent.lb.check.interval", "0",
+            "The interval in seconds after which agent should check and try to connect to its preferred host. Set 0 to disable it.",
+            true, ConfigKey.Scope.Cluster);
+
+    private static Map<String, org.apache.cloudstack.agent.lb.IndirectAgentLBAlgorithm> algorithmMap = new HashMap<>();
+
+    @Inject
+    private HostDao hostDao;
+    @Inject
+    private MessageBus messageBus;
+    @Inject
+    private AgentManager agentManager;
+
+    //////////////////////////////////////////////////////
+    /////////////// Agent MSLB Methods ///////////////////
+    //////////////////////////////////////////////////////
+
+    @Override
+    public List<String> getManagementServerList(final Long hostId, final Long dcId, final List<Long> orderedHostIdList) {
+        final String msServerAddresses = ApiServiceConfiguration.ManagementServerAddresses.value();
+        if (Strings.isNullOrEmpty(msServerAddresses)) {
+            throw new CloudRuntimeException(String.format("No management server addresses are defined in '%s' setting",
+                    ApiServiceConfiguration.ManagementServerAddresses.key()));
+        }
+
+        List<Long> hostIdList = orderedHostIdList;
+        if (hostIdList == null) {
+            hostIdList = getOrderedHostIdList(dcId);
+        }
+
+        final org.apache.cloudstack.agent.lb.IndirectAgentLBAlgorithm algorithm = getAgentMSLBAlgorithm();
+        final List<String> msList = Arrays.asList(msServerAddresses.replace(" ", "").split(","));
+        return algorithm.sort(msList, hostIdList, hostId);
+    }
+
+    @Override
+    public boolean compareManagementServerList(final Long hostId, final Long dcId, final List<String> receivedMSHosts) {
+        if (receivedMSHosts == null || receivedMSHosts.size() < 1) {
+            return false;
+        }
+        final List<String> expectedMSList = getManagementServerList(hostId, dcId, null);
+        final org.apache.cloudstack.agent.lb.IndirectAgentLBAlgorithm algorithm = getAgentMSLBAlgorithm();
+        return algorithm.compare(expectedMSList, receivedMSHosts);
+    }
+
+    @Override
+    public String getLBAlgorithmName() {
+        return IndirectAgentLBAlgorithm.value();
+    }
+
+    @Override
+    public Long getLBPreferredHostCheckInterval(final Long clusterId) {
+        return IndirectAgentLBCheckInterval.valueIn(clusterId);
+    }
+
+    List<Long> getOrderedHostIdList(final Long dcId) {
+        final List<Long> hostIdList = new ArrayList<>();
+        for (final Host host : getAllAgentBasedHosts()) {
+            if (host.getDataCenterId() == dcId) {
+                hostIdList.add(host.getId());
+            }
+        }
+        Collections.sort(hostIdList, new Comparator<Long>() {
+            @Override
+            public int compare(Long x, Long y) {
+                return Long.compare(x,y);
+            }
+        });
+        return hostIdList;
+    }
+
+    private List<Host> getAllAgentBasedHosts() {
+        final List<HostVO> allHosts = hostDao.listAll();
+        if (allHosts == null) {
+            return new ArrayList<>();
+        }
+        final List <Host> agentBasedHosts = new ArrayList<>();
+        for (final Host host : allHosts) {
+            if (host == null || host.getResourceState() != ResourceState.Enabled) {
+                continue;
+            }
+            if (host.getType() == Host.Type.Routing || host.getType() == Host.Type.ConsoleProxy || host.getType() == Host.Type.SecondaryStorage || host.getType() == Host.Type.SecondaryStorageVM) {
+                if (host.getHypervisorType() != null && host.getHypervisorType() != Hypervisor.HypervisorType.KVM && host.getHypervisorType() != Hypervisor.HypervisorType.LXC) {
+                    continue;
+                }
+                agentBasedHosts.add(host);
+            }
+        }
+        return agentBasedHosts;
+    }
+
+    private org.apache.cloudstack.agent.lb.IndirectAgentLBAlgorithm getAgentMSLBAlgorithm() {
+        final String algorithm = getLBAlgorithmName();
+        if (algorithmMap.containsKey(algorithm)) {
+            return algorithmMap.get(algorithm);
+        }
+        throw new CloudRuntimeException(String.format("Algorithm configured for '%s' not found, valid values are: %s",
+                IndirectAgentLBAlgorithm.key(), algorithmMap.keySet()));
+    }
+
+    ////////////////////////////////////////////////////////////
+    /////////////// Agent MSLB Configuration ///////////////////
+    ////////////////////////////////////////////////////////////
+
+    private void propagateMSListToAgents() {
+        LOG.debug("Propagating management server list update to agents");
+        final String lbAlgorithm = getLBAlgorithmName();
+        final Map<Long, List<Long>> dcOrderedHostsMap = new HashMap<>();
+        for (final Host host : getAllAgentBasedHosts()) {
+            final Long dcId = host.getDataCenterId();
+            if (!dcOrderedHostsMap.containsKey(dcId)) {
+                dcOrderedHostsMap.put(dcId, getOrderedHostIdList(dcId));
+            }
+            final List<String> msList = getManagementServerList(host.getId(), host.getDataCenterId(), dcOrderedHostsMap.get(dcId));
+            final Long lbCheckInterval = getLBPreferredHostCheckInterval(host.getClusterId());
+            final SetupMSListCommand cmd = new SetupMSListCommand(msList, lbAlgorithm, lbCheckInterval);
+            final Answer answer = agentManager.easySend(host.getId(), cmd);
+            if (answer == null || !answer.getResult()) {
+                LOG.warn("Failed to setup management servers list to the agent of host id=" + host.getId());
+            }
+        }
+    }
+
+    private void configureMessageBusListener() {
+        messageBus.subscribe(EventTypes.EVENT_CONFIGURATION_VALUE_EDIT, new MessageSubscriber() {
+            @Override
+            public void onPublishMessage(final String senderAddress, String subject, Object args) {
+                final String globalSettingUpdated = (String) args;
+                if (Strings.isNullOrEmpty(globalSettingUpdated)) {
+                    return;
+                }
+                if (globalSettingUpdated.equals(ApiServiceConfiguration.ManagementServerAddresses.key()) ||
+                        globalSettingUpdated.equals(IndirectAgentLBAlgorithm.key())) {
+                    propagateMSListToAgents();
+                }
+            }
+        });
+    }
+
+    private void configureAlgorithmMap() {
+        final List<org.apache.cloudstack.agent.lb.IndirectAgentLBAlgorithm> algorithms = new ArrayList<>();
+        algorithms.add(new IndirectAgentLBStaticAlgorithm());
+        algorithms.add(new IndirectAgentLBRoundRobinAlgorithm());
+        algorithms.add(new IndirectAgentLBShuffleAlgorithm());
+        algorithmMap.clear();
+        for (org.apache.cloudstack.agent.lb.IndirectAgentLBAlgorithm algorithm : algorithms) {
+            algorithmMap.put(algorithm.getName(), algorithm);
+        }
+    }
+
+    @Override
+    public boolean configure(final String name, final Map<String, Object> params) throws ConfigurationException {
+        super.configure(name, params);
+        configureAlgorithmMap();
+        configureMessageBusListener();
+        return true;
+    }
+
+    @Override
+    public String getConfigComponentName() {
+        return IndirectAgentLBServiceImpl.class.getSimpleName();
+    }
+
+    @Override
+    public ConfigKey<?>[] getConfigKeys() {
+        return new ConfigKey<?>[] {
+                IndirectAgentLBAlgorithm,
+                IndirectAgentLBCheckInterval
+        };
+    }
+}
\ No newline at end of file
diff --git a/server/src/org/apache/cloudstack/agent/lb/algorithm/IndirectAgentLBRoundRobinAlgorithm.java b/server/src/org/apache/cloudstack/agent/lb/algorithm/IndirectAgentLBRoundRobinAlgorithm.java
new file mode 100644
index 0000000..0c9044a
--- /dev/null
+++ b/server/src/org/apache/cloudstack/agent/lb/algorithm/IndirectAgentLBRoundRobinAlgorithm.java
@@ -0,0 +1,59 @@
+// 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.
+package org.apache.cloudstack.agent.lb.algorithm;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.cloudstack.agent.lb.IndirectAgentLBAlgorithm;
+
+public class IndirectAgentLBRoundRobinAlgorithm implements IndirectAgentLBAlgorithm {
+
+    private int findRRPivotIndex(final List<String> msList, final List<Long> orderedHostList, final Long hostId) {
+        return orderedHostList.indexOf(hostId) % msList.size();
+    }
+
+    @Override
+    public List<String> sort(final List<String> msList, final List<Long> orderedHostList, final Long hostId) {
+        if (msList.size() < 2) {
+            return msList;
+        }
+
+        final List<Long> hostList = new ArrayList<>(orderedHostList);
+        Long searchId = hostId;
+        if (hostId == null) {
+            searchId = -1L;
+            hostList.add(searchId);
+        }
+
+        final int pivotIndex = findRRPivotIndex(msList, hostList, searchId);
+        final List<String> roundRobin = new ArrayList<>(msList.subList(pivotIndex, msList.size()));
+        roundRobin.addAll(msList.subList(0, pivotIndex));
+
+        return roundRobin;
+    }
+
+    @Override
+    public String getName() {
+        return "roundrobin";
+    }
+
+    @Override
+    public boolean compare(final List<String> msList, final List<String> receivedMsList) {
+        return msList != null && receivedMsList != null && msList.equals(receivedMsList);
+    }
+}
\ No newline at end of file
diff --git a/server/src/org/apache/cloudstack/agent/lb/algorithm/IndirectAgentLBShuffleAlgorithm.java b/server/src/org/apache/cloudstack/agent/lb/algorithm/IndirectAgentLBShuffleAlgorithm.java
new file mode 100644
index 0000000..ccc1bfc
--- /dev/null
+++ b/server/src/org/apache/cloudstack/agent/lb/algorithm/IndirectAgentLBShuffleAlgorithm.java
@@ -0,0 +1,44 @@
+// 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.
+package org.apache.cloudstack.agent.lb.algorithm;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Random;
+import org.apache.cloudstack.agent.lb.IndirectAgentLBAlgorithm;
+import org.apache.commons.collections.SetUtils;
+
+public class IndirectAgentLBShuffleAlgorithm implements IndirectAgentLBAlgorithm {
+
+    @Override
+    public List<String> sort(final List<String> msList, final List<Long> orderedHostList, final Long hostId) {
+        final List<String> randomList = new ArrayList<>(msList);
+        Collections.shuffle(randomList, new Random(System.currentTimeMillis()));
+        return randomList;
+    }
+
+    @Override
+    public String getName() {
+        return "shuffle";
+    }
+
+    @Override
+    public boolean compare(List<String> msList, List<String> receivedMsList) {
+        return SetUtils.isEqualSet(msList, receivedMsList);
+    }
+}
\ No newline at end of file
diff --git a/core/src/com/cloud/agent/api/ReadyCommand.java b/server/src/org/apache/cloudstack/agent/lb/algorithm/IndirectAgentLBStaticAlgorithm.java
similarity index 53%
copy from core/src/com/cloud/agent/api/ReadyCommand.java
copy to server/src/org/apache/cloudstack/agent/lb/algorithm/IndirectAgentLBStaticAlgorithm.java
index b02b004..b30a010 100644
--- a/core/src/com/cloud/agent/api/ReadyCommand.java
+++ b/server/src/org/apache/cloudstack/agent/lb/algorithm/IndirectAgentLBStaticAlgorithm.java
@@ -1,4 +1,3 @@
-//
 // 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
@@ -15,48 +14,27 @@
 // KIND, either express or implied.  See the License for the
 // specific language governing permissions and limitations
 // under the License.
-//
-
-package com.cloud.agent.api;
-
-public class ReadyCommand extends Command {
-    private String _details;
-
-    public ReadyCommand() {
-        super();
-    }
-
-    private Long dcId;
-    private Long hostId;
+package org.apache.cloudstack.agent.lb.algorithm;
 
-    public ReadyCommand(Long dcId) {
-        super();
-        this.dcId = dcId;
-    }
+import java.util.ArrayList;
+import java.util.List;
 
-    public ReadyCommand(Long dcId, Long hostId) {
-        this(dcId);
-        this.hostId = hostId;
-    }
+import org.apache.cloudstack.agent.lb.IndirectAgentLBAlgorithm;
 
-    public void setDetails(String details) {
-        _details = details;
-    }
+public class IndirectAgentLBStaticAlgorithm implements IndirectAgentLBAlgorithm {
 
-    public String getDetails() {
-        return _details;
-    }
-
-    public Long getDataCenterId() {
-        return dcId;
+    @Override
+    public List<String> sort(final List<String> msList, final List<Long> orderedHostList, final Long hostId) {
+        return new ArrayList<>(msList);
     }
 
     @Override
-    public boolean executeInSequence() {
-        return true;
+    public String getName() {
+        return "static";
     }
 
-    public Long getHostId() {
-        return hostId;
+    @Override
+    public boolean compare(final List<String> msList, final List<String> receivedMsList) {
+        return msList != null && receivedMsList != null && msList.equals(receivedMsList);
     }
-}
+}
\ No newline at end of file
diff --git a/server/test/org/apache/cloudstack/agent/lb/IndirectAgentLBServiceImplTest.java b/server/test/org/apache/cloudstack/agent/lb/IndirectAgentLBServiceImplTest.java
new file mode 100644
index 0000000..79b5421
--- /dev/null
+++ b/server/test/org/apache/cloudstack/agent/lb/IndirectAgentLBServiceImplTest.java
@@ -0,0 +1,208 @@
+// 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.
+package org.apache.cloudstack.agent.lb;
+
+import static org.mockito.Mockito.when;
+
+import java.lang.reflect.Field;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.List;
+
+import org.apache.cloudstack.config.ApiServiceConfiguration;
+import org.apache.cloudstack.framework.config.ConfigKey;
+import org.apache.cloudstack.framework.messagebus.MessageBus;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.InjectMocks;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.mockito.Spy;
+
+import com.cloud.agent.AgentManager;
+import com.cloud.host.Host;
+import com.cloud.host.HostVO;
+import com.cloud.host.dao.HostDao;
+import com.cloud.hypervisor.Hypervisor;
+import com.cloud.resource.ResourceState;
+import com.cloud.utils.exception.CloudRuntimeException;
+
+public class IndirectAgentLBServiceImplTest {
+
+    @Mock
+    HostDao hostDao;
+    @Mock
+    MessageBus messageBus;
+    @Mock
+    AgentManager agentManager;
+
+    @Mock
+    HostVO host1;
+    @Mock
+    HostVO host2;
+    @Mock
+    HostVO host3;
+    @Mock
+    HostVO host4;
+
+    @Spy
+    @InjectMocks
+    private IndirectAgentLBServiceImpl agentMSLB = new IndirectAgentLBServiceImpl();
+
+    private final String msCSVList = "192.168.10.10, 192.168.10.11, 192.168.10.12";
+    private final List<String> msList = Arrays.asList(msCSVList.replace(" ","").split(","));
+
+    private static final long DC_1_ID = 1L;
+    private static final long DC_2_ID = 2L;
+
+    private void overrideDefaultConfigValue(final ConfigKey configKey, final String name, final Object o) throws IllegalAccessException, NoSuchFieldException {
+        final Field f = ConfigKey.class.getDeclaredField(name);
+        f.setAccessible(true);
+        f.set(configKey, o);
+    }
+
+    private void addField(final IndirectAgentLBServiceImpl provider, final String name, final Object o) throws IllegalAccessException, NoSuchFieldException {
+        Field f = IndirectAgentLBServiceImpl.class.getDeclaredField(name);
+        f.setAccessible(true);
+        f.set(provider, o);
+    }
+
+    private void configureMocks() throws NoSuchFieldException, IllegalAccessException {
+        long id = 1;
+        for (HostVO h : Arrays.asList(host1, host2, host3, host4)) {
+            when(h.getId()).thenReturn(id);
+            when(h.getDataCenterId()).thenReturn(DC_1_ID);
+            when(h.getHypervisorType()).thenReturn(Hypervisor.HypervisorType.KVM);
+            when(h.getType()).thenReturn(Host.Type.Routing);
+            when(h.getRemoved()).thenReturn(null);
+            when(h.getResourceState()).thenReturn(ResourceState.Enabled);
+            id++;
+        }
+        addField(agentMSLB, "hostDao", hostDao);
+        addField(agentMSLB, "messageBus", messageBus);
+        addField(agentMSLB, "agentManager", agentManager);
+
+        when(hostDao.listAll()).thenReturn(Arrays.asList(host4, host2, host1, host3));
+    }
+
+    @Before
+    public void setUp() throws Exception {
+        MockitoAnnotations.initMocks(this);
+        configureMocks();
+        agentMSLB.configure("someName", null);
+        overrideDefaultConfigValue(ApiServiceConfiguration.ManagementServerAddresses, "_defaultValue", msCSVList);
+    }
+
+    @Test
+    public void testStaticLBSetting() throws NoSuchFieldException, IllegalAccessException {
+        overrideDefaultConfigValue(IndirectAgentLBServiceImpl.IndirectAgentLBAlgorithm, "_defaultValue", "static");
+        for (HostVO host : Arrays.asList(host1, host2, host3, host4)) {
+            List<String> listToSend = agentMSLB.getManagementServerList(host.getId(), host.getDataCenterId(), null);
+            Assert.assertEquals(msList, listToSend);
+        }
+    }
+
+    @Test
+    public void testStaticLBSettingNullHostId() throws NoSuchFieldException, IllegalAccessException {
+        overrideDefaultConfigValue(IndirectAgentLBServiceImpl.IndirectAgentLBAlgorithm, "_defaultValue", "static");
+        List<String> listToSend = agentMSLB.getManagementServerList(host2.getId(), host2.getDataCenterId(), null);
+        Assert.assertEquals(listToSend, agentMSLB.getManagementServerList(null, DC_1_ID, null));
+    }
+
+    private void testRoundRobinForExistingHosts(List<String> list) {
+        for (HostVO hostVO : Arrays.asList(host1, host2, host3, host4)) {
+            List<String> listToSend = agentMSLB.getManagementServerList(hostVO.getId(), hostVO.getDataCenterId(), null);
+            Assert.assertEquals(list, listToSend);
+            Assert.assertEquals(list.get(0), listToSend.get(0));
+            list.add(list.get(0));
+            list.remove(0);
+        }
+    }
+
+    @Test
+    public void testRoundRobinDeterministicOrder() throws NoSuchFieldException, IllegalAccessException {
+        overrideDefaultConfigValue(IndirectAgentLBServiceImpl.IndirectAgentLBAlgorithm, "_defaultValue", "roundrobin");
+        List<String> listHost2 = agentMSLB.getManagementServerList(host2.getId(), host2.getDataCenterId(), null);
+        Assert.assertEquals(listHost2, agentMSLB.getManagementServerList(host2.getId(), host2.getDataCenterId(), null));
+    }
+
+    @Test
+    public void testRoundRobinLBSettingConnectedAgents() throws NoSuchFieldException, IllegalAccessException {
+        overrideDefaultConfigValue(IndirectAgentLBServiceImpl.IndirectAgentLBAlgorithm, "_defaultValue", "roundrobin");
+        List<String> list = new ArrayList<>(msList);
+        testRoundRobinForExistingHosts(list);
+    }
+
+    @Test
+    public void testRoundRobinLBSettingNullHostId() throws NoSuchFieldException, IllegalAccessException {
+        overrideDefaultConfigValue(IndirectAgentLBServiceImpl.IndirectAgentLBAlgorithm, "_defaultValue", "roundrobin");
+        List<String> list = new ArrayList<>(msList);
+        testRoundRobinForExistingHosts(list);
+        List<String> listToSend = agentMSLB.getManagementServerList(null, DC_1_ID, null);
+        Assert.assertEquals(list, listToSend);
+        Assert.assertEquals(list.get(0), listToSend.get(0));
+        list.add(list.get(0));
+        list.remove(0);
+    }
+
+    @Test
+    public void testShuffleLBSetting() throws NoSuchFieldException, IllegalAccessException {
+        overrideDefaultConfigValue(IndirectAgentLBServiceImpl.IndirectAgentLBAlgorithm, "_defaultValue", "shuffle");
+        List<String> shuffleListHost2 = agentMSLB.getManagementServerList(host2.getId(), host2.getDataCenterId(), null);
+        Assert.assertEquals(new HashSet<>(msList), new HashSet<>(shuffleListHost2));
+    }
+
+    @Test
+    public void testShuffleLBSettingNullHostId() throws NoSuchFieldException, IllegalAccessException {
+        overrideDefaultConfigValue(IndirectAgentLBServiceImpl.IndirectAgentLBAlgorithm, "_defaultValue", "shuffle");
+        Assert.assertEquals(new HashSet<>(msList), new HashSet<>(agentMSLB.getManagementServerList(null, DC_1_ID, null)));
+    }
+
+    @Test(expected = CloudRuntimeException.class)
+    public void testInvalidAlgorithmSetting() throws NoSuchFieldException, IllegalAccessException {
+        overrideDefaultConfigValue(IndirectAgentLBServiceImpl.IndirectAgentLBAlgorithm, "_defaultValue", "invalid-algo");
+        agentMSLB.getManagementServerList(host1.getId(), host1.getDataCenterId(), null);
+    }
+
+    @Test(expected = CloudRuntimeException.class)
+    public void testExceptionOnEmptyHostSetting() throws NoSuchFieldException, IllegalAccessException {
+        overrideDefaultConfigValue(ApiServiceConfiguration.ManagementServerAddresses, "_defaultValue", "");
+        // This should throw exception
+        agentMSLB.getManagementServerList(host1.getId(), host1.getDataCenterId(), null);
+    }
+
+    @Test
+    public void testGetOrderedRunningHostIdsNullList() {
+        when(hostDao.listAll()).thenReturn(null);
+        Assert.assertTrue(agentMSLB.getOrderedHostIdList(DC_1_ID).size() == 0);
+    }
+
+    @Test
+    public void testGetOrderedRunningHostIdsOrderList() {
+        when(hostDao.listAll()).thenReturn(Arrays.asList(host4, host2, host1, host3));
+        Assert.assertEquals(Arrays.asList(host1.getId(), host2.getId(), host3.getId(), host4.getId()),
+                agentMSLB.getOrderedHostIdList(DC_1_ID));
+    }
+
+    @Test
+    public void testGetHostsPerZoneNullHosts() {
+        when(hostDao.listAll()).thenReturn(null);
+        Assert.assertTrue(agentMSLB.getOrderedHostIdList(DC_2_ID).size() == 0);
+    }
+}
\ No newline at end of file
diff --git a/server/test/org/apache/cloudstack/agent/lb/algorithm/IndirectAgentLBRoundRobinAlgorithmTest.java b/server/test/org/apache/cloudstack/agent/lb/algorithm/IndirectAgentLBRoundRobinAlgorithmTest.java
new file mode 100644
index 0000000..c38f733
--- /dev/null
+++ b/server/test/org/apache/cloudstack/agent/lb/algorithm/IndirectAgentLBRoundRobinAlgorithmTest.java
@@ -0,0 +1,76 @@
+// 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.
+package org.apache.cloudstack.agent.lb.algorithm;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+
+import org.apache.cloudstack.agent.lb.IndirectAgentLBAlgorithm;
+import org.junit.Assert;
+import org.junit.Test;
+
+public class IndirectAgentLBRoundRobinAlgorithmTest {
+    private IndirectAgentLBAlgorithm algorithm = new IndirectAgentLBRoundRobinAlgorithm();
+
+    private List<String> msList = Arrays.asList("10.1.1.1", "10.1.1.2", "10.1.1.3");
+    private List<Long> hostList = new ArrayList<>(Arrays.asList(1L, 5L, 10L, 20L, 50L, 60L, 70L, 80L));
+
+    @Test
+    public void testGetMSListForNewHost() throws Exception {
+        List<String> startList = algorithm.sort(msList, hostList, null);
+        Assert.assertNotEquals(msList, startList);
+        Assert.assertFalse(algorithm.compare(msList, startList));
+
+        hostList.add(100L);
+        List<String> nextList = algorithm.sort(msList, hostList, null);
+        List<String> expectedList = startList.subList(1, startList.size());
+        expectedList.addAll(startList.subList(0, 1));
+        Assert.assertEquals(nextList, expectedList);
+    }
+
+    @Test
+    public void testGetMSListForExistingHost() throws Exception {
+        List<String> startList = new ArrayList<>(msList);
+        for (Long hostId : hostList.subList(1, hostList.size())) {
+            List<String> nextList = new ArrayList<>(startList.subList(1, msList.size()));
+            nextList.addAll(startList.subList(0, 1));
+            List<String> expectedList = algorithm.sort(msList, hostList, hostId);
+            Assert.assertEquals(expectedList, nextList);
+            startList = nextList;
+        }
+    }
+
+    @Test
+    public void testName() throws Exception {
+        Assert.assertEquals(algorithm.getName(), "roundrobin");
+    }
+
+    @Test
+    public void testListComparison() throws Exception {
+        Assert.assertTrue(algorithm.compare(Collections.singletonList("10.1.1.1"), Collections.singletonList("10.1.1.1")));
+        Assert.assertTrue(algorithm.compare(Arrays.asList("10.1.1.2", "10.1.1.1"), Arrays.asList("10.1.1.2", "10.1.1.1")));
+        Assert.assertTrue(algorithm.compare(msList, Arrays.asList("10.1.1.1", "10.1.1.2", "10.1.1.3")));
+
+        Assert.assertFalse(algorithm.compare(msList, Arrays.asList("10.1.1.3", "10.1.1.2", "10.1.1.1")));
+        Assert.assertFalse(algorithm.compare(msList, Arrays.asList("10.1.1.0", "10.2.2.2")));
+        Assert.assertFalse(algorithm.compare(msList, new ArrayList<String>()));
+        Assert.assertFalse(algorithm.compare(msList, null));
+    }
+
+}
\ No newline at end of file
diff --git a/server/test/org/apache/cloudstack/agent/lb/algorithm/IndirectAgentLBShuffleAlgorithmTest.java b/server/test/org/apache/cloudstack/agent/lb/algorithm/IndirectAgentLBShuffleAlgorithmTest.java
new file mode 100644
index 0000000..59894b3
--- /dev/null
+++ b/server/test/org/apache/cloudstack/agent/lb/algorithm/IndirectAgentLBShuffleAlgorithmTest.java
@@ -0,0 +1,60 @@
+// 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.
+package org.apache.cloudstack.agent.lb.algorithm;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+
+import org.apache.cloudstack.agent.lb.IndirectAgentLBAlgorithm;
+import org.junit.Assert;
+import org.junit.Test;
+
+public class IndirectAgentLBShuffleAlgorithmTest {
+    private IndirectAgentLBAlgorithm algorithm = new IndirectAgentLBShuffleAlgorithm();
+
+    private List<String> msList = Arrays.asList("10.1.1.1", "10.1.1.2", "10.1.1.3");
+
+    @Test
+    public void testGetMSList() throws Exception {
+        for (int i = 0; i < 100; i++) {
+            final List<String> newList = algorithm.sort(msList, null, null);
+            if (!msList.equals(newList)) {
+                return;
+            }
+            Thread.sleep(10);
+        }
+        Assert.fail("Shuffle failed to produce a randomly sorted management server list");
+    }
+
+    @Test
+    public void testName() throws Exception {
+        Assert.assertEquals(algorithm.getName(), "shuffle");
+    }
+
+    @Test
+    public void testListComparison() throws Exception {
+        Assert.assertTrue(algorithm.compare(Collections.singletonList("10.1.1.1"), Collections.singletonList("10.1.1.1")));
+        Assert.assertTrue(algorithm.compare(msList, Arrays.asList("10.1.1.1", "10.1.1.2", "10.1.1.3")));
+        Assert.assertTrue(algorithm.compare(msList, Arrays.asList("10.1.1.3", "10.1.1.2", "10.1.1.1")));
+
+        Assert.assertFalse(algorithm.compare(msList, Arrays.asList("10.1.1.0", "10.2.2.2")));
+        Assert.assertFalse(algorithm.compare(msList, new ArrayList<String>()));
+        Assert.assertFalse(algorithm.compare(msList, null));
+    }
+}
\ No newline at end of file
diff --git a/server/test/org/apache/cloudstack/agent/lb/algorithm/IndirectAgentLBStaticAlgorithmTest.java b/server/test/org/apache/cloudstack/agent/lb/algorithm/IndirectAgentLBStaticAlgorithmTest.java
new file mode 100644
index 0000000..7ba5c90
--- /dev/null
+++ b/server/test/org/apache/cloudstack/agent/lb/algorithm/IndirectAgentLBStaticAlgorithmTest.java
@@ -0,0 +1,49 @@
+// 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.
+package org.apache.cloudstack.agent.lb.algorithm;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+import org.apache.cloudstack.agent.lb.IndirectAgentLBAlgorithm;
+import org.junit.Assert;
+import org.junit.Test;
+
+public class IndirectAgentLBStaticAlgorithmTest {
+    private IndirectAgentLBAlgorithm algorithm = new IndirectAgentLBStaticAlgorithm();
+
+    private List<String> msList = Arrays.asList("10.1.1.1", "10.1.1.2", "10.1.1.3");
+
+    @Test
+    public void testGetMSList() throws Exception {
+        Assert.assertEquals(msList, algorithm.sort(msList, null, null));
+    }
+
+    @Test
+    public void testName() throws Exception {
+        Assert.assertEquals(algorithm.getName(), "static");
+    }
+
+    @Test
+    public void testListComparison() throws Exception {
+        Assert.assertTrue(algorithm.compare(msList, Arrays.asList("10.1.1.1", "10.1.1.2", "10.1.1.3")));
+        Assert.assertFalse(algorithm.compare(msList, Arrays.asList("10.1.1.0", "10.2.2.2")));
+        Assert.assertFalse(algorithm.compare(msList, new ArrayList<String>()));
+        Assert.assertFalse(algorithm.compare(msList, null));
+    }
+}
\ No newline at end of file
diff --git a/server/test/resources/createNetworkOffering.xml b/server/test/resources/createNetworkOffering.xml
index 126e265..68ff007 100644
--- a/server/test/resources/createNetworkOffering.xml
+++ b/server/test/resources/createNetworkOffering.xml
@@ -54,4 +54,5 @@
     <bean id="userIpAddressDetailsDao" class="org.apache.cloudstack.resourcedetail.dao.UserIpAddressDetailsDaoImpl" />
     <bean id="loadBalancerVMMapDaoImpl" class="com.cloud.network.dao.LoadBalancerVMMapDaoImpl" />
     <bean id="imageStoreDaoImpl" class="org.apache.cloudstack.storage.datastore.db.ImageStoreDaoImpl" />
+    <bean id="messageBus" class="org.apache.cloudstack.framework.messagebus.MessageBusBase" />
 </beans>
diff --git a/server/test/resources/testContext.xml b/server/test/resources/testContext.xml
index e842448..c267648 100644
--- a/server/test/resources/testContext.xml
+++ b/server/test/resources/testContext.xml
@@ -79,9 +79,10 @@
     <constructor-arg ref="transportProvider" />
     <property name="messageSerializer" ref="messageSerializer" />
   </bean>
-
-  <bean id="eventBus" class = "org.apache.cloudstack.framework.eventbus.EventBusBase" />
-
+
+  <bean id="eventBus" class = "org.apache.cloudstack.framework.eventbus.EventBusBase" />
+  <bean id="messageBus" class="org.apache.cloudstack.framework.messagebus.MessageBusBase" />
+
   <bean id="apiServlet" class = "com.cloud.api.ApiServlet" />
 
 </beans>
diff --git a/services/secondary-storage/controller/src/org/apache/cloudstack/secondarystorage/SecondaryStorageManagerImpl.java b/services/secondary-storage/controller/src/org/apache/cloudstack/secondarystorage/SecondaryStorageManagerImpl.java
index a158c9c..66c436b 100644
--- a/services/secondary-storage/controller/src/org/apache/cloudstack/secondarystorage/SecondaryStorageManagerImpl.java
+++ b/services/secondary-storage/controller/src/org/apache/cloudstack/secondarystorage/SecondaryStorageManagerImpl.java
@@ -30,7 +30,7 @@ import java.util.Map;
 import javax.inject.Inject;
 import javax.naming.ConfigurationException;
 
-import org.apache.cloudstack.config.ApiServiceConfiguration;
+import org.apache.cloudstack.agent.lb.IndirectAgentLB;
 import org.apache.cloudstack.context.CallContext;
 import org.apache.cloudstack.engine.orchestration.service.NetworkOrchestrationService;
 import org.apache.cloudstack.engine.subsystem.api.storage.DataStore;
@@ -89,12 +89,12 @@ import com.cloud.info.RunningHostInfoAgregator.ZoneHostInfo;
 import com.cloud.network.Network;
 import com.cloud.network.NetworkModel;
 import com.cloud.network.Networks.TrafficType;
+import com.cloud.network.StorageNetworkManager;
 import com.cloud.network.dao.IPAddressDao;
 import com.cloud.network.dao.IPAddressVO;
 import com.cloud.network.dao.NetworkDao;
 import com.cloud.network.dao.NetworkVO;
 import com.cloud.network.rules.RulesManager;
-import com.cloud.network.StorageNetworkManager;
 import com.cloud.offering.NetworkOffering;
 import com.cloud.offering.ServiceOffering;
 import com.cloud.offerings.dao.NetworkOfferingDao;
@@ -246,6 +246,9 @@ public class SecondaryStorageManagerImpl extends ManagerBase implements Secondar
     VolumeDataStoreDao _volumeStoreDao;
     @Inject
     private ImageStoreDetailsUtil imageStoreDetailsUtil;
+    @Inject
+    private IndirectAgentLB indirectAgentLB;
+
     private long _capacityScanInterval = DEFAULT_CAPACITY_SCAN_INTERVAL;
     private int _secStorageVmMtuSize;
 
@@ -1119,7 +1122,7 @@ public class SecondaryStorageManagerImpl extends ManagerBase implements Secondar
 
         StringBuilder buf = profile.getBootArgsBuilder();
         buf.append(" template=domP type=secstorage");
-        buf.append(" host=").append(StringUtils.shuffleCSVList(ApiServiceConfiguration.ManagementHostIPAdr.value()));
+        buf.append(" host=").append(StringUtils.toCSVList(indirectAgentLB.getManagementServerList(dest.getHost().getId(), dest.getDataCenter().getId(), null)));
         buf.append(" port=").append(_mgmtPort);
         buf.append(" name=").append(profile.getVirtualMachine().getHostName());
 
diff --git a/utils/src/main/java/com/cloud/utils/StringUtils.java b/utils/src/main/java/com/cloud/utils/StringUtils.java
index 6ada2ad..f7ef0bc 100644
--- a/utils/src/main/java/com/cloud/utils/StringUtils.java
+++ b/utils/src/main/java/com/cloud/utils/StringUtils.java
@@ -21,12 +21,10 @@ package com.cloud.utils;
 
 import java.nio.charset.Charset;
 import java.util.ArrayList;
-import java.util.Collections;
 import java.util.HashMap;
 import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
-import java.util.Random;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 
@@ -323,9 +321,7 @@ public class StringUtils {
         return listOfChunks;
     }
 
-    public static String shuffleCSVList(final String csvList) {
-        List<String> list = csvTagsToList(csvList);
-        Collections.shuffle(list, new Random(System.nanoTime()));
-        return join(list, ",");
+    public static String toCSVList(final List<String> csvList) {
+        return join(csvList, ",");
     }
 }
diff --git a/utils/src/test/java/com/cloud/utils/StringUtilsTest.java b/utils/src/test/java/com/cloud/utils/StringUtilsTest.java
index e8e62b0..09a6c71 100644
--- a/utils/src/test/java/com/cloud/utils/StringUtilsTest.java
+++ b/utils/src/test/java/com/cloud/utils/StringUtilsTest.java
@@ -20,9 +20,8 @@
 package com.cloud.utils;
 
 import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertTrue;
 
 import java.nio.charset.Charset;
 import java.util.ArrayList;
@@ -254,13 +253,9 @@ public class StringUtilsTest {
     }
 
     @Test
-    public void testShuffleCSVList() {
+    public void testToCSVList() {
         String input = "one,two,three,four,five,six,seven,eight,nine,ten";
-        String output = StringUtils.shuffleCSVList(input);
-        assertFalse(input.equals(output));
-
-        input = "only-one";
-        output = StringUtils.shuffleCSVList("only-one");
+        String output = StringUtils.toCSVList(Arrays.asList(input.split(",")));
         assertTrue(input.equals(output));
     }
 }

-- 
To stop receiving notification emails like this one, please contact
rohit@apache.org.