go.ligato.io/vpp-agent/v3@v3.5.0/examples/kvscheduler/mock_plugins/l2plugin/descriptor/fib.go (about)

     1  // Copyright (c) 2018 Cisco and/or its affiliates.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at:
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package descriptor
    16  
    17  import (
    18  	"net"
    19  	"strings"
    20  
    21  	"github.com/pkg/errors"
    22  	"go.ligato.io/cn-infra/v2/logging"
    23  
    24  	ifdescriptor "go.ligato.io/vpp-agent/v3/examples/kvscheduler/mock_plugins/ifplugin/descriptor"
    25  	"go.ligato.io/vpp-agent/v3/examples/kvscheduler/mock_plugins/l2plugin/descriptor/adapter"
    26  	"go.ligato.io/vpp-agent/v3/examples/kvscheduler/mock_plugins/l2plugin/mockcalls"
    27  	l2 "go.ligato.io/vpp-agent/v3/examples/kvscheduler/mock_plugins/l2plugin/model"
    28  	"go.ligato.io/vpp-agent/v3/pkg/models"
    29  	kvs "go.ligato.io/vpp-agent/v3/plugins/kvscheduler/api"
    30  )
    31  
    32  const (
    33  	// FIBDescriptorName is the name of the descriptor for L2 FIBs in the mock SB.
    34  	FIBDescriptorName = "mock-l2-fib"
    35  
    36  	// dependency labels
    37  	bridgedInterfaceDep = "bridged-interface"
    38  	bridgeDomainDep     = "bridge-domain"
    39  )
    40  
    41  // Example of some validation errors:
    42  var (
    43  	// ErrFIBWithoutHwAddr is returned when mock L2 FIB has undefined hardware
    44  	// address.
    45  	ErrFIBWithoutHwAddr = errors.New("mock L2 FIB defined without hardware address")
    46  
    47  	// ErrFIBWithoutBD is returned when mock L2 FIB has undefined bridge domain.
    48  	ErrFIBWithoutBD = errors.New("mock L2 FIB defined without bridge domain")
    49  
    50  	// ErrForwardFIBWithoutInterface is returned when mock L2 FORWARD FIB has undefined outgoing interface.
    51  	ErrForwardFIBWithoutInterface = errors.New("mock L2 FORWARD FIB defined without outgoing interface")
    52  )
    53  
    54  // FIBDescriptor teaches KVScheduler how to configure L2 FIBs in the mock SB.
    55  type FIBDescriptor struct {
    56  	// dependencies
    57  	log        logging.Logger
    58  	fibHandler mockcalls.MockFIBAPI
    59  }
    60  
    61  // NewFIBDescriptor creates a new instance of the FIB descriptor.
    62  func NewFIBDescriptor(fibHandler mockcalls.MockFIBAPI, log logging.PluginLogger) *kvs.KVDescriptor {
    63  	// descriptors are supposed to be stateless and this principle is not broken
    64  	// here - we only need to keep context consisting of references to logger
    65  	// and the FIB handler for mock SB, to be used inside the CRUD methods.
    66  	descrCtx := &FIBDescriptor{
    67  		fibHandler: fibHandler,
    68  		log:        log.NewLogger("mock-l2-fib-descriptor"),
    69  	}
    70  
    71  	// use adapter to convert typed descriptor into generic descriptor API
    72  	typedDescr := &adapter.FIBDescriptor{
    73  		Name:            FIBDescriptorName,
    74  		NBKeyPrefix:     l2.ModelFIBEntry.KeyPrefix(),
    75  		ValueTypeName:   l2.ModelFIBEntry.ProtoName(),
    76  		KeySelector:     l2.ModelFIBEntry.IsKeyValid,
    77  		KeyLabel:        l2.ModelFIBEntry.StripKeyPrefix,
    78  		ValueComparator: descrCtx.EquivalentFIBs,
    79  		Validate:        descrCtx.Validate,
    80  		Create:          descrCtx.Create,
    81  		Delete:          descrCtx.Delete,
    82  		Retrieve:        descrCtx.Retrieve,
    83  		Dependencies:    descrCtx.Dependencies,
    84  
    85  		// Note: Update operation is not defined, which will cause any change
    86  		//       in the FIB configuration to be applied via full re-creation
    87  		//       (Delete for the obsolete config, followed by Create for the new
    88  		//        config).
    89  
    90  		// Note: L2 FIBs do not need any metadata in our example with mock SB.
    91  
    92  		// Retrieve interfaces and bridge domain first to have the indexes with
    93  		// interface and BD metadata up-to-date when Retrieve for FIBs is called,
    94  		// which then uses the index to translate interface and BD names to the
    95  		// corresponding integer handles used in the mock SB.
    96  
    97  		RetrieveDependencies: []string{ifdescriptor.InterfaceDescriptorName, BridgeDomainDescriptorName},
    98  	}
    99  	return adapter.NewFIBDescriptor(typedDescr)
   100  }
   101  
   102  // EquivalentFIBs is case-insensitive comparison function for l2.FIBEntry.
   103  func (d *FIBDescriptor) EquivalentFIBs(key string, oldFIB, newFIB *l2.FIBEntry) bool {
   104  	// parameters compared as usually
   105  	if oldFIB.Action != newFIB.Action || oldFIB.BridgeDomain != newFIB.BridgeDomain {
   106  		return false
   107  	}
   108  
   109  	// outgoing interface is relevant only for FORWARD FIBs
   110  	if oldFIB.Action == l2.FIBEntry_FORWARD {
   111  		if oldFIB.OutgoingInterface != newFIB.OutgoingInterface {
   112  			return false
   113  		}
   114  	}
   115  
   116  	// MAC addresses compared case-insensitively
   117  	return strings.ToLower(oldFIB.PhysAddress) == strings.ToLower(newFIB.PhysAddress)
   118  }
   119  
   120  // Validate validates mock L2 FIB configuration.
   121  func (d *FIBDescriptor) Validate(key string, fib *l2.FIBEntry) error {
   122  	// validate MAC address
   123  	if fib.PhysAddress == "" {
   124  		return kvs.NewInvalidValueError(ErrFIBWithoutHwAddr, "phys_address")
   125  	}
   126  	_, err := net.ParseMAC(fib.PhysAddress)
   127  	if err != nil {
   128  		return kvs.NewInvalidValueError(err, "phys_address")
   129  	}
   130  
   131  	// validate outgoing interface reference
   132  	if fib.Action == l2.FIBEntry_FORWARD && fib.OutgoingInterface == "" {
   133  		return kvs.NewInvalidValueError(ErrForwardFIBWithoutInterface, "action", "outgoing_interface")
   134  	}
   135  
   136  	// validate bridge domain reference
   137  	if fib.BridgeDomain == "" {
   138  		return kvs.NewInvalidValueError(ErrFIBWithoutBD, "bridge_domain")
   139  	}
   140  	return nil
   141  }
   142  
   143  // Create adds new L2 FIB.
   144  func (d *FIBDescriptor) Create(key string, fib *l2.FIBEntry) (metadata interface{}, err error) {
   145  	// add L2 FIB
   146  	err = d.fibHandler.CreateL2FIB(fib)
   147  	if err != nil {
   148  		d.log.Error(err)
   149  	}
   150  	return nil, err
   151  }
   152  
   153  // Delete removes VPP L2 FIB.
   154  func (d *FIBDescriptor) Delete(key string, fib *l2.FIBEntry, metadata interface{}) error {
   155  	err := d.fibHandler.DeleteL2FIB(fib)
   156  	if err != nil {
   157  		d.log.Error(err)
   158  	}
   159  	return err
   160  }
   161  
   162  // Retrieve returns all configured VPP L2 FIBs.
   163  func (d *FIBDescriptor) Retrieve(correlate []adapter.FIBKVWithMetadata) (retrieved []adapter.FIBKVWithMetadata, err error) {
   164  	fibs, err := d.fibHandler.DumpL2FIBs()
   165  	if err != nil {
   166  		return nil, err
   167  	}
   168  
   169  	for _, fib := range fibs {
   170  		retrieved = append(retrieved, adapter.FIBKVWithMetadata{
   171  			Key:    models.Key(fib),
   172  			Value:  fib,
   173  			Origin: kvs.FromNB, // not considering OBTAINED FIBs in our simplified example
   174  		})
   175  	}
   176  	return retrieved, nil
   177  }
   178  
   179  // Dependencies for FIBs are:
   180  //  * FORWARD FIB: bridge domain + outgoing interface already put into the bridge domain
   181  //  * DROP FIB: bridge domain
   182  func (d *FIBDescriptor) Dependencies(key string, fib *l2.FIBEntry) (dependencies []kvs.Dependency) {
   183  	if fib.Action == l2.FIBEntry_FORWARD {
   184  		// example of a dependency on a derived value
   185  		dependencies = append(dependencies, kvs.Dependency{
   186  			Label: bridgedInterfaceDep,
   187  			Key:   l2.BDInterfaceKey(fib.BridgeDomain, fib.OutgoingInterface),
   188  		})
   189  	} else {
   190  		dependencies = append(dependencies, kvs.Dependency{
   191  			Label: bridgeDomainDep,
   192  			Key:   l2.BridgeDomainKey(fib.BridgeDomain),
   193  		})
   194  	}
   195  	return dependencies
   196  }