Skip to content

Commit

Permalink
Add support for building a RIB from a gRIBI.Get response. (#193)
Browse files Browse the repository at this point in the history
* Add support for building a RIB from a gRIBI.Get response.

 * (A) rib/helpers(_test)?.go
   - Add a new function that allows a RIB to be built from a slice of
     gRIBI GetResponse messages that are received from a server. This
     allows a RIB to be built from an external source.

* run gofmt.
  • Loading branch information
robshakir committed Sep 18, 2023
1 parent f608df6 commit 0717f4c
Show file tree
Hide file tree
Showing 2 changed files with 330 additions and 0 deletions.
72 changes: 72 additions & 0 deletions rib/helpers.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
// Copyright 2023 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package rib

import (
"fmt"

aftpb "github.com/openconfig/gribi/v1/proto/gribi_aft"
spb "github.com/openconfig/gribi/v1/proto/service"
"github.com/openconfig/ygot/ygot"
)

// RIBFromGetResponses returns a RIB from a slice of gRIBI GetResponse messages.
// The supplied defaultName is used as the default network instance name.
func RIBFromGetResponses(defaultName string, responses []*spb.GetResponse) (*RIB, error) {

Check failure on line 27 in rib/helpers.go

View workflow job for this annotation

GitHub Actions / go / Static Analysis

func name will be used as rib.RIBFromGetResponses by other packages, and that stutters; consider calling this FromGetResponses

Check failure on line 27 in rib/helpers.go

View workflow job for this annotation

GitHub Actions / go / Static Analysis

func name will be used as rib.RIBFromGetResponses by other packages, and that stutters; consider calling this FromGetResponses

Check failure on line 27 in rib/helpers.go

View workflow job for this annotation

GitHub Actions / go / Static Analysis

func name will be used as rib.RIBFromGetResponses by other packages, and that stutters; consider calling this FromGetResponses
r := New(defaultName)
niAFTs := map[string]*aftpb.Afts{}

for _, resp := range responses {
for _, e := range resp.Entry {
ni := e.GetNetworkInstance()
if _, ok := niAFTs[ni]; !ok {
niAFTs[ni] = &aftpb.Afts{}
}
switch t := e.GetEntry().(type) {
case *spb.AFTEntry_Ipv4:
niAFTs[ni].Ipv4Entry = append(niAFTs[ni].Ipv4Entry, t.Ipv4)
case *spb.AFTEntry_Mpls:
niAFTs[ni].LabelEntry = append(niAFTs[ni].LabelEntry, t.Mpls)
case *spb.AFTEntry_NextHopGroup:
niAFTs[ni].NextHopGroup = append(niAFTs[ni].NextHopGroup, t.NextHopGroup)
case *spb.AFTEntry_NextHop:
niAFTs[ni].NextHop = append(niAFTs[ni].NextHop, t.NextHop)
default:
return nil, fmt.Errorf("unknown/unhandled type %T in received GetResponses", t)
}
}
}

// Throughout this operation we don't worry about locking the structures in the new
// RIB because we are the only function that can access the newly created data structure.
for ni, niRIB := range niAFTs {
cr, err := candidateRIB(niRIB)
if err != nil {
return nil, fmt.Errorf("cannot build RIB for NI %s, err: %v", ni, err)
}

if ni != defaultName {
if err := r.AddNetworkInstance(ni); err != nil {
return nil, fmt.Errorf("cannot create network instance RIB for NI %s, err: %v", ni, err)
}
}

if err := ygot.MergeStructInto(r.niRIB[ni].r, cr); err != nil {
return nil, fmt.Errorf("cannot populate network instance RIB for NI %s, err: %v", ni, err)
}
}

return r, nil
}
258 changes: 258 additions & 0 deletions rib/helpers_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,258 @@
// Copyright 2021 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package rib

import (
"testing"

"github.com/google/go-cmp/cmp"
"github.com/google/go-cmp/cmp/cmpopts"
"github.com/openconfig/gribigo/aft"

aftpb "github.com/openconfig/gribi/v1/proto/gribi_aft"
spb "github.com/openconfig/gribi/v1/proto/service"
wpb "github.com/openconfig/ygot/proto/ywrapper"
"github.com/openconfig/ygot/ygot"
)

func TestRIBFromGetResponses(t *testing.T) {
defaultName := "DEFAULT"
tests := []struct {
desc string
inDefaultName string
inResponses []*spb.GetResponse
wantRIB *RIB
wantErr bool
}{{
desc: "single ipv4 entry",
inResponses: []*spb.GetResponse{{
Entry: []*spb.AFTEntry{{
NetworkInstance: "VRF-1",
Entry: &spb.AFTEntry_Ipv4{
Ipv4: &aftpb.Afts_Ipv4EntryKey{
Prefix: "1.1.1.1/32",
Ipv4Entry: &aftpb.Afts_Ipv4Entry{
NextHopGroup: &wpb.UintValue{Value: 42},
},
},
},
}},
}},
inDefaultName: "DEFAULT",
wantRIB: &RIB{
defaultName: defaultName,
niRIB: map[string]*RIBHolder{
"DEFAULT": {
name: "DEFAULT",
r: &aft.RIB{
Afts: &aft.Afts{},
},
},
"VRF-1": {
name: "VRF-1",
r: &aft.RIB{
Afts: &aft.Afts{
Ipv4Entry: map[string]*aft.Afts_Ipv4Entry{
"1.1.1.1/32": {
Prefix: ygot.String("1.1.1.1/32"),
NextHopGroup: ygot.Uint64(42),
},
},
},
},
},
},
},
}, {
desc: "next-hop group entry",
inResponses: []*spb.GetResponse{{
Entry: []*spb.AFTEntry{{
NetworkInstance: defaultName,
Entry: &spb.AFTEntry_NextHopGroup{
NextHopGroup: &aftpb.Afts_NextHopGroupKey{
Id: 42,
NextHopGroup: &aftpb.Afts_NextHopGroup{
BackupNextHopGroup: &wpb.UintValue{Value: 42},
},
},
},
}},
}},
inDefaultName: "DEFAULT",
wantRIB: &RIB{
defaultName: defaultName,
niRIB: map[string]*RIBHolder{
"DEFAULT": {
name: "DEFAULT",
r: &aft.RIB{
Afts: &aft.Afts{
NextHopGroup: map[uint64]*aft.Afts_NextHopGroup{
42: {
Id: ygot.Uint64(42),
BackupNextHopGroup: ygot.Uint64(42),
},
},
},
},
},
},
},
}, {
desc: "nexthop entry",
inResponses: []*spb.GetResponse{{
Entry: []*spb.AFTEntry{{
NetworkInstance: defaultName,
Entry: &spb.AFTEntry_NextHop{
NextHop: &aftpb.Afts_NextHopKey{
Index: 42,
NextHop: &aftpb.Afts_NextHop{
IpAddress: &wpb.StringValue{Value: "1.1.1.1"},
},
},
},
}},
}},
inDefaultName: "DEFAULT",
wantRIB: &RIB{
defaultName: defaultName,
niRIB: map[string]*RIBHolder{
"DEFAULT": {
name: "DEFAULT",
r: &aft.RIB{
Afts: &aft.Afts{
NextHop: map[uint64]*aft.Afts_NextHop{
42: {
Index: ygot.Uint64(42),
IpAddress: ygot.String("1.1.1.1"),
},
},
},
},
},
},
},
}, {
desc: "mpls entry",
inResponses: []*spb.GetResponse{{
Entry: []*spb.AFTEntry{{
NetworkInstance: defaultName,
Entry: &spb.AFTEntry_Mpls{
Mpls: &aftpb.Afts_LabelEntryKey{
Label: &aftpb.Afts_LabelEntryKey_LabelUint64{
LabelUint64: 42,
},
LabelEntry: &aftpb.Afts_LabelEntry{
NextHopGroup: &wpb.UintValue{Value: 42},
},
},
},
}},
}},
inDefaultName: "DEFAULT",
wantRIB: &RIB{
defaultName: defaultName,
niRIB: map[string]*RIBHolder{
"DEFAULT": {
name: "DEFAULT",
r: &aft.RIB{
Afts: &aft.Afts{
LabelEntry: map[aft.Afts_LabelEntry_Label_Union]*aft.Afts_LabelEntry{
aft.UnionUint32(42): {
Label: aft.UnionUint32(42),
NextHopGroup: ygot.Uint64(42),
},
},
},
},
},
},
},
}, {
desc: "multiple network instances",
inResponses: []*spb.GetResponse{{
Entry: []*spb.AFTEntry{{
NetworkInstance: "VRF-1",
Entry: &spb.AFTEntry_Ipv4{
Ipv4: &aftpb.Afts_Ipv4EntryKey{
Prefix: "1.1.1.1/32",
Ipv4Entry: &aftpb.Afts_Ipv4Entry{
NextHopGroup: &wpb.UintValue{Value: 42},
},
},
},
}, {
NetworkInstance: defaultName,
Entry: &spb.AFTEntry_Ipv4{
Ipv4: &aftpb.Afts_Ipv4EntryKey{
Prefix: "2.2.2.2/32",
Ipv4Entry: &aftpb.Afts_Ipv4Entry{
NextHopGroup: &wpb.UintValue{Value: 42},
},
},
},
}},
}},
inDefaultName: "DEFAULT",
wantRIB: &RIB{
defaultName: defaultName,
niRIB: map[string]*RIBHolder{
"DEFAULT": {
name: "DEFAULT",
r: &aft.RIB{
Afts: &aft.Afts{
Ipv4Entry: map[string]*aft.Afts_Ipv4Entry{
"2.2.2.2/32": {
Prefix: ygot.String("2.2.2.2/32"),
NextHopGroup: ygot.Uint64(42),
},
},
},
},
},
"VRF-1": {
name: "VRF-1",
r: &aft.RIB{
Afts: &aft.Afts{
Ipv4Entry: map[string]*aft.Afts_Ipv4Entry{
"1.1.1.1/32": {
Prefix: ygot.String("1.1.1.1/32"),
NextHopGroup: ygot.Uint64(42),
},
},
},
},
},
},
},
}}

for _, tt := range tests {
t.Run(tt.desc, func(t *testing.T) {
got, err := RIBFromGetResponses(tt.inDefaultName, tt.inResponses)
if (err != nil) != tt.wantErr {
t.Fatalf("RIBFromGetResponses(...): did not get expected error, got: %v, wantErr? %v", err, tt.wantErr)
}

if diff := cmp.Diff(got, tt.wantRIB,
cmpopts.EquateEmpty(), cmp.AllowUnexported(RIB{}),
cmpopts.IgnoreFields(RIB{}, "nrMu", "pendMu", "ribCheck"),
cmp.AllowUnexported(RIBHolder{}),
cmpopts.IgnoreFields(RIBHolder{}, "mu", "refCounts", "checkFn"),
); diff != "" {
t.Fatalf("RIBFromGetResponses(...): did not get expected RIB, diff(-got,+want):\n%s", diff)
}
})
}
}

0 comments on commit 0717f4c

Please sign in to comment.