Skip to content
New issue

Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? # to your account

Framework for reconciler implementation. #194

Merged
merged 4 commits into from
Oct 18, 2023
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
115 changes: 115 additions & 0 deletions rib/reconciler/reconcile.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
// 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 reconciler reconciles the contents of two gRIBI RIBs -- the intended RIB
// is assumed to contain the desired RIB entries, whereas the 'target' RIB is to be
// programmed. The reconciler:
//
// - Uses the messages that are returned from the `Get` RPC to build the contents of
// an external RIB.
// - Calculates a diff between the two RIBs.
// - Sends gRIBI operations to the target RIB to make it consistent with the
// intended RIB.
package reconciler

import (
"fmt"

"github.com/openconfig/gribigo/rib"

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

type R struct {
// intended and target are the mechanisms by which to access the intended
// RIB (source of truth) and the target it is to be reconciled with.
intended, target RIBTarget

// intended is a RIB containing the AFT entries that are intended to be
// programmed by the reconciler.
lastIntended *rib.RIB
// lastTarget is a cache of the last RIB entries that were returned from
// the target RIB.
lastTarget *rib.RIB
}

// RIBTarget is an interface that abstracts a local and remote RIB in the
// reconciler. It allows the RIB contents to be retrieved and programmed either
// via gRIBI or from a local RIB cache.
type RIBTarget interface {
// Get returns a RIB containing all network-instances and AFTs that are
// supported by the RIB.
Get() (*rib.RIB, error)
}

// LocalRIB wraps a RIB that is locally available on the system as a gRIBIgo
// RIB type.
type LocalRIB struct {
r *rib.RIB
}

// Get returns the contents of the local RIB.
func (l *LocalRIB) Get() (*rib.RIB, error) {
return l.r, nil
}

// New returns a new reconciler with the specified intended and target RIBs.
func New(intended, target RIBTarget) *R {
return &R{
intended: intended,
target: target,
}
}

// Reconcile performs a reconciliation operation between the intended and specified
// remote RIB.
func (r *R) Reconcile() error {
// Get the current contents of intended and target.
iRIB, err := r.intended.Get()
if err != nil {
return fmt.Errorf("cannot reconcile RIBs, cannot get contents of intended, %v", err)
}

tRIB, err := r.target.Get()
if err != nil {
return fmt.Errorf("cannot reconcile RIBs, cannot get contents of target, %v", err)
}

// Perform diff on their contents.

diffs, err := diff(iRIB, tRIB)
if err != nil {
return fmt.Errorf("cannot reconcile RIBs, cannot calculate diff, %v", err)
}
_ = diffs
_, _ = r.lastIntended, r.lastTarget

// Enqueue the operations towards target that bring it in-line with intended.
// TODO(robjs): Implement enqueuing in client.
return fmt.Errorf("reconciliation unimplemented")

}

// diff returns the difference between the src and dst RIBs expressed as gRIBI
// AFTOperations. That is to say, for each network instance RIB within the RIBs:
//
// - entries that are present in src but not dst are returned as ADD
// operations.
// - entries that are present in src but not dst and their contents diff are
// returned as MODIFY operations.
// - entries that are not present in src but are present in dst are returned
// as DELETE operations.
func diff(src, dst *rib.RIB) ([]*spb.AFTOperation, error) {
return nil, fmt.Errorf("unimplemented")
}
34 changes: 34 additions & 0 deletions rib/reconciler/reconcile_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package reconciler

import (
"testing"

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

func TestLocalRIB(t *testing.T) {

t.Run("get", func(t *testing.T) {
l := &LocalRIB{
r: rib.New("DEFAULT"),
}

want := rib.New("DEFAULT")
got, err := l.Get()
if err != nil {
t.Fatalf("(*LocalRIB).Get(): did not get expected error, got: %v, want: nil", err)
}

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

}
2 changes: 1 addition & 1 deletion server/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -1009,7 +1009,7 @@ func checkElectionForModify(opID uint64, opElecID *spb.Uint128, election *electi
return nil, true, nil
}

// doGet implents the Get RPC for the gRIBI server. It handles the input GetRequest, writing
// doGet implements the Get RPC for the gRIBI server. It handles the input GetRequest, writing
// the set of GetResponses to the specified msgCh. When the Get is done, the function writes to
// doneCh such that the caller knows that the work that is being done is complete. If a message
// is received on stopCh the function returns. Any errors that are experienced are written to
Expand Down