Skip to content

Commit d0eff76

Browse files
Merge pull request #19578 from danwinship/auto-egress-ip-ha
Basic high availability for auto egress IPs
2 parents f0b959b + 0439a83 commit d0eff76

File tree

5 files changed

+553
-84
lines changed

5 files changed

+553
-84
lines changed

pkg/network/node/egressip.go

Lines changed: 90 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -15,13 +15,16 @@ import (
1515
networkapi "github.com/openshift/origin/pkg/network/apis/network"
1616
"github.com/openshift/origin/pkg/network/common"
1717
networkinformers "github.com/openshift/origin/pkg/network/generated/informers/internalversion"
18+
"github.com/openshift/origin/pkg/util/netutils"
1819

1920
"github.com/vishvananda/netlink"
2021
)
2122

2223
type nodeEgress struct {
2324
nodeIP string
25+
sdnIP string
2426
requestedIPs sets.String
27+
offline bool
2528
}
2629

2730
type namespaceEgress struct {
@@ -48,13 +51,14 @@ type egressIPWatcher struct {
4851

4952
networkInformers networkinformers.SharedInformerFactory
5053
iptables *NodeIPTables
54+
vxlanMonitor *egressVXLANMonitor
5155

5256
nodesByNodeIP map[string]*nodeEgress
5357
namespacesByVNID map[uint32]*namespaceEgress
5458
egressIPs map[string]*egressIPInfo
5559

56-
changedEgressIPs []*egressIPInfo
57-
changedNamespaces []*namespaceEgress
60+
changedEgressIPs map[*egressIPInfo]bool
61+
changedNamespaces map[*namespaceEgress]bool
5862

5963
localEgressLink netlink.Link
6064
localEgressNet *net.IPNet
@@ -70,6 +74,9 @@ func newEgressIPWatcher(oc *ovsController, localIP string, masqueradeBit *int32)
7074
nodesByNodeIP: make(map[string]*nodeEgress),
7175
namespacesByVNID: make(map[uint32]*namespaceEgress),
7276
egressIPs: make(map[string]*egressIPInfo),
77+
78+
changedEgressIPs: make(map[*egressIPInfo]bool),
79+
changedNamespaces: make(map[*namespaceEgress]bool),
7380
}
7481
if masqueradeBit != nil {
7582
eip.masqueradeBit = 1 << uint32(*masqueradeBit)
@@ -87,6 +94,10 @@ func (eip *egressIPWatcher) Start(networkInformers networkinformers.SharedInform
8794
eip.networkInformers = networkInformers
8895
eip.iptables = iptables
8996

97+
updates := make(chan *egressVXLANNode)
98+
eip.vxlanMonitor = newEgressVXLANMonitor(eip.oc.ovs, updates)
99+
go eip.watchVXLAN(updates)
100+
90101
eip.watchHostSubnets()
91102
eip.watchNetNamespaces()
92103
return nil
@@ -114,19 +125,15 @@ func (eip *egressIPWatcher) ensureEgressIPInfo(egressIP string) *egressIPInfo {
114125
}
115126

116127
func (eip *egressIPWatcher) egressIPChanged(eg *egressIPInfo) {
117-
eip.changedEgressIPs = append(eip.changedEgressIPs, eg)
128+
eip.changedEgressIPs[eg] = true
118129
for _, ns := range eg.namespaces {
119-
eip.changedNamespaces = append(eip.changedNamespaces, ns)
130+
eip.changedNamespaces[ns] = true
120131
}
121132
}
122133

123134
func (eip *egressIPWatcher) addNode(egressIP string, node *nodeEgress) {
124135
eg := eip.ensureEgressIPInfo(egressIP)
125-
if len(eg.nodes) != 0 {
126-
utilruntime.HandleError(fmt.Errorf("Multiple nodes claiming EgressIP %q (nodes %q, %q)", eg.ip, node.nodeIP, eg.nodes[0].nodeIP))
127-
}
128136
eg.nodes = append(eg.nodes, node)
129-
130137
eip.egressIPChanged(eg)
131138
}
132139

@@ -147,11 +154,7 @@ func (eip *egressIPWatcher) deleteNode(egressIP string, node *nodeEgress) {
147154

148155
func (eip *egressIPWatcher) addNamespace(egressIP string, ns *namespaceEgress) {
149156
eg := eip.ensureEgressIPInfo(egressIP)
150-
if len(eg.namespaces) != 0 {
151-
utilruntime.HandleError(fmt.Errorf("Multiple namespaces claiming EgressIP %q (NetIDs %d, %d)", eg.ip, ns.vnid, eg.namespaces[0].vnid))
152-
}
153157
eg.namespaces = append(eg.namespaces, ns)
154-
155158
eip.egressIPChanged(eg)
156159
}
157160

@@ -179,17 +182,23 @@ func (eip *egressIPWatcher) handleAddOrUpdateHostSubnet(obj, _ interface{}, even
179182
hs := obj.(*networkapi.HostSubnet)
180183
glog.V(5).Infof("Watch %s event for HostSubnet %q", eventType, hs.Name)
181184

182-
eip.updateNodeEgress(hs.HostIP, hs.EgressIPs)
185+
_, cidr, err := net.ParseCIDR(hs.Subnet)
186+
if err != nil {
187+
utilruntime.HandleError(fmt.Errorf("could not parse HostSubnet %q CIDR: %v", hs.Name, err))
188+
}
189+
sdnIP := netutils.GenerateDefaultGateway(cidr).String()
190+
191+
eip.updateNodeEgress(hs.HostIP, sdnIP, hs.EgressIPs)
183192
}
184193

185194
func (eip *egressIPWatcher) handleDeleteHostSubnet(obj interface{}) {
186195
hs := obj.(*networkapi.HostSubnet)
187196
glog.V(5).Infof("Watch %s event for HostSubnet %q", watch.Deleted, hs.Name)
188197

189-
eip.updateNodeEgress(hs.HostIP, nil)
198+
eip.updateNodeEgress(hs.HostIP, "", nil)
190199
}
191200

192-
func (eip *egressIPWatcher) updateNodeEgress(nodeIP string, nodeEgressIPs []string) {
201+
func (eip *egressIPWatcher) updateNodeEgress(nodeIP, sdnIP string, nodeEgressIPs []string) {
193202
eip.Lock()
194203
defer eip.Unlock()
195204

@@ -200,11 +209,18 @@ func (eip *egressIPWatcher) updateNodeEgress(nodeIP string, nodeEgressIPs []stri
200209
}
201210
node = &nodeEgress{
202211
nodeIP: nodeIP,
212+
sdnIP: sdnIP,
203213
requestedIPs: sets.NewString(),
204214
}
205215
eip.nodesByNodeIP[nodeIP] = node
216+
if eip.vxlanMonitor != nil && node.nodeIP != eip.localIP {
217+
eip.vxlanMonitor.AddNode(node.nodeIP, node.sdnIP)
218+
}
206219
} else if len(nodeEgressIPs) == 0 {
207220
delete(eip.nodesByNodeIP, nodeIP)
221+
if eip.vxlanMonitor != nil {
222+
eip.vxlanMonitor.RemoveNode(node.nodeIP)
223+
}
208224
}
209225
oldRequestedIPs := node.requestedIPs
210226
node.requestedIPs = sets.NewString(nodeEgressIPs...)
@@ -229,11 +245,6 @@ func (eip *egressIPWatcher) handleAddOrUpdateNetNamespace(obj, _ interface{}, ev
229245
netns := obj.(*networkapi.NetNamespace)
230246
glog.V(5).Infof("Watch %s event for NetNamespace %q", eventType, netns.Name)
231247

232-
if len(netns.EgressIPs) != 0 {
233-
if len(netns.EgressIPs) > 1 {
234-
glog.Warningf("Ignoring extra EgressIPs (%v) in NetNamespace %q", netns.EgressIPs[1:], netns.Name)
235-
}
236-
}
237248
eip.updateNamespaceEgress(netns.NetID, netns.EgressIPs)
238249
}
239250

@@ -284,37 +295,46 @@ func (eip *egressIPWatcher) deleteNamespaceEgress(vnid uint32) {
284295
eip.updateNamespaceEgress(vnid, nil)
285296
}
286297

287-
func (eip *egressIPWatcher) syncEgressIPs() {
288-
changedEgressIPs := make(map[*egressIPInfo]bool)
289-
for _, eg := range eip.changedEgressIPs {
290-
changedEgressIPs[eg] = true
298+
func (eip *egressIPWatcher) egressIPActive(eg *egressIPInfo) (bool, error) {
299+
if len(eg.nodes) == 0 || len(eg.namespaces) == 0 {
300+
return false, nil
291301
}
292-
eip.changedEgressIPs = eip.changedEgressIPs[:0]
293-
294-
changedNamespaces := make(map[*namespaceEgress]bool)
295-
for _, ns := range eip.changedNamespaces {
296-
changedNamespaces[ns] = true
302+
if len(eg.nodes) > 1 {
303+
return false, fmt.Errorf("Multiple nodes (%s, %s) claiming EgressIP %s", eg.nodes[0].nodeIP, eg.nodes[1].nodeIP, eg.ip)
297304
}
298-
eip.changedNamespaces = eip.changedNamespaces[:0]
305+
if len(eg.namespaces) > 1 {
306+
return false, fmt.Errorf("Multiple namespaces (%d, %d) claiming EgressIP %s", eg.namespaces[0].vnid, eg.namespaces[1].vnid, eg.ip)
307+
}
308+
for _, ip := range eg.namespaces[0].requestedIPs {
309+
eg2 := eip.egressIPs[ip]
310+
if eg2 != eg && len(eg2.nodes) == 1 && eg2.nodes[0] == eg.nodes[0] {
311+
return false, fmt.Errorf("Multiple EgressIPs (%s, %s) for VNID %d on node %s", eg.ip, eg2.ip, eg.namespaces[0].vnid, eg.nodes[0].nodeIP)
312+
}
313+
}
314+
return true, nil
315+
}
299316

300-
for eg := range changedEgressIPs {
301-
eip.syncEgressNodeState(eg)
317+
func (eip *egressIPWatcher) syncEgressIPs() {
318+
for eg := range eip.changedEgressIPs {
319+
active, err := eip.egressIPActive(eg)
320+
if err != nil {
321+
utilruntime.HandleError(err)
322+
}
323+
eip.syncEgressNodeState(eg, active)
302324
}
325+
eip.changedEgressIPs = make(map[*egressIPInfo]bool)
303326

304-
for ns := range changedNamespaces {
327+
for ns := range eip.changedNamespaces {
305328
err := eip.syncEgressNamespaceState(ns)
306329
if err != nil {
307330
utilruntime.HandleError(fmt.Errorf("Error updating Namespace egress rules for VNID %d: %v", ns.vnid, err))
308331
}
309332
}
333+
eip.changedNamespaces = make(map[*namespaceEgress]bool)
310334
}
311335

312-
func (eip *egressIPWatcher) syncEgressNodeState(eg *egressIPInfo) {
313-
// The egressIPInfo should have an assigned node IP if and only if the
314-
// egress IP is active (ie, it is assigned to exactly 1 node and exactly
315-
// 1 namespace, and it's the first one for its namespace).
316-
egressIPActive := (len(eg.nodes) == 1 && len(eg.namespaces) == 1 && eg.ip == eg.namespaces[0].requestedIPs[0])
317-
if egressIPActive && eg.assignedNodeIP != eg.nodes[0].nodeIP {
336+
func (eip *egressIPWatcher) syncEgressNodeState(eg *egressIPInfo, active bool) {
337+
if active && eg.assignedNodeIP != eg.nodes[0].nodeIP {
318338
glog.V(4).Infof("Assigning egress IP %s to node %s", eg.ip, eg.nodes[0].nodeIP)
319339
eg.assignedNodeIP = eg.nodes[0].nodeIP
320340
eg.assignedIPTablesMark = getMarkForVNID(eg.namespaces[0].vnid, eip.masqueradeBit)
@@ -324,7 +344,7 @@ func (eip *egressIPWatcher) syncEgressNodeState(eg *egressIPInfo) {
324344
eg.assignedNodeIP = ""
325345
}
326346
}
327-
} else if !egressIPActive && eg.assignedNodeIP != "" {
347+
} else if !active && eg.assignedNodeIP != "" {
328348
glog.V(4).Infof("Removing egress IP %s from node %s", eg.ip, eg.assignedNodeIP)
329349
if eg.assignedNodeIP == eip.localIP {
330350
if err := eip.releaseEgressIP(eg.ip, eg.assignedIPTablesMark); err != nil {
@@ -333,8 +353,6 @@ func (eip *egressIPWatcher) syncEgressNodeState(eg *egressIPInfo) {
333353
}
334354
eg.assignedNodeIP = ""
335355
eg.assignedIPTablesMark = ""
336-
} else if !egressIPActive {
337-
glog.V(4).Infof("Egress IP %s is not assignable (%d namespaces, %d nodes)", eg.ip, len(eg.namespaces), len(eg.nodes))
338356
}
339357
}
340358

@@ -344,7 +362,7 @@ func (eip *egressIPWatcher) syncEgressNamespaceState(ns *namespaceEgress) error
344362
}
345363

346364
var active *egressIPInfo
347-
for i, ip := range ns.requestedIPs {
365+
for _, ip := range ns.requestedIPs {
348366
eg := eip.egressIPs[ip]
349367
if eg == nil {
350368
continue
@@ -354,9 +372,11 @@ func (eip *egressIPWatcher) syncEgressNamespaceState(ns *namespaceEgress) error
354372
glog.V(4).Infof("VNID %d gets no egress due to multiply-assigned egress IP %s", ns.vnid, eg.ip)
355373
break
356374
}
357-
if active == nil && i == 0 {
375+
if active == nil {
358376
if eg.assignedNodeIP == "" {
359377
glog.V(4).Infof("VNID %d cannot use unassigned egress IP %s", ns.vnid, eg.ip)
378+
} else if len(ns.requestedIPs) > 1 && eg.nodes[0].offline {
379+
glog.V(4).Infof("VNID %d cannot use egress IP %s on offline node %s", ns.vnid, eg.ip, eg.assignedNodeIP)
360380
} else {
361381
active = eg
362382
}
@@ -436,3 +456,29 @@ func (eip *egressIPWatcher) releaseEgressIP(egressIP, mark string) error {
436456

437457
return nil
438458
}
459+
460+
func (eip *egressIPWatcher) watchVXLAN(updates chan *egressVXLANNode) {
461+
for node := range updates {
462+
eip.updateNode(node.nodeIP, node.offline)
463+
}
464+
}
465+
466+
func (eip *egressIPWatcher) updateNode(nodeIP string, offline bool) {
467+
eip.Lock()
468+
defer eip.Unlock()
469+
470+
node := eip.nodesByNodeIP[nodeIP]
471+
if node == nil {
472+
eip.vxlanMonitor.RemoveNode(nodeIP)
473+
return
474+
}
475+
476+
node.offline = offline
477+
for _, ip := range node.requestedIPs.UnsortedList() {
478+
eg := eip.egressIPs[ip]
479+
if eg != nil {
480+
eip.egressIPChanged(eg)
481+
}
482+
}
483+
eip.syncEgressIPs()
484+
}

0 commit comments

Comments
 (0)