Mark Bergsma has uploaded a new change for review.

  https://gerrit.wikimedia.org/r/187346

Change subject: Add no-gravity configuration option
......................................................................

Add no-gravity configuration option

If no-gravity = True on an LVS service, instead of removing a server
from IPVS for depooling, its weight is set to 0 instead.

This adds a lot of complications, as previously held invariants
(e.g. (server.pooled <==> server listed in IPVS)) no longer hold.

* server.pooled has been renamed to server.listed to clarify the
distinction between "being used" and "being configured in IPVS"
* server.listWeight has been added to distinguish between desired server
weight when pooled vs weight configured in IPVS

The existing code is also inconsistent with (boolean) server state
variables that indicate _intent_ and _current state_. This is (at
least partially) resolved by creating new variables:

* server.list vs server.listed
* server.pool vs server.pooled

These are now maintained as separate concerns in the code. Tests still
need to be adapted.

Bug: T86650
Change-Id: I1f8aec57401481520eec80c7f9bfd0ca5271f7c2
---
M pybal/ipvs.py
M pybal/pybal.py
2 files changed, 98 insertions(+), 26 deletions(-)


  git pull ssh://gerrit.wikimedia.org:29418/operations/debs/pybal 
refs/changes/46/187346/1

diff --git a/pybal/ipvs.py b/pybal/ipvs.py
index 6d4aa76..25cba80 100644
--- a/pybal/ipvs.py
+++ b/pybal/ipvs.py
@@ -141,8 +141,8 @@
         cmd = " ".join(['-a', cls.subCommandService(service), 
cls.subCommandServer(server)])
         
         # Include weight if specified
-        if server.weight:
-            cmd += ' -w %d' % server.weight
+        if server.listWeight:
+            cmd += ' -w %d' % server.listWeight
 
         return cmd
     
@@ -159,8 +159,8 @@
         cmd = " ".join(['-e', cls.subCommandService(service), 
cls.subCommandServer(server)])
         
         # Include weight if specified
-        if server.weight:
-            cmd += ' -w %d' % server.weight
+        if server.listWeight:
+            cmd += ' -w %d' % server.listWeight
         
         return cmd
 
@@ -192,6 +192,7 @@
         self.configuration = configuration
 
         self.ipvsManager.DryRun = configuration.getboolean('dryrun', False)
+        self.noGravity = configuration.getboolean('no-gravity', False)
         
         if self.configuration.getboolean('bgp', False):
             from pybal import BGPFailover
@@ -228,8 +229,15 @@
                 [self.ipvsManager.commandEditServer(self.service(), server) 
for server in newServers & self.servers] + \
                 [self.ipvsManager.commandRemoveServer(self.service(), server) 
for server in self.servers - newServers]
 
-        self.servers = newServers
         self.ipvsManager.modifyState(cmdList)
+
+        # Update server.listed & server.pooled with new status
+        for server in self.servers + newServers:
+            server.listed = server in newServers
+            server.pooled = (server.listed and server.listWeight > 0)
+
+        self.servers = newServers
+
     
     def addServer(self, server):
         """Adds (pools) a single Server to the LVS state"""
@@ -243,7 +251,8 @@
         self.servers.add(server)
         
         self.ipvsManager.modifyState(cmdList)
-        server.pooled = True
+        server.listed = True
+        server.pooled = (server.weight != 0)
     
     def removeServer(self, server):
         """Removes (depools) a single Server from the LVS state"""
@@ -252,8 +261,25 @@
         
         self.servers.remove(server) # May raise KeyError
 
-        server.pooled = False        
         self.ipvsManager.modifyState(cmdList)
+        server.listed = False
+        server.pooled = False
+
+    
+    def editServer(self, server):
+        """
+        Changes the IPVS attributes of an existing server.
+        
+        Handles weight 0 as special (depooled) case
+        Does NOT modify server.weight
+        """
+
+        assert server.listed and server in self.servers
+
+        cmdList = [self.ipvsManager.commandEditServer(self.service(), server)]
+        self.ipvsManager.modifyState(cmdList)
+        server.pooled = (server.weight != 0)
+
     
     def initServer(self, server):
         """Initializes a server instance with LVS service specific 
configuration."""
diff --git a/pybal/pybal.py b/pybal/pybal.py
index 6dae0d3..4a4249e 100644
--- a/pybal/pybal.py
+++ b/pybal/pybal.py
@@ -49,15 +49,19 @@
         self.monitors = set()
         
         # A few invariants that SHOULD be maintained (but currently may not 
be):
-        # P0: pooled => enabled /\ ready
-        # P1: up => pooled \/ !enabled \/ !ready
+        # P0: list => enabled /\ ready
+        # P1: up => pool \/ !enabled \/ !ready
         # P2: pooled => up \/ !canDepool
         
-        self.weight = self.DEF_WEIGHT
-        self.up = False
-        self.pooled = False
-        self.enabled = True
-        self.ready = False
+        self.weight = self.DEF_WEIGHT   # Desired weight of the server when 
pooled
+        self.listWeight = None          # Weight the server will actually be 
listed with in IPVS
+        self.up = False                 # True if there is consensus among 
monitors that this server is up
+        self.list = None                # Whether this server will be listed 
in IPVS (regardless of state)
+        self.listed = False             # True IFF added as realserver to IPVS 
(regardless of weight)
+        self.pool = False               # Whether this server will be pooled 
(listed with weight > 0)
+        self.pooled = False             # True IFF listed with weight > 0
+        self.enabled = True             # Whether this server will be 
considered for pooling at all
+        self.ready = False              # Set to True when this server has 
initialized and can be listed
         self.modified = None
     
     def __eq__(self, other):
@@ -156,7 +160,9 @@
 
         self.ready = True
         self.up = self.DEF_STATE
-        self.pooled = self.DEF_STATE
+        self.pool = self.DEF_STATE and self.weight > 0
+        self.maintainState()
+        self.setListWeight()
         self.maintainState()
 
         self.createMonitoringInstances(coordinator)
@@ -218,14 +224,24 @@
                              self.up and "up" or (self.calcPartialStatus() and 
"partially up" or "down"),
                              self.pooled and "pooled" or "not pooled")
 
+    def setListState(self):
+        """Sets the list attributes as appropriate for no-gravity"""
+
+        self.list = self.ready and self.enabled and (self.lvsservice.noGravity 
or self.pool)
+
+        if self.lvsservice.noGravity and not self.pool:
+            self.listWeight = 0
+        else:
+            self.listWeight = self.weight
+
     def maintainState(self):
         """Maintains a few invariants on configuration changes"""
         
         # P0
         if not self.enabled or not self.ready:
-            self.pooled = False
+            self.list = False
         # P1
-        if not self.pooled and self.enabled:
+        if not self.pool and self.enabled:
             self.up = False
 
     def merge(self, configuration):
@@ -289,7 +305,7 @@
 
         # Hand over enabled servers to LVSService
         self.lvsservice.assignServers(
-            set([server for server in self.servers.itervalues() if 
server.pooled]))
+            set([server for server in self.servers.itervalues() if 
server.list]))
     
     def refreshModifiedServers(self):
         """
@@ -300,7 +316,10 @@
             if not server.modified: continue
             
             server.up = server.calcStatus()
-            server.pooled = server.enabled and server.up
+            server.pool = server.enabled and server.up
+            server.setListState()
+            server.maintainState()
+
 
     def resultDown(self, monitor, reason=None):
         """
@@ -327,7 +346,7 @@
         if not server.up and server.calcStatus():
             print self, "Server %s (%s) is up" % (server.host, 
server.textStatus())
             server.up = True
-            if server.enabled and server.ready: self.repool(server)    
+            if server.enabled and server.ready: self.repool(server)
 
     def depool(self, server):
         """Depools a single Server, if possible"""
@@ -335,7 +354,16 @@
         assert server.pooled
         
         if self.canDepool():
-            self.lvsservice.removeServer(server)
+            server.pool = False
+            # Set to weight 0 or remove
+            if self.lvsservice.noGravity:
+                assert server.listed and server.listWeight > 0
+                server.listWeight = 0
+                self.lvsservice.editServer(server)
+            else:
+                assert server.listed
+                self.list = False
+                self.lvsservice.removeServer(server)
             self.pooledDownServers.discard(server)
         else:
             self.pooledDownServers.add(server)
@@ -350,10 +378,18 @@
         assert server.enabled and server.ready
         
         if not server.pooled:
-            self.lvsservice.addServer(server)
+            server.pool = True
+            # Set original weight or re-add
+            if self.lvsservice.noGravity:
+                assert server.listed and server.listWeight == 0
+                self.lvsservice.editServer(server)
+            else:
+                assert not server.listed
+                server.list = True
+                self.lvsservice.addServer(server)
         else:
             print self, "Leaving previously pooled but down server", 
server.host, "pooled"
-        
+
         # If it had been pooled in down state before, remove it from the list
         self.pooledDownServers.discard(server)
 
@@ -361,15 +397,25 @@
         while len(self.pooledDownServers) > 0 and self.canDepool():
             self.depool(self.pooledDownServers.pop())
 
+    def pooledServers(self):
+        """
+        Returns a set of pooled servers.
+
+        This is a subset of self.servers as servers with weight 0
+        are not considered 'pooled'
+        """
+
+        return set([server for server in self.servers.itervalues() if 
server.pooled])
+
     def canDepool(self):
         """Returns a boolean denoting whether another server can be depooled"""
         
         # Construct a list of servers that have status 'down'
         downServers = [server for server in self.servers.itervalues() if not 
server.up]
-        
+
         # The total amount of pooled servers may never drop below a configured 
threshold
-        return len(self.servers) - len(downServers) >= len(self.servers) * 
self.lvsservice.getDepoolThreshold()
-    
+        return len(self.pooledServers) - len(downServers) >= 
len(self.pooledServers) * self.lvsservice.getDepoolThreshold()
+
     def loadServers(self, configURL=None):
         """Periodic task to load a new server list/configuration file from a 
specified URL."""
         

-- 
To view, visit https://gerrit.wikimedia.org/r/187346
To unsubscribe, visit https://gerrit.wikimedia.org/r/settings

Gerrit-MessageType: newchange
Gerrit-Change-Id: I1f8aec57401481520eec80c7f9bfd0ca5271f7c2
Gerrit-PatchSet: 1
Gerrit-Project: operations/debs/pybal
Gerrit-Branch: master
Gerrit-Owner: Mark Bergsma <[email protected]>

_______________________________________________
MediaWiki-commits mailing list
[email protected]
https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits

Reply via email to