go.ligato.io/vpp-agent/v3@v3.5.0/plugins/vpp/abfplugin/descriptor/abf.go (about)

     1  //  Copyright (c) 2019 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  	"github.com/go-errors/errors"
    19  	"google.golang.org/protobuf/proto"
    20  	prototypes "google.golang.org/protobuf/types/known/emptypb"
    21  
    22  	"go.ligato.io/cn-infra/v2/idxmap"
    23  	"go.ligato.io/cn-infra/v2/logging"
    24  
    25  	"go.ligato.io/vpp-agent/v3/plugins/kvscheduler/api"
    26  	"go.ligato.io/vpp-agent/v3/plugins/vpp/abfplugin/abfidx"
    27  	"go.ligato.io/vpp-agent/v3/plugins/vpp/abfplugin/descriptor/adapter"
    28  	"go.ligato.io/vpp-agent/v3/plugins/vpp/abfplugin/vppcalls"
    29  	"go.ligato.io/vpp-agent/v3/plugins/vpp/aclplugin/aclidx"
    30  	"go.ligato.io/vpp-agent/v3/plugins/vpp/aclplugin/descriptor"
    31  	ifdescriptor "go.ligato.io/vpp-agent/v3/plugins/vpp/ifplugin/descriptor"
    32  	abf "go.ligato.io/vpp-agent/v3/proto/ligato/vpp/abf"
    33  	acl "go.ligato.io/vpp-agent/v3/proto/ligato/vpp/acl"
    34  	vpp_interfaces "go.ligato.io/vpp-agent/v3/proto/ligato/vpp/interfaces"
    35  )
    36  
    37  const (
    38  	// ABFDescriptorName is descriptor name
    39  	ABFDescriptorName = "vpp-abf"
    40  
    41  	// dependency labels
    42  	aclDep = "acl-exists"
    43  )
    44  
    45  // A list of non-retriable errors:
    46  var (
    47  	// ErrABFWithoutACL is returned when ABF configuration does not contain associated access list.
    48  	ErrABFWithoutACL = errors.New("ABF configuration defined without ACL")
    49  )
    50  
    51  // ABFDescriptor is descriptor for ABF
    52  type ABFDescriptor struct {
    53  	// dependencies
    54  	log        logging.Logger
    55  	abfHandler vppcalls.ABFVppAPI
    56  
    57  	// runtime
    58  	aclIndex aclidx.ACLMetadataIndex
    59  }
    60  
    61  // NewABFDescriptor is constructor for ABF descriptor and returns descriptor
    62  // suitable for registration (via adapter) with the KVScheduler.
    63  func NewABFDescriptor(
    64  	abfHandler vppcalls.ABFVppAPI,
    65  	aclIndex aclidx.ACLMetadataIndex,
    66  	logger logging.PluginLogger,
    67  ) *api.KVDescriptor {
    68  	ctx := &ABFDescriptor{
    69  		log:        logger.NewLogger("abf-descriptor"),
    70  		aclIndex:   aclIndex,
    71  		abfHandler: abfHandler,
    72  	}
    73  	typedDescr := &adapter.ABFDescriptor{
    74  		Name:          ABFDescriptorName,
    75  		NBKeyPrefix:   abf.ModelABF.KeyPrefix(),
    76  		ValueTypeName: abf.ModelABF.ProtoName(),
    77  		KeySelector:   abf.ModelABF.IsKeyValid,
    78  		KeyLabel:      abf.ModelABF.StripKeyPrefix,
    79  		WithMetadata:  true,
    80  		MetadataMapFactory: func() idxmap.NamedMappingRW {
    81  			return abfidx.NewABFIndex(ctx.log, "vpp-abf-index")
    82  		},
    83  		ValueComparator:      ctx.EquivalentABFs,
    84  		Validate:             ctx.Validate,
    85  		Create:               ctx.Create,
    86  		Delete:               ctx.Delete,
    87  		Retrieve:             ctx.Retrieve,
    88  		DerivedValues:        ctx.DerivedValues,
    89  		Dependencies:         ctx.Dependencies,
    90  		RetrieveDependencies: []string{ifdescriptor.InterfaceDescriptorName, descriptor.ACLDescriptorName},
    91  	}
    92  	return adapter.NewABFDescriptor(typedDescr)
    93  }
    94  
    95  // EquivalentABFs compares related ACL name, list of attached interfaces and forwarding paths to
    96  // specify ABS equality.
    97  func (d *ABFDescriptor) EquivalentABFs(key string, oldABF, newABF *abf.ABF) bool {
    98  	// check index and associated ACL
    99  	if oldABF.AclName != newABF.AclName {
   100  		return false
   101  	}
   102  
   103  	// compare attached interfaces
   104  	if len(oldABF.AttachedInterfaces) != len(newABF.AttachedInterfaces) {
   105  		return false
   106  	}
   107  	if !equivalentABFAttachedInterfaces(oldABF.AttachedInterfaces, newABF.AttachedInterfaces) {
   108  		return false
   109  	}
   110  
   111  	// compare forwarding paths
   112  	if len(oldABF.ForwardingPaths) != len(newABF.ForwardingPaths) {
   113  		return false
   114  	}
   115  	return equivalentABFForwardingPaths(oldABF.ForwardingPaths, newABF.ForwardingPaths)
   116  }
   117  
   118  // Validate validates VPP ABF configuration.
   119  func (d *ABFDescriptor) Validate(key string, abfData *abf.ABF) error {
   120  	if abfData.AclName == "" {
   121  		return api.NewInvalidValueError(ErrABFWithoutACL, "acl_name")
   122  	}
   123  	return nil
   124  }
   125  
   126  // Create validates ABF (mainly index), verifies ACL existence and configures ABF policy. Attached interfaces
   127  // are put to metadata together with the ABF index to make it available for other ABF descriptors.
   128  func (d *ABFDescriptor) Create(key string, abfData *abf.ABF) (*abfidx.ABFMetadata, error) {
   129  	// get ACL index
   130  	aclData, exists := d.aclIndex.LookupByName(abfData.AclName)
   131  	if !exists {
   132  		err := errors.Errorf("failed to obtain metadata for ACL %s", abfData.AclName)
   133  		d.log.Error(err)
   134  		return nil, err
   135  	}
   136  
   137  	// add new ABF policy
   138  	if err := d.abfHandler.AddAbfPolicy(abfData.Index, aclData.Index, abfData.ForwardingPaths); err != nil {
   139  		d.log.Error(err)
   140  		return nil, err
   141  	}
   142  
   143  	// fill the metadata
   144  	metadata := &abfidx.ABFMetadata{
   145  		Index:    abfData.Index,
   146  		Attached: abfData.AttachedInterfaces,
   147  	}
   148  
   149  	return metadata, nil
   150  }
   151  
   152  // Delete removes ABF policy
   153  func (d *ABFDescriptor) Delete(key string, abfData *abf.ABF, metadata *abfidx.ABFMetadata) error {
   154  	// ACL ID is not required
   155  	return d.abfHandler.DeleteAbfPolicy(metadata.Index, abfData.ForwardingPaths)
   156  }
   157  
   158  // Retrieve returns ABF policies from the VPP.
   159  func (d *ABFDescriptor) Retrieve(correlate []adapter.ABFKVWithMetadata) (abfs []adapter.ABFKVWithMetadata, err error) {
   160  	// Retrieve VPP configuration.
   161  	abfPolicies, err := d.abfHandler.DumpABFPolicy()
   162  	if err != nil {
   163  		return nil, errors.Errorf("failed to dump ABF policy: %v", err)
   164  	}
   165  
   166  	for _, abfPolicy := range abfPolicies {
   167  		abfs = append(abfs, adapter.ABFKVWithMetadata{
   168  			Key:   abf.Key(abfPolicy.ABF.Index),
   169  			Value: abfPolicy.ABF,
   170  			Metadata: &abfidx.ABFMetadata{
   171  				Index:    abfPolicy.Meta.PolicyID,
   172  				Attached: abfPolicy.ABF.AttachedInterfaces,
   173  			},
   174  			Origin: api.FromNB,
   175  		})
   176  	}
   177  
   178  	return abfs, nil
   179  }
   180  
   181  // DerivedValues returns list of derived values for ABF.
   182  func (d *ABFDescriptor) DerivedValues(key string, value *abf.ABF) (derived []api.KeyValuePair) {
   183  	for _, attachedIf := range value.GetAttachedInterfaces() {
   184  		derived = append(derived, api.KeyValuePair{
   185  			Key:   abf.ToInterfaceKey(value.Index, attachedIf.InputInterface),
   186  			Value: &prototypes.Empty{},
   187  		})
   188  	}
   189  	return derived
   190  }
   191  
   192  // A list of ABF dependencies (ACL + forwarding path interfaces).
   193  func (d *ABFDescriptor) Dependencies(key string, abfData *abf.ABF) (dependencies []api.Dependency) {
   194  	// forwarding path interfaces
   195  	for _, abfDataLabel := range abfData.ForwardingPaths {
   196  		if abfDataLabel.InterfaceName != "" {
   197  			dependencies = append(dependencies, api.Dependency{
   198  				Label: interfaceDep,
   199  				Key:   vpp_interfaces.InterfaceKey(abfDataLabel.InterfaceName),
   200  			})
   201  		}
   202  	}
   203  	// access list
   204  	dependencies = append(dependencies, api.Dependency{
   205  		Label: aclDep,
   206  		Key:   acl.Key(abfData.AclName),
   207  	})
   208  
   209  	return dependencies
   210  }
   211  
   212  func equivalentABFAttachedInterfaces(oldIfs, newIfs []*abf.ABF_AttachedInterface) bool {
   213  	for _, oldIf := range oldIfs {
   214  		var found bool
   215  		for _, newIf := range newIfs {
   216  			if proto.Equal(oldIf, newIf) {
   217  				found = true
   218  			}
   219  		}
   220  		if !found {
   221  			return false
   222  		}
   223  	}
   224  	return true
   225  }
   226  
   227  func equivalentABFForwardingPaths(oldPaths, newPaths []*abf.ABF_ForwardingPath) bool {
   228  	for _, oldPath := range oldPaths {
   229  		var found bool
   230  		for _, newPath := range newPaths {
   231  			if oldPath.InterfaceName == newPath.InterfaceName &&
   232  				oldPath.NextHopIp == newPath.NextHopIp &&
   233  				oldPath.Weight == newPath.Weight &&
   234  				oldPath.Preference == newPath.Preference &&
   235  				oldPath.Dvr == newPath.Dvr {
   236  				found = true
   237  			}
   238  		}
   239  		if !found {
   240  			return false
   241  		}
   242  	}
   243  	return true
   244  }