Skip to content

Commit

Permalink
Configure TemplateLB with all host addresses
Browse files Browse the repository at this point in the history
TemplateLB variables for node IP addresses have to
support multiple IP noda address, hence the variables
are in the format of NODE_IPv4_0, NODE_IPv4_1, NODE_IPv4_2, ...
Struct `NodeIPsTemplates` manage the template variables
for multiple nodes that may have different number of IP addresses each.

Add unit and e2e tests on TemplateLBs with multiple IP addresses.

Refs: ovn-org#3328

Signed-off-by: Andrea Panattoni <apanatto@redhat.com>
  • Loading branch information
zeeke committed Apr 28, 2023
1 parent 92f45bd commit 7289339
Show file tree
Hide file tree
Showing 6 changed files with 396 additions and 110 deletions.
97 changes: 52 additions & 45 deletions go-controller/pkg/ovn/controller/services/lb_config.go
Original file line number Diff line number Diff line change
Expand Up @@ -313,7 +313,7 @@ func buildClusterLBs(service *v1.Service, configs []lbConfig, nodeInfos []nodeIn
// NodePort services with ETP=local or affinity timeout set still need
// non-template per-node LBs.
func buildTemplateLBs(service *v1.Service, configs []lbConfig, nodes []nodeInfo,
nodeIPv4Template, nodeIPv6Template *Template) []LB {
nodeIPv4Templates, nodeIPv6Templates *NodeIPsTemplates) []LB {

cbp := configsByProto(configs)
eids := util.ExternalIDsForObject(service)
Expand Down Expand Up @@ -406,57 +406,63 @@ func buildTemplateLBs(service *v1.Service, configs []lbConfig, nodes []nodeInfo,
sharedV6Targets = joinHostsPort(config.eps.V6IPs, config.eps.Port)
}

if switchV4TargetNeedsTemplate {
switchV4Rules = append(switchV4Rules, LBRule{
Source: Addr{Template: nodeIPv4Template, Port: config.inport},
Targets: []Addr{{Template: switchV4TemplateTarget}},
})
} else {
switchV4Rules = append(switchV4Rules, LBRule{
Source: Addr{Template: nodeIPv4Template, Port: config.inport},
Targets: sharedV4Targets,
})
}
for _, nodeIPv4Template := range nodeIPv4Templates.AsTemplates() {

if switchV6TargetNeedsTemplate {
switchV6Rules = append(switchV6Rules, LBRule{
Source: Addr{Template: nodeIPv6Template, Port: config.inport},
Targets: []Addr{{Template: switchV6TemplateTarget}},
})
} else {
switchV6Rules = append(switchV6Rules, LBRule{
Source: Addr{Template: nodeIPv6Template, Port: config.inport},
Targets: sharedV6Targets,
})
}
if switchV4TargetNeedsTemplate {
switchV4Rules = append(switchV4Rules, LBRule{
Source: Addr{Template: nodeIPv4Template, Port: config.inport},
Targets: []Addr{{Template: switchV4TemplateTarget}},
})
} else {
switchV4Rules = append(switchV4Rules, LBRule{
Source: Addr{Template: nodeIPv4Template, Port: config.inport},
Targets: sharedV4Targets,
})
}

if routerV4TargetNeedsTemplate {
routerV4Rules = append(routerV4Rules, LBRule{
Source: Addr{Template: nodeIPv4Template, Port: config.inport},
Targets: []Addr{{Template: routerV4TemplateTarget}},
})
} else {
routerV4Rules = append(routerV4Rules, LBRule{
Source: Addr{Template: nodeIPv4Template, Port: config.inport},
Targets: sharedV4Targets,
})
if routerV4TargetNeedsTemplate {
routerV4Rules = append(routerV4Rules, LBRule{
Source: Addr{Template: nodeIPv4Template, Port: config.inport},
Targets: []Addr{{Template: routerV4TemplateTarget}},
})
} else {
routerV4Rules = append(routerV4Rules, LBRule{
Source: Addr{Template: nodeIPv4Template, Port: config.inport},
Targets: sharedV4Targets,
})
}
}

if routerV6TargetNeedsTemplate {
routerV6Rules = append(routerV6Rules, LBRule{
Source: Addr{Template: nodeIPv6Template, Port: config.inport},
Targets: []Addr{{Template: routerV6TemplateTarget}},
})
} else {
routerV6Rules = append(routerV6Rules, LBRule{
Source: Addr{Template: nodeIPv6Template, Port: config.inport},
Targets: sharedV6Targets,
})
for _, nodeIPv6Template := range nodeIPv6Templates.AsTemplates() {

if switchV6TargetNeedsTemplate {
switchV6Rules = append(switchV6Rules, LBRule{
Source: Addr{Template: nodeIPv6Template, Port: config.inport},
Targets: []Addr{{Template: switchV6TemplateTarget}},
})
} else {
switchV6Rules = append(switchV6Rules, LBRule{
Source: Addr{Template: nodeIPv6Template, Port: config.inport},
Targets: sharedV6Targets,
})
}

if routerV6TargetNeedsTemplate {
routerV6Rules = append(routerV6Rules, LBRule{
Source: Addr{Template: nodeIPv6Template, Port: config.inport},
Targets: []Addr{{Template: routerV6TemplateTarget}},
})
} else {
routerV6Rules = append(routerV6Rules, LBRule{
Source: Addr{Template: nodeIPv6Template, Port: config.inport},
Targets: sharedV6Targets,
})
}
}
}
}

if nodeIPv4Template.len() > 0 {
if nodeIPv4Templates.Len() > 0 {
if len(switchV4Rules) > 0 {
out = append(out, LB{
Name: makeLBName(service, proto, "node_switch_template_IPv4"),
Expand All @@ -480,7 +486,8 @@ func buildTemplateLBs(service *v1.Service, configs []lbConfig, nodes []nodeInfo,
})
}
}
if nodeIPv6Template.len() > 0 {

if nodeIPv6Templates.Len() > 0 {
if len(switchV6Rules) > 0 {
out = append(out, LB{
Name: makeLBName(service, proto, "node_switch_template_IPv6"),
Expand Down
67 changes: 36 additions & 31 deletions go-controller/pkg/ovn/controller/services/services_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -58,13 +58,13 @@ func NewController(client clientset.Interface,
klog.V(4).Info("Creating event broadcaster")

c := &Controller{
client: client,
nbClient: nbClient,
queue: workqueue.NewNamedRateLimitingQueue(newRatelimiter(100), controllerName),
workerLoopPeriod: time.Second,
alreadyApplied: map[string][]LB{},
nodeIPv4Template: makeTemplate(makeLBNodeIPTemplateName(v1.IPv4Protocol)),
nodeIPv6Template: makeTemplate(makeLBNodeIPTemplateName(v1.IPv6Protocol)),
client: client,
nbClient: nbClient,
queue: workqueue.NewNamedRateLimitingQueue(newRatelimiter(100), controllerName),
workerLoopPeriod: time.Second,
alreadyApplied: map[string][]LB{},
nodeIPv4Templates: NewNodeIPsTemplates(v1.IPv4Protocol),
nodeIPv6Templates: NewNodeIPsTemplates(v1.IPv6Protocol),
}

// services
Expand Down Expand Up @@ -154,10 +154,10 @@ type Controller struct {
// chassis' node IP (v4 and v6).
// Must be accessed only with the nodeInfo mutex taken.
// These are written in RequestFullSync().
nodeInfos []nodeInfo
nodeIPv4Template *Template
nodeIPv6Template *Template
nodeInfoRWLock sync.RWMutex
nodeInfos []nodeInfo
nodeIPv4Templates *NodeIPsTemplates
nodeIPv6Templates *NodeIPsTemplates
nodeInfoRWLock sync.RWMutex

// alreadyApplied is a map of service key -> already applied configuration, so we can short-circuit
// if a service's config hasn't changed
Expand Down Expand Up @@ -401,7 +401,7 @@ func (c *Controller) syncService(key string) error {
// Convert the LB configs in to load-balancer objects
clusterLBs := buildClusterLBs(service, clusterConfigs, c.nodeInfos, c.useLBGroups)
templateLBs := buildTemplateLBs(service, templateConfigs, c.nodeInfos,
c.nodeIPv4Template, c.nodeIPv6Template)
c.nodeIPv4Templates, c.nodeIPv6Templates)
perNodeLBs := buildPerNodeLBs(service, perNodeConfigs, c.nodeInfos)
klog.V(5).Infof("Built service %s cluster-wide LB %#v", key, clusterLBs)
klog.V(5).Infof("Built service %s per-node LB %#v", key, perNodeLBs)
Expand Down Expand Up @@ -453,38 +453,43 @@ func (c *Controller) syncNodeInfos(nodeInfos []nodeInfo) {
}

// Compute the nodeIP template values.
c.nodeIPv4Template = makeTemplate(makeLBNodeIPTemplateName(v1.IPv4Protocol))
c.nodeIPv6Template = makeTemplate(makeLBNodeIPTemplateName(v1.IPv6Protocol))
c.nodeIPv4Templates = NewNodeIPsTemplates(v1.IPv4Protocol)
c.nodeIPv6Templates = NewNodeIPsTemplates(v1.IPv6Protocol)

for _, node := range c.nodeInfos {
if node.chassisID == "" {
for _, nodeInfo := range c.nodeInfos {
if nodeInfo.chassisID == "" {
continue
}
// Services are currently supported only on the node's first IP.
// Extract that one and populate the node's IP template value.

if globalconfig.IPv4Mode {
if ipv4, err := util.MatchFirstIPFamily(false, node.l3gatewayAddresses); err == nil {
c.nodeIPv4Template.Value[node.chassisID] = ipv4.String()
ips, err := util.MatchIPFamily(false, nodeInfo.hostAddresses)
if err != nil {
klog.Warningf("Error while searching for IPv4 host addresses: %v", err)
continue
}

for _, ip := range ips {
c.nodeIPv4Templates.AddIP(nodeInfo.chassisID, ip)
}
}

if globalconfig.IPv6Mode {
if ipv6, err := util.MatchFirstIPFamily(true, node.l3gatewayAddresses); err == nil {
c.nodeIPv6Template.Value[node.chassisID] = ipv6.String()
ips, err := util.MatchIPFamily(true, nodeInfo.hostAddresses)
if err != nil {
klog.Warningf("Error while searching for IPv6 host addresses: %v", err)
continue
}

for _, ip := range ips {
c.nodeIPv6Templates.AddIP(nodeInfo.chassisID, ip)
}
}
}

// Sync the nodeIP template values to the DB.
nodeIPTemplateMap := TemplateMap{}
if c.nodeIPv4Template.len() > 0 {
nodeIPTemplateMap[c.nodeIPv4Template.Name] = c.nodeIPv4Template
}
if c.nodeIPv6Template.len() > 0 {
nodeIPTemplateMap[c.nodeIPv6Template.Name] = c.nodeIPv6Template
}

nodeIPTemplates := []TemplateMap{
nodeIPTemplateMap,
c.nodeIPv4Templates.AsTemplateMap(),
c.nodeIPv6Templates.AsTemplateMap(),
}
if err := svcCreateOrUpdateTemplateVar(c.nbClient, nodeIPTemplates); err != nil {
klog.Errorf("Could not sync node IP templates")
Expand Down

0 comments on commit 7289339

Please sign in to comment.