Mark Bergsma has uploaded a new change for review. (
https://gerrit.wikimedia.org/r/393097 )
Change subject: [WiP] Support per-service-IP BGP MED values
......................................................................
[WiP] Support per-service-IP BGP MED values
Add a per-service 'bgp-med' configuation option that allows overriding
the global MED value per service.
Note that as multiple services may share a service IP (e.g. port 80 and
443), the MED values are required to be equal.
These changes also add scaffolding for later support for announcing and
retracting service IPs based on the service health.
Bug: T165764
Change-Id: I08687e8072dd8f2ae88833a1660d154ae28c7add
---
M pybal/bgpfailover.py
M pybal/coordinator.py
M pybal/ipvs.py
3 files changed, 59 insertions(+), 27 deletions(-)
git pull ssh://gerrit.wikimedia.org:29418/operations/debs/pybal
refs/changes/97/393097/1
diff --git a/pybal/bgpfailover.py b/pybal/bgpfailover.py
index 1b6865f..70811ed 100755
--- a/pybal/bgpfailover.py
+++ b/pybal/bgpfailover.py
@@ -22,6 +22,7 @@
prefixes = {}
peerings = {}
+ ipServices = {}
def __init__(self, globalConfig):
if not globalConfig.getboolean('bgp', False):
@@ -35,29 +36,22 @@
myASN = self.globalConfig.getint('bgp-local-asn')
asPath = self.globalConfig.get('bgp-as-path', str(myASN))
asPath = [int(asn) for asn in asPath.split()]
- med = self.globalConfig.getint('bgp-med', 0)
- baseAttrs = [bgp.OriginAttribute(), bgp.ASPathAttribute(asPath)]
- if med: baseAttrs.append(bgp.MEDAttribute(med))
- attributes = {}
+ defaultMED = self.globalConfig.getint('bgp-med', 0)
+
try:
- attributes[(bgp.AFI_INET, bgp.SAFI_UNICAST)] =
bgp.FrozenAttributeDict(baseAttrs + [
-
bgp.NextHopAttribute(self.globalConfig['bgp-nexthop-ipv4'])])
+ nexthopIPv4 = self.globalConfig['bgp-nexthop-ipv4']
except KeyError:
if (bgp.AFI_INET, bgp.SAFI_UNICAST) in BGPFailover.prefixes:
raise ValueError("IPv4 BGP NextHop (global configuration
variable 'bgp-nexthop-ipv4') not set")
try:
- attributes[(bgp.AFI_INET6, bgp.SAFI_UNICAST)] =
bgp.FrozenAttributeDict(baseAttrs + [
- bgp.MPReachNLRIAttribute((bgp.AFI_INET6, bgp.SAFI_UNICAST,
-
bgp.IPv6IP(self.globalConfig['bgp-nexthop-ipv6']), []))])
+ nexthopIPv6 = self.globalConfig['bgp-nexthop-ipv6']
except KeyError:
if (bgp.AFI_INET6, bgp.SAFI_UNICAST) in BGPFailover.prefixes:
raise ValueError("IPv6 BGP NextHop (global configuration
variable 'bgp-nexthop-ipv6') not set")
- advertisements = set([bgp.Advertisement(prefix, attributes[af], af)
- for af in attributes.keys()
- for prefix in BGPFailover.prefixes.get(af,
set())])
+ advertisements = self.buildAdvertisements(myASN, asPath,
nexthopIPv4, nexthopIPv6, defaultMED)
bgpPeerAddress = self.globalConfig.get('bgp-peer-address',
'').strip()
if bgpPeerAddress[0] != '[': bgpPeerAddress = "[ \"{}\"
]".format(bgpPeerAddress)
@@ -66,7 +60,7 @@
for peerAddr in peerAddresses:
peering = bgp.NaiveBGPPeering(myASN, peerAddr)
- peering.setEnabledAddressFamilies(set(attributes.keys()))
+ peering.setEnabledAddressFamilies(set(self.prefixes.keys()))
peering.setAdvertisements(advertisements)
log.info("Starting BGP session with peer {}".format(peerAddr))
@@ -102,13 +96,49 @@
peering.setAdvertisements(set())
return peering.manualStop()
+ def buildAdvertisements(self, myASN, asPath, nexthopIPv4, nexthopIPv6,
defaultMED):
+ baseAttrs = [bgp.OriginAttribute(), bgp.ASPathAttribute(asPath)]
+
+ advertisements = set()
+ for af in self.prefixes:
+ for prefix in self.prefixes[af]:
+ if af[0] == (bgp.AFI_INET):
+ attrList = baseAttrs + [bgp.NextHopAttribute(nexthopIPv4)]
+ elif af[0] == (bgp.AFI_INET6):
+ attrList = (baseAttrs
+ + [bgp.MPReachNLRIAttribute((af,
bgp.IPv6IP(nexthopIPv6), []))])
+ else:
+ raise ValueError("Unsupported address family
{}".format(af))
+
+ # This service IP may use a non-default MED
+ med = self.ipServices[prefix][0]['med'] # Guaranteed to exist,
may be None
+ attrList.append(bgp.MEDAttribute(med or defaultMED))
+
+ attributes = bgp.FrozenAttributeDict(attrList)
+ advertisements.add(bgp.Advertisement(prefix, attributes, af))
+
+ return advertisements
+
@classmethod
- def addPrefix(cls, prefix):
- try:
- if ':' not in prefix:
- cls.prefixes.setdefault((bgp.AFI_INET, bgp.SAFI_UNICAST),
set()).add(bgp.IPv4IP(prefix))
- else:
- cls.prefixes.setdefault((bgp.AFI_INET6, bgp.SAFI_UNICAST),
set()).add(bgp.IPv6IP(prefix))
- except NameError:
- # bgp not imported
- pass
+ def associateService(cls, ip, lvsservice, med):
+ if ':' not in ip:
+ af = (bgp.AFI_INET, bgp.SAFI_UNICAST)
+ prefix = bgp.IPv4IP(ip)
+ else:
+ af = (bgp.AFI_INET6, bgp.SAFI_UNICAST)
+ prefix = bgp.IPv6IP(ip)
+
+ # All services need to agree on the same MED for this IP
+ if prefix in cls.ipServices and not med ==
cls.ipServices[prefix][0]['med']:
+ raise ValueError(
+ "LVS service {} MED value {} differs from other MED values for
IP {}".format(
+ lvsservice.name, med, ip))
+
+ service_state = {
+ 'lvsservice': lvsservice,
+ 'af': af,
+ 'med': med
+ }
+
+ cls.ipServices.setdefault(prefix, []).append(service_state)
+ cls.prefixes.setdefault(af, set()).add(prefix)
diff --git a/pybal/coordinator.py b/pybal/coordinator.py
index a526b5b..1bf108c 100755
--- a/pybal/coordinator.py
+++ b/pybal/coordinator.py
@@ -15,6 +15,7 @@
from twisted.python import failure
from pybal import config, util
+from pybal.bgpfailover import BGPFailover
from pybal.metrics import Counter, Gauge
log = util.log
@@ -337,6 +338,12 @@
self.configObserver = config.ConfigurationObserver.fromUrl(self,
configUrl)
self.configObserver.startObserving()
+ if self.lvsservice.configuration.getboolean('bgp', True):
+ # Pass a per-service(-ip) MED if one is provided
+ med = self.lvsservice.configuration.get('bgp-med', None)
+ # Associcate service ip to this coordinator for BGP announcements
+ BGPFailover.associateService(self.lvsservice.ip, self.lvsservice,
med)
+
self.metrics['depool_threshold'].labels(
**self.metric_labels
).set(self.lvsservice.getDepoolThreshold())
diff --git a/pybal/ipvs.py b/pybal/ipvs.py
index 8365e50..7538b9f 100644
--- a/pybal/ipvs.py
+++ b/pybal/ipvs.py
@@ -5,7 +5,6 @@
LVS state/configuration classes for PyBal
"""
from . import util
-from pybal.bgpfailover import BGPFailover
import os
log = util.log
@@ -191,10 +190,6 @@
self.ipvsManager.DryRun = configuration.getboolean('dryrun', False)
self.ipvsManager.Debug = configuration.getboolean('debug', False)
-
- if self.configuration.getboolean('bgp', True):
- # Add service ip to the BGP announcements
- BGPFailover.addPrefix(self.ip)
self.createService()
--
To view, visit https://gerrit.wikimedia.org/r/393097
To unsubscribe, visit https://gerrit.wikimedia.org/r/settings
Gerrit-MessageType: newchange
Gerrit-Change-Id: I08687e8072dd8f2ae88833a1660d154ae28c7add
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