Bug 1897 - Implementing NeutronLoadBalancerPoolAware interface 55/11255/1
authorSrini Seetharaman <srini.seetharaman@gmail.com>
Tue, 16 Sep 2014 21:16:36 +0000 (14:16 -0700)
committerSrini Seetharaman <srini.seetharaman@gmail.com>
Tue, 16 Sep 2014 21:17:20 +0000 (14:17 -0700)
1. Adding LBaaSPoolHandler class and changes to Activator
2. Allowing any order of VIP and Pool creation, instead of Pool
   and members first as it was implemented earlier.
3. Removing hack in LBaaSPoolMemberHandler member deletion and
   recomputing indices inline without enqueuing another event.

Note: With current implementation:
- It is possible to have more than 1 VIP per pool
- Create pool and VIP in any order
- There can only be 1 VIP per subnet.
- Two pools cannot share the same VIP.

Change-Id: I38d2adf07b70393a51d5ceaa467022426f3146fc
Signed-off-by: Srini Seetharaman <srini.seetharaman@gmail.com>
openstack/net-virt/src/main/java/org/opendaylight/ovsdb/openstack/netvirt/Activator.java
openstack/net-virt/src/main/java/org/opendaylight/ovsdb/openstack/netvirt/LBaaSHandler.java
openstack/net-virt/src/main/java/org/opendaylight/ovsdb/openstack/netvirt/LBaaSPoolHandler.java [new file with mode: 0755]
openstack/net-virt/src/main/java/org/opendaylight/ovsdb/openstack/netvirt/LBaaSPoolMemberHandler.java
openstack/net-virt/src/main/java/org/opendaylight/ovsdb/openstack/netvirt/api/LoadBalancerConfiguration.java

index da8c9d5daaca6745c8ded8e902e62d7a6f149bf3..48bc3fd14ae628e0b737f0d3f5974634bcfbfa81 100644 (file)
@@ -19,9 +19,9 @@ import org.opendaylight.controller.networkconfig.neutron.INeutronFirewallRuleAwa
 import org.opendaylight.controller.networkconfig.neutron.INeutronFloatingIPAware;
 import org.opendaylight.controller.networkconfig.neutron.INeutronLoadBalancerAware;
 import org.opendaylight.controller.networkconfig.neutron.INeutronLoadBalancerCRUD;
+import org.opendaylight.controller.networkconfig.neutron.INeutronLoadBalancerPoolAware;
 import org.opendaylight.controller.networkconfig.neutron.INeutronLoadBalancerPoolCRUD;
 import org.opendaylight.controller.networkconfig.neutron.INeutronLoadBalancerPoolMemberAware;
-import org.opendaylight.controller.networkconfig.neutron.INeutronLoadBalancerPoolMemberCRUD;
 import org.opendaylight.controller.networkconfig.neutron.INeutronNetworkAware;
 import org.opendaylight.controller.networkconfig.neutron.INeutronNetworkCRUD;
 import org.opendaylight.controller.networkconfig.neutron.INeutronPortAware;
@@ -109,6 +109,7 @@ public class Activator extends ComponentActivatorAbstractBase {
                         EventDispatcherImpl.class,
                         FWaasHandler.class,
                         LBaaSHandler.class,
+                        LBaaSPoolHandler.class,
                         LBaaSPoolMemberHandler.class,
                         NeutronL3Adapter.class,
                         OpenstackRouter.class,
@@ -258,7 +259,21 @@ public class Activator extends ComponentActivatorAbstractBase {
             c.add(createServiceDependency().setService(INeutronPortCRUD.class).setRequired(true));
             c.add(createServiceDependency().setService(INeutronLoadBalancerCRUD.class).setRequired(true));
             c.add(createServiceDependency().setService(INeutronLoadBalancerPoolCRUD.class).setRequired(true));
-            c.add(createServiceDependency().setService(INeutronLoadBalancerPoolMemberCRUD.class).setRequired(true));
+            c.add(createServiceDependency().setService(LoadBalancerProvider.class).setRequired(true));
+            c.add(createServiceDependency().setService(ISwitchManager.class).setRequired(true));
+        }
+
+        if (imp.equals(LBaaSPoolHandler.class)) {
+            Properties lbaasPoolHandlerProperties = new Properties();
+            lbaasPoolHandlerProperties.put(Constants.EVENT_HANDLER_TYPE_PROPERTY,
+                    AbstractEvent.HandlerType.NEUTRON_LOAD_BALANCER_POOL);
+            c.setInterface(new String[] {INeutronLoadBalancerPoolAware.class.getName(),
+                    AbstractHandler.class.getName()},
+                    lbaasPoolHandlerProperties);
+            c.add(createServiceDependency().setService(EventDispatcher.class).setRequired(true));
+            c.add(createServiceDependency().setService(INeutronPortCRUD.class).setRequired(true));
+            c.add(createServiceDependency().setService(INeutronLoadBalancerCRUD.class).setRequired(true));
+            c.add(createServiceDependency().setService(INeutronLoadBalancerPoolCRUD.class).setRequired(true));
             c.add(createServiceDependency().setService(LoadBalancerProvider.class).setRequired(true));
             c.add(createServiceDependency().setService(ISwitchManager.class).setRequired(true));
         }
index cfb39aacecf2843eda5b71153e1ded80b86c9137..3f76f9fa54e67c973c6cc2a37040597bad9e1619 100644 (file)
@@ -13,7 +13,6 @@ package org.opendaylight.ovsdb.openstack.netvirt;
 import org.opendaylight.controller.networkconfig.neutron.INeutronLoadBalancerAware;
 import org.opendaylight.controller.networkconfig.neutron.INeutronLoadBalancerCRUD;
 import org.opendaylight.controller.networkconfig.neutron.INeutronLoadBalancerPoolCRUD;
-import org.opendaylight.controller.networkconfig.neutron.INeutronLoadBalancerPoolMemberCRUD;
 import org.opendaylight.controller.networkconfig.neutron.INeutronPortCRUD;
 import org.opendaylight.controller.networkconfig.neutron.NeutronLoadBalancer;
 import org.opendaylight.controller.networkconfig.neutron.NeutronLoadBalancerPool;
@@ -50,18 +49,14 @@ public class LBaaSHandler extends AbstractHandler
     // The implementation for each of these services is resolved by the OSGi Service Manager
     private volatile INeutronLoadBalancerCRUD neutronLBCache;
     private volatile INeutronLoadBalancerPoolCRUD neutronLBPoolCache;
-    private volatile INeutronLoadBalancerPoolMemberCRUD neutronLBPoolMemberCache;
     private volatile INeutronPortCRUD neutronPortsCache;
     private volatile LoadBalancerProvider loadBalancerProvider;
     private volatile ISwitchManager switchManager;
 
     @Override
     public int canCreateNeutronLoadBalancer(NeutronLoadBalancer neutronLB) {
-        LoadBalancerConfiguration lbConfig = extractLBConfiguration(neutronLB);
-        if (!lbConfig.isValid())
-            return HttpURLConnection.HTTP_NOT_ACCEPTABLE;
-        else
-            return HttpURLConnection.HTTP_OK;
+        //Always allowed and not wait for pool and members to be created
+        return HttpURLConnection.HTTP_OK;
     }
 
     @Override
@@ -91,11 +86,8 @@ public class LBaaSHandler extends AbstractHandler
 
     @Override
     public int canUpdateNeutronLoadBalancer(NeutronLoadBalancer delta, NeutronLoadBalancer original) {
-        LoadBalancerConfiguration lbConfig = extractLBConfiguration(delta);
-        if (!lbConfig.isValid())
-            return HttpURLConnection.HTTP_NOT_ACCEPTABLE;
-        else
-            return HttpURLConnection.HTTP_OK;
+        //Update allowed anytime, even when the LB has no active pool yet
+        return HttpURLConnection.HTTP_OK;
     }
 
     @Override
@@ -106,11 +98,8 @@ public class LBaaSHandler extends AbstractHandler
 
     @Override
     public int canDeleteNeutronLoadBalancer(NeutronLoadBalancer neutronLB) {
-        LoadBalancerConfiguration lbConfig = extractLBConfiguration(neutronLB);
-        if (lbConfig == null)
-            return HttpURLConnection.HTTP_NOT_ACCEPTABLE;
-        else
-            return HttpURLConnection.HTTP_OK;
+        //Always allowed and not wait for pool to stop using it
+        return HttpURLConnection.HTTP_OK;
     }
 
     @Override
@@ -207,6 +196,10 @@ public class LBaaSHandler extends AbstractHandler
                     memberID = neutronLBPoolMember.getPoolMemberID();
                     memberIP = neutronLBPoolMember.getPoolMemberAddress();
                     memberPort = neutronLBPoolMember.getPoolMemberProtoPort();
+                    if (memberSubnetID == null || memberID == null || memberIP == null || memberPort == null) {
+                        logger.debug("Neutron LB pool member details incomplete: {}", neutronLBPoolMember);
+                        continue;
+                    }
                     memberMAC = NeutronCacheUtils.getMacAddress(neutronPortsCache, memberIP);
                     if (memberMAC == null)
                         continue;
diff --git a/openstack/net-virt/src/main/java/org/opendaylight/ovsdb/openstack/netvirt/LBaaSPoolHandler.java b/openstack/net-virt/src/main/java/org/opendaylight/ovsdb/openstack/netvirt/LBaaSPoolHandler.java
new file mode 100755 (executable)
index 0000000..0b9c381
--- /dev/null
@@ -0,0 +1,244 @@
+/*
+ * Copyright (C) 2014 SDN Hub, LLC.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Authors : Srini Seetharaman
+ */
+
+package org.opendaylight.ovsdb.openstack.netvirt;
+
+import org.opendaylight.controller.networkconfig.neutron.INeutronLoadBalancerCRUD;
+import org.opendaylight.controller.networkconfig.neutron.INeutronLoadBalancerPoolAware;
+import org.opendaylight.controller.networkconfig.neutron.INeutronLoadBalancerPoolCRUD;
+import org.opendaylight.controller.networkconfig.neutron.INeutronPortCRUD;
+import org.opendaylight.controller.networkconfig.neutron.NeutronLoadBalancer;
+import org.opendaylight.controller.networkconfig.neutron.NeutronLoadBalancerPool;
+import org.opendaylight.controller.networkconfig.neutron.NeutronLoadBalancerPoolMember;
+import org.opendaylight.controller.sal.core.Node;
+import org.opendaylight.controller.switchmanager.ISwitchManager;
+import org.opendaylight.ovsdb.openstack.netvirt.api.Action;
+import org.opendaylight.ovsdb.openstack.netvirt.api.LoadBalancerConfiguration;
+import org.opendaylight.ovsdb.openstack.netvirt.api.LoadBalancerProvider;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.common.base.Preconditions;
+import com.google.common.collect.Lists;
+
+import java.net.HttpURLConnection;
+import java.util.List;
+
+/**
+ * Handle requests for OpenStack Neutron v2.0 LBaaS API calls for
+ * /v2.0/pools . It is possible that each pool spans multiple subnets.
+ * In that case, the user should be creating a separate VIP for each subnet.
+ */
+
+public class LBaaSPoolHandler extends AbstractHandler
+        implements INeutronLoadBalancerPoolAware {
+
+    private static final Logger logger = LoggerFactory.getLogger(LBaaSPoolHandler.class);
+
+    // The implementation for each of these services is resolved by the OSGi Service Manager
+    private volatile INeutronLoadBalancerPoolCRUD neutronLBPoolCache;
+    private volatile INeutronLoadBalancerCRUD neutronLBCache;
+    private volatile INeutronPortCRUD neutronPortsCache;
+    private volatile LoadBalancerProvider loadBalancerProvider;
+    private volatile ISwitchManager switchManager;
+
+    @Override
+    public int canCreateNeutronLoadBalancerPool(NeutronLoadBalancerPool neutronLBPool) {
+        String poolProtocol = neutronLBPool.getLoadBalancerPoolProtocol();
+        if (poolProtocol == null)
+            return HttpURLConnection.HTTP_BAD_REQUEST;
+        else if (!(poolProtocol.equalsIgnoreCase(LoadBalancerConfiguration.PROTOCOL_HTTP) ||
+                poolProtocol.equalsIgnoreCase(LoadBalancerConfiguration.PROTOCOL_HTTPS)))
+            return HttpURLConnection.HTTP_NOT_ACCEPTABLE;
+        else
+            return HttpURLConnection.HTTP_OK;
+    }
+
+    @Override
+    public void neutronLoadBalancerPoolCreated(NeutronLoadBalancerPool neutronLBPool) {
+        logger.debug("Neutron LB Pool Creation : {}", neutronLBPool.toString());
+        enqueueEvent(new NorthboundEvent(neutronLBPool, Action.ADD));
+    }
+
+    /**
+     * Assuming that the pool information is fully populated before this call is made,
+     * we go with creating the LoadBalancerConfiguration object for this call with
+     * all information that is necessary to insert flow_mods
+     */
+    private void doNeutronLoadBalancerPoolCreate(NeutronLoadBalancerPool neutronLBPool) {
+        Preconditions.checkNotNull(loadBalancerProvider);
+        List<LoadBalancerConfiguration> lbConfigList = extractLBConfiguration(neutronLBPool);
+        if (lbConfigList == null) {
+            logger.trace("Neutron LB configuration invalid for pool {} ", neutronLBPool.getLoadBalancerPoolID());
+        } else if (lbConfigList.size() == 0) {
+            logger.trace("No Neutron LB VIP not created yet for pool {} ", neutronLBPool.getLoadBalancerPoolID());
+        } else if (this.switchManager.getNodes().size() == 0) {
+            logger.trace("Noop with LB pool {} creation because no nodes available.", neutronLBPool.getLoadBalancerPoolID());
+        } else {
+            for (LoadBalancerConfiguration lbConfig: lbConfigList) {
+                if (!lbConfig.isValid()) {
+                    logger.debug("Neutron LB pool configuration invalid for {} ", lbConfig.getName());
+                    continue;
+                } else {
+                    for (Node node: this.switchManager.getNodes())
+                        loadBalancerProvider.programLoadBalancerRules(node, lbConfig, Action.ADD);
+                }
+            }
+        }
+    }
+
+    @Override
+    public int canUpdateNeutronLoadBalancerPool(NeutronLoadBalancerPool delta, NeutronLoadBalancerPool original) {
+        return HttpURLConnection.HTTP_NOT_IMPLEMENTED;
+    }
+
+    @Override
+    public void neutronLoadBalancerPoolUpdated(NeutronLoadBalancerPool neutronLBPool) {
+        logger.debug("Neutron LB Pool Update : {}", neutronLBPool.toString());
+        enqueueEvent(new NorthboundEvent(neutronLBPool, Action.UPDATE));
+    }
+
+    @Override
+    public int canDeleteNeutronLoadBalancerPool(NeutronLoadBalancerPool neutronLBPool) {
+        String poolProtocol = neutronLBPool.getLoadBalancerPoolProtocol();
+        if (poolProtocol == null)
+            return HttpURLConnection.HTTP_BAD_REQUEST;
+        else if (!(poolProtocol.equalsIgnoreCase(LoadBalancerConfiguration.PROTOCOL_HTTP) ||
+                poolProtocol.equalsIgnoreCase(LoadBalancerConfiguration.PROTOCOL_HTTPS)))
+            return HttpURLConnection.HTTP_NOT_ACCEPTABLE;
+        else
+            return HttpURLConnection.HTTP_OK;
+    }
+
+    @Override
+    public void neutronLoadBalancerPoolDeleted(NeutronLoadBalancerPool neutronLBPool) {
+        logger.debug("Neutron LB Pool Deletion : {}", neutronLBPool.toString());
+        enqueueEvent(new NorthboundEvent(neutronLBPool, Action.DELETE));
+    }
+
+    private void doNeutronLoadBalancerPoolDelete(NeutronLoadBalancerPool neutronLBPool) {
+        Preconditions.checkNotNull(loadBalancerProvider);
+
+        List<LoadBalancerConfiguration> lbConfigList = extractLBConfiguration(neutronLBPool);
+        if (lbConfigList == null) {
+            logger.trace("Neutron LB configuration invalid for pool {} ", neutronLBPool.getLoadBalancerPoolID());
+        } else if (lbConfigList.size() == 0) {
+            logger.trace("No Neutron LB VIP not created yet for pool {} ", neutronLBPool.getLoadBalancerPoolID());
+        } else if (this.switchManager.getNodes().size() == 0) {
+            logger.trace("Noop with LB pool {} deletion because no nodes available.", neutronLBPool.getLoadBalancerPoolID());
+        } else {
+            for (LoadBalancerConfiguration lbConfig: lbConfigList) {
+                if (!lbConfig.isValid()) {
+                    logger.debug("Neutron LB pool configuration invalid for {} ", lbConfig.getName());
+                    continue;
+                } else {
+                    for (Node node: this.switchManager.getNodes())
+                        loadBalancerProvider.programLoadBalancerRules(node, lbConfig, Action.DELETE);
+                }
+            }
+        }
+    }
+
+    /**
+     * Process the event.
+     *
+     * @param abstractEvent the {@link org.opendaylight.ovsdb.openstack.netvirt.AbstractEvent} event to be handled.
+     * @see org.opendaylight.ovsdb.openstack.netvirt.api.EventDispatcher
+     */
+    @Override
+    public void processEvent(AbstractEvent abstractEvent) {
+        logger.debug("Processing Loadbalancer event " + abstractEvent);
+        if (!(abstractEvent instanceof NorthboundEvent)) {
+            logger.error("Unable to process abstract event " + abstractEvent);
+            return;
+        }
+        NorthboundEvent ev = (NorthboundEvent) abstractEvent;
+        switch (ev.getAction()) {
+            case ADD:
+                doNeutronLoadBalancerPoolCreate(ev.getLoadBalancerPool());
+                break;
+            case DELETE:
+                doNeutronLoadBalancerPoolDelete(ev.getLoadBalancerPool());
+                break;
+            case UPDATE:
+                /**
+                 * Typical upgrade involves changing algorithm. Right now
+                 * we do not support this flexibility. TODO
+                 */
+                logger.warn("Load balancer pool update is not supported");
+                break;
+            default:
+                logger.warn("Unable to process event action " + ev.getAction());
+                break;
+        }
+    }
+
+    /**
+     * Useful utility for extracting the loadbalancer instance. With
+     * each LB pool, we allow multiple VIP and LB to be instantiated.
+     */
+    public List<LoadBalancerConfiguration> extractLBConfiguration(NeutronLoadBalancerPool neutronLBPool) {
+        String poolProtocol = neutronLBPool.getLoadBalancerPoolProtocol();
+        if (poolProtocol == null)
+            return null;
+        if (!(poolProtocol.equalsIgnoreCase(LoadBalancerConfiguration.PROTOCOL_HTTP) ||
+                poolProtocol.equalsIgnoreCase(LoadBalancerConfiguration.PROTOCOL_HTTPS)))
+            return null;
+
+        List<NeutronLoadBalancerPoolMember> poolMembers = neutronLBPool.getLoadBalancerPoolMembers();
+        if (poolMembers.size() == 0) {
+            logger.debug("Neutron LB pool is empty: {}", neutronLBPool);
+            return null;
+        }
+
+        List<LoadBalancerConfiguration> lbConfigList = Lists.newLinkedList();
+
+        /* Iterate over all the Loadbalancers created so far and identify VIP
+         */
+        String loadBalancerSubnetID, loadBalancerVip=null, loadBalancerName=null;
+        for (NeutronLoadBalancer neutronLB: neutronLBCache.getAllNeutronLoadBalancers()) {
+            loadBalancerSubnetID = neutronLB.getLoadBalancerVipSubnetID();
+            loadBalancerName = neutronLB.getLoadBalancerName();
+            loadBalancerVip = neutronLB.getLoadBalancerVipAddress();
+            LoadBalancerConfiguration lbConfig = new LoadBalancerConfiguration(loadBalancerName, loadBalancerVip);
+
+            /* Iterate over all the members in this pool and find those in same
+             * subnet as the VIP. Those will be included in the lbConfigList
+             */
+            String memberSubnetID, memberIP, memberID, memberMAC;
+            Integer memberPort;
+            Boolean memberAdminStateIsUp;
+            for (NeutronLoadBalancerPoolMember neutronLBPoolMember: neutronLBPool.getLoadBalancerPoolMembers()) {
+                memberAdminStateIsUp = neutronLBPoolMember.getPoolMemberAdminStateIsUp();
+                memberSubnetID = neutronLBPoolMember.getPoolMemberSubnetID();
+                if (memberSubnetID == null || memberAdminStateIsUp == null)
+                    continue;
+                else if (memberSubnetID.equals(loadBalancerSubnetID) && memberAdminStateIsUp.booleanValue()) {
+                    memberID = neutronLBPoolMember.getPoolMemberID();
+                    memberIP = neutronLBPoolMember.getPoolMemberAddress();
+                    memberPort = neutronLBPoolMember.getPoolMemberProtoPort();
+                    if (memberSubnetID == null || memberID == null || memberIP == null || memberPort == null) {
+                        logger.debug("Neutron LB pool member details incomplete: {}", neutronLBPoolMember);
+                        continue;
+                    }
+                    memberMAC = NeutronCacheUtils.getMacAddress(neutronPortsCache, memberIP);
+                    if (memberMAC == null)
+                        continue;
+                    lbConfig.addMember(memberID, memberIP, memberMAC, poolProtocol, memberPort);
+                }
+            }
+
+            if (lbConfig.getMembers().size() > 0)
+                lbConfigList.add(lbConfig);
+        }
+
+        return lbConfigList;
+    }
+}
index cd9417ba7681b04d93d100d464b2b5884539331e..8c26f4a6a69ac904b829e4088b16462d91bd383d 100755 (executable)
@@ -88,13 +88,7 @@ public class LBaaSPoolMemberHandler extends AbstractHandler
 
     @Override
     public int canUpdateNeutronLoadBalancerPoolMember(NeutronLoadBalancerPoolMember delta, NeutronLoadBalancerPoolMember original) {
-        LoadBalancerConfiguration lbConfig = extractLBConfiguration(delta);
-        if (lbConfig == null)
-            return HttpURLConnection.HTTP_BAD_REQUEST;
-        else if (!lbConfig.isValid())
-            return HttpURLConnection.HTTP_NOT_ACCEPTABLE;
-        else
-            return HttpURLConnection.HTTP_OK;
+        return HttpURLConnection.HTTP_NOT_IMPLEMENTED;
     }
 
     @Override
@@ -131,20 +125,17 @@ public class LBaaSPoolMemberHandler extends AbstractHandler
         } else if (!lbConfig.isValid()) {
             logger.trace("Neutron LB pool configuration invalid for {} ", lbConfig.getName());
         } else if (this.switchManager.getNodes().size() == 0) {
-            logger.trace("Noop with LB pool member {} creation because no nodes available.", neutronLBPoolMember.getPoolMemberID());
+            logger.trace("Noop with LB pool member {} deletion because no nodes available.", neutronLBPoolMember.getPoolMemberID());
         } else {
             /* As of now, deleting a member involves recomputing member indices.
              * This is best done through a complete update of the load balancer instance.
              */
-            for (Node node: this.switchManager.getNodes())
-                loadBalancerProvider.programLoadBalancerRules(node, lbConfig, Action.DELETE);
+            LoadBalancerConfiguration newLBConfig = new LoadBalancerConfiguration(lbConfig);
+            newLBConfig.removeMember(neutronLBPoolMember.getPoolMemberID());
 
-            for (NeutronLoadBalancer neutronLB: neutronLBCache.getAllNeutronLoadBalancers()) {
-                String loadBalancerSubnetID = neutronLB.getLoadBalancerVipSubnetID();
-                if (neutronLBPoolMember.getPoolMemberSubnetID().equals(loadBalancerSubnetID)) {
-                    enqueueEvent(new NorthboundEvent(neutronLB, Action.ADD));
-                    break;
-                }
+            for (Node node: this.switchManager.getNodes()) {
+                loadBalancerProvider.programLoadBalancerRules(node, lbConfig, Action.DELETE);
+                loadBalancerProvider.programLoadBalancerRules(node, newLBConfig, Action.ADD);
             }
         }
     }
@@ -173,7 +164,7 @@ public class LBaaSPoolMemberHandler extends AbstractHandler
             case UPDATE:
                 /**
                  * Typical upgrade involves changing weights. Since weights are not
-                 * supported yet, updates are not supported either.
+                 * supported yet, updates are not supported either. TODO
                  */
                 logger.warn("Load balancer pool member update is not supported");
                 break;
index f5a0281835357c31d31ef9617cf80fd436af6386..7965a9e96318bad5979a498bef4ded1ac283910b 100755 (executable)
@@ -109,6 +109,12 @@ public class LoadBalancerConfiguration {
         this.vip = vip;
     }
 
+    public LoadBalancerConfiguration(LoadBalancerConfiguration lbConfig) {
+        this.members = Maps.newHashMap(lbConfig.getMembers());
+        this.name = lbConfig.getName();
+        this.vip = lbConfig.getVip();
+    }
+
     public Map<String, LoadBalancerPoolMember> getMembers() {
         return this.members;
     }
@@ -127,6 +133,11 @@ public class LoadBalancerConfiguration {
     }
     public Map<String, LoadBalancerPoolMember> removeMember(String uuid) {
         this.members.remove(uuid);
+        /* Update indices of all other members
+         */
+        int index = 0;
+        for(Map.Entry<String, LoadBalancerPoolMember> entry : this.getMembers().entrySet())
+            ((LoadBalancerPoolMember) entry.getValue()).setIndex(index++);
         return this.members;
     }