Skip to content

Commit

Permalink
[ag] support static routes on vrfs
Browse files Browse the repository at this point in the history
  • Loading branch information
Frostman committed Nov 26, 2023
1 parent 2cc0e1e commit 4af66d9
Show file tree
Hide file tree
Showing 3 changed files with 145 additions and 12 deletions.
35 changes: 23 additions & 12 deletions pkg/agent/dozer/bcm/enforcer.go
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,9 @@ const (

ActionWeightVRFVNIUpdate

ActionWeightVRFStaticRouteDelete // it seems like it's better to first remove routes and then add new ones
ActionWeightVRFStaticRouteUpdate

// Deletes:

ActionWeightVRFVNIDelete
Expand Down Expand Up @@ -212,15 +215,16 @@ type DefaultValueEnforcer[Key comparable, Value dozer.SpecPart] struct {

CustomHandler func(basePath string, key Key, actual, desired Value, actions *ActionQueue) error // will be used instead of default one

Path string // used by default value handler
CreatePath string
PathFunc func(key Key, value Value) string
Marshal func(key Key, value Value) (ygot.ValidatedGoStruct, error) // used by default value handler
Weight ActionWeight
UpdateWeight ActionWeight
DeleteWeight ActionWeight
WarningOnError bool
SkipDelete bool
Path string // used by default value handler
CreatePath string
PathFunc func(key Key, value Value) string
Marshal func(key Key, value Value) (ygot.ValidatedGoStruct, error) // used by default value handler
Weight ActionWeight
UpdateWeight ActionWeight
DeleteWeight ActionWeight
WarningOnError bool
SkipDelete bool
RecreateOnUpdate bool
}

func (h *DefaultValueEnforcer[Key, Value]) Handle(basePath string, key Key, actual, desired Value, actions *ActionQueue) error {
Expand Down Expand Up @@ -275,8 +279,13 @@ func (h *DefaultValueEnforcer[Key, Value]) Handle(basePath string, key Key, actu
if h.DeleteWeight >= ActionWeightMax {
return errors.Errorf("delete weight %d is greater than max %d", h.DeleteWeight, ActionWeightMax)
}
if h.RecreateOnUpdate && h.UpdateWeight < h.DeleteWeight {
// if we want to recreate on update we need to delete first
return errors.Errorf("update weight %d is less than delete weight %d for %s but recreate on update requests", h.UpdateWeight, h.DeleteWeight, summary)
}

if desired.IsNil() { // delete actual value if desired isn't present
// delete actual value if desired isn't present or recreate on update requested
if desired.IsNil() || !actual.IsNil() && h.RecreateOnUpdate {
if h.SkipDelete {
slog.Debug("Skipping delete", "summary", summary, "key", key)
return nil
Expand All @@ -297,13 +306,15 @@ func (h *DefaultValueEnforcer[Key, Value]) Handle(basePath string, key Key, actu
}); err != nil {
return errors.Wrapf(err, "failed to add delete action for %s (key %v)", summary, key)
}
} else {
}

if !desired.IsNil() {
path := SafeSprintf(h.Path, key)
if h.PathFunc != nil {
path = h.PathFunc(key, desired)
}

if actual.IsNil() {
if actual.IsNil() || h.RecreateOnUpdate {
summary = fmt.Sprintf("Create %s", summary)
if h.CreatePath != "" {
path = SafeSprintf(h.CreatePath, key)
Expand Down
106 changes: 106 additions & 0 deletions pkg/agent/dozer/bcm/spec_vrf.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,12 @@ var specVRFEnforcer = &DefaultValueEnforcer[string, *dozer.SpecVRF]{
return errors.Wrap(err, "failed to handle vrf table connections")
}

actualStaticRoutes, desiredStaticRoutes := ValueOrNil(actual, desired,
func(value *dozer.SpecVRF) map[string]*dozer.SpecVRFStaticRoute { return value.StaticRoutes })
if err := specVRFStaticRoutesEnforcer.Handle(basePath, actualStaticRoutes, desiredStaticRoutes, actions); err != nil {
return errors.Wrap(err, "failed to handle vrf static routes")
}

return nil
},
}
Expand Down Expand Up @@ -382,6 +388,57 @@ var specVRFTableConnectionEnforcer = &DefaultValueEnforcer[string, *dozer.SpecVR
},
}

var specVRFStaticRoutesEnforcer = &DefaultMapEnforcer[string, *dozer.SpecVRFStaticRoute]{
Summary: "VRF static routes",
ValueHandler: specVRFStaticRouteEnforcer,
}

var specVRFStaticRouteEnforcer = &DefaultValueEnforcer[string, *dozer.SpecVRFStaticRoute]{
Summary: "VRF static route %s",
Path: "/protocols/protocol[identifier=STATIC][name=static]/static-routes/static[prefix=%s]",
RecreateOnUpdate: true,
UpdateWeight: ActionWeightVRFStaticRouteUpdate,
DeleteWeight: ActionWeightVRFStaticRouteDelete,
Marshal: func(prefix string, value *dozer.SpecVRFStaticRoute) (ygot.ValidatedGoStruct, error) {
nextHops := map[string]*oc.OpenconfigNetworkInstance_NetworkInstances_NetworkInstance_Protocols_Protocol_StaticRoutes_Static_NextHops_NextHop{}

for _, nextHop := range value.NextHops {
if nextHop.Interface == nil {
return nil, errors.Errorf("invalid next hop %v", nextHop)
}

index := fmt.Sprintf("%s_%s", *nextHop.Interface, nextHop.IP)
nextHops[index] = &oc.OpenconfigNetworkInstance_NetworkInstances_NetworkInstance_Protocols_Protocol_StaticRoutes_Static_NextHops_NextHop{
Index: ygot.String(index),
Config: &oc.OpenconfigNetworkInstance_NetworkInstances_NetworkInstance_Protocols_Protocol_StaticRoutes_Static_NextHops_NextHop_Config{
Index: ygot.String(index),
NextHop: oc.UnionString(nextHop.IP),
},
InterfaceRef: &oc.OpenconfigNetworkInstance_NetworkInstances_NetworkInstance_Protocols_Protocol_StaticRoutes_Static_NextHops_NextHop_InterfaceRef{
Config: &oc.OpenconfigNetworkInstance_NetworkInstances_NetworkInstance_Protocols_Protocol_StaticRoutes_Static_NextHops_NextHop_InterfaceRef_Config{
Interface: nextHop.Interface,
},
},
}
}

return &oc.OpenconfigNetworkInstance_NetworkInstances_NetworkInstance_Protocols_Protocol_StaticRoutes{
Static: map[string]*oc.OpenconfigNetworkInstance_NetworkInstances_NetworkInstance_Protocols_Protocol_StaticRoutes_Static{
prefix: {
Prefix: ygot.String(prefix),
Config: &oc.OpenconfigNetworkInstance_NetworkInstances_NetworkInstance_Protocols_Protocol_StaticRoutes_Static_Config{
Prefix: ygot.String(prefix),
Description: value.Description,
},
NextHops: &oc.OpenconfigNetworkInstance_NetworkInstances_NetworkInstance_Protocols_Protocol_StaticRoutes_Static_NextHops{
NextHop: nextHops,
},
},
},
}, nil
},
}

func loadActualVRFs(ctx context.Context, client *gnmi.Client, spec *dozer.Spec) error {
ocVal := &oc.OpenconfigNetworkInstance_NetworkInstances{}
err := client.Get(ctx, "/network-instances/network-instance", ocVal)
Expand Down Expand Up @@ -545,6 +602,54 @@ func unmarshalOCVRFs(ocVal *oc.OpenconfigNetworkInstance_NetworkInstances) (map[
}
}

staticRoutes := map[string]*dozer.SpecVRFStaticRoute{}

if ocVRF.Protocols != nil && ocVRF.Protocols.Protocol != nil {
staticProto := ocVRF.Protocols.Protocol[oc.OpenconfigNetworkInstance_NetworkInstances_NetworkInstance_Protocols_Protocol_Key{
Identifier: oc.OpenconfigPolicyTypes_INSTALL_PROTOCOL_TYPE_STATIC,
Name: "static",
}]
if staticProto != nil && staticProto.StaticRoutes != nil {
for prefix, staticRoute := range staticProto.StaticRoutes.Static {
var description *string
if staticRoute.Config != nil {
description = staticRoute.Config.Description
}

nextHops := []dozer.SpecVRFStaticRouteNextHop{}
if staticRoute.NextHops != nil {
for _, nextHop := range staticRoute.NextHops.NextHop {
if nextHop.Config == nil || nextHop.Config.NextHop == nil {
continue
}

var iface *string
if nextHop.InterfaceRef != nil || nextHop.InterfaceRef.Config != nil {
iface = nextHop.InterfaceRef.Config.Interface
}

ip := ""
if union, ok := nextHop.Config.NextHop.(oc.UnionString); ok {
ip = string(union)
} else {
return nil, errors.Errorf("invalid next hop %v for %s", nextHop, prefix)
}

nextHops = append(nextHops, dozer.SpecVRFStaticRouteNextHop{
Interface: iface,
IP: ip,
})
}
}

staticRoutes[prefix] = &dozer.SpecVRFStaticRoute{
Description: description,
NextHops: nextHops,
}
}
}
}

enabled := ocVRF.Config.Enabled
if enabled == nil {
enabled = ygot.Bool(true)
Expand All @@ -566,6 +671,7 @@ func unmarshalOCVRFs(ocVal *oc.OpenconfigNetworkInstance_NetworkInstances) (map[
Interfaces: interfaces,
BGP: bgp,
TableConnections: tableConns,
StaticRoutes: staticRoutes,
}
}

Expand Down
16 changes: 16 additions & 0 deletions pkg/agent/dozer/dozer.go
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,7 @@ type SpecVRF struct {
Interfaces map[string]*SpecVRFInterface `json:"interfaces,omitempty"`
BGP *SpecVRFBGP `json:"bgp,omitempty"`
TableConnections map[string]*SpecVRFTableConnection `json:"tableConnections,omitempty"`
StaticRoutes map[string]*SpecVRFStaticRoute `json:"staticRoutes,omitempty"`
}

type SpecVRFInterface struct{}
Expand Down Expand Up @@ -158,6 +159,16 @@ type SpecVRFTableConnection struct {
ImportPolicies []string `json:"importPolicies,omitempty"`
}

type SpecVRFStaticRoute struct {
Description *string `json:"description,omitempty"`
NextHops []SpecVRFStaticRouteNextHop `json:"nextHops,omitempty"`
}

type SpecVRFStaticRouteNextHop struct {
IP string `json:"ip,omitempty"`
Interface *string `json:"interface,omitempty"`
}

type SpecRouteMap struct {
NoAdvertise *bool `json:"noAdvertise,omitempty"`
}
Expand Down Expand Up @@ -333,6 +344,7 @@ var (
_ SpecPart = (*SpecVRFBGPNeighbor)(nil)
_ SpecPart = (*SpecVRFBGPImportVRF)(nil)
_ SpecPart = (*SpecVRFTableConnection)(nil)
_ SpecPart = (*SpecVRFStaticRoute)(nil)
_ SpecPart = (*SpecRouteMap)(nil)
_ SpecPart = (*SpecDHCPRelay)(nil)
_ SpecPart = (*SpecNAT)(nil)
Expand Down Expand Up @@ -416,6 +428,10 @@ func (s *SpecVRFTableConnection) IsNil() bool {
return s == nil
}

func (s *SpecVRFStaticRoute) IsNil() bool {
return s == nil
}

func (s *SpecRouteMap) IsNil() bool {
return s == nil
}
Expand Down

0 comments on commit 4af66d9

Please sign in to comment.