go.ligato.io/vpp-agent/v3@v3.5.0/plugins/vpp/l3plugin/descriptor/l3xc.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  	"context"
    19  	"fmt"
    20  	"net"
    21  	"strconv"
    22  	"strings"
    23  
    24  	"github.com/pkg/errors"
    25  	"go.ligato.io/cn-infra/v2/logging"
    26  	"google.golang.org/protobuf/proto"
    27  
    28  	"go.ligato.io/vpp-agent/v3/pkg/models"
    29  	kvs "go.ligato.io/vpp-agent/v3/plugins/kvscheduler/api"
    30  	ifdescriptor "go.ligato.io/vpp-agent/v3/plugins/vpp/ifplugin/descriptor"
    31  	"go.ligato.io/vpp-agent/v3/plugins/vpp/ifplugin/ifaceidx"
    32  	"go.ligato.io/vpp-agent/v3/plugins/vpp/l3plugin/descriptor/adapter"
    33  	"go.ligato.io/vpp-agent/v3/plugins/vpp/l3plugin/vppcalls"
    34  	interfaces "go.ligato.io/vpp-agent/v3/proto/ligato/vpp/interfaces"
    35  	l3 "go.ligato.io/vpp-agent/v3/proto/ligato/vpp/l3"
    36  )
    37  
    38  const (
    39  	// L3XCDescriptorName is the name of the descriptor.
    40  	L3XCDescriptorName = "vpp-l3xc"
    41  
    42  	// dependency labels
    43  	l3xcTargetInterfaceDep = "target-interface-exists"
    44  	l3xcPathInterfaceDep   = "outgoing-interface-exists"
    45  )
    46  
    47  // L3XCDescriptor teaches KVScheduler how to configure VPP L3XCs.
    48  type L3XCDescriptor struct {
    49  	log         logging.Logger
    50  	l3xcHandler vppcalls.L3XCVppAPI
    51  	ifIndexes   ifaceidx.IfaceMetadataIndex
    52  }
    53  
    54  // NewL3XCDescriptor creates a new instance of the L3XCDescriptor.
    55  func NewL3XCDescriptor(l3xcHandler vppcalls.L3XCVppAPI, ifIndexes ifaceidx.IfaceMetadataIndex,
    56  	log logging.PluginLogger,
    57  ) *kvs.KVDescriptor {
    58  
    59  	ctx := &L3XCDescriptor{
    60  		ifIndexes:   ifIndexes,
    61  		l3xcHandler: l3xcHandler,
    62  		log:         log.NewLogger("l3xc-descriptor"),
    63  	}
    64  
    65  	typedDescr := &adapter.L3XCDescriptor{
    66  		Name:                 L3XCDescriptorName,
    67  		NBKeyPrefix:          l3.ModelL3XC.KeyPrefix(),
    68  		ValueTypeName:        l3.ModelL3XC.ProtoName(),
    69  		KeySelector:          l3.ModelL3XC.IsKeyValid,
    70  		KeyLabel:             l3.ModelL3XC.StripKeyPrefix,
    71  		ValueComparator:      ctx.EquivalentL3XCs,
    72  		Validate:             ctx.Validate,
    73  		Create:               ctx.Create,
    74  		Update:               ctx.Update,
    75  		Delete:               ctx.Delete,
    76  		Retrieve:             ctx.Retrieve,
    77  		Dependencies:         ctx.Dependencies,
    78  		RetrieveDependencies: []string{ifdescriptor.InterfaceDescriptorName},
    79  	}
    80  	return adapter.NewL3XCDescriptor(typedDescr)
    81  }
    82  
    83  // EquivalentL3XCs is comparison function for L3XC entries.
    84  func (d *L3XCDescriptor) EquivalentL3XCs(key string, oldL3XC, newL3XC *l3.L3XConnect) bool {
    85  	return proto.Equal(oldL3XC, newL3XC)
    86  }
    87  
    88  // Validate returns if given l3xc is valid.
    89  func (d *L3XCDescriptor) Validate(key string, l3xc *l3.L3XConnect) error {
    90  	if l3xc.Interface == "" {
    91  		return errors.Errorf("no interface defined")
    92  	}
    93  	if len(l3xc.Paths) == 0 {
    94  		return errors.Errorf("no paths defined")
    95  	}
    96  	return nil
    97  }
    98  
    99  // Dependencies lists dependencies for a VPP L3XC entry.
   100  func (d *L3XCDescriptor) Dependencies(key string, l3xc *l3.L3XConnect) (deps []kvs.Dependency) {
   101  	// the outgoing interface must exist
   102  	if l3xc.Interface != "" {
   103  		deps = append(deps, kvs.Dependency{
   104  			Label: l3xcTargetInterfaceDep,
   105  			Key:   interfaces.InterfaceKey(l3xc.Interface),
   106  		})
   107  	}
   108  	for _, path := range l3xc.Paths {
   109  		deps = append(deps, kvs.Dependency{
   110  			Label: l3xcPathInterfaceDep,
   111  			Key:   interfaces.InterfaceKey(path.OutgoingInterface),
   112  		})
   113  	}
   114  	return deps
   115  }
   116  
   117  // Create adds VPP L3XC entry.
   118  func (d *L3XCDescriptor) Create(key string, l3xc *l3.L3XConnect) (interface{}, error) {
   119  	return d.update(key, l3xc)
   120  }
   121  
   122  // Update updates VPP L3XC entry.
   123  func (d *L3XCDescriptor) Update(key string, oldL3XC, newL3XC *l3.L3XConnect, oldMeta interface{}) (interface{}, error) {
   124  	return d.update(key, newL3XC)
   125  }
   126  
   127  func (d *L3XCDescriptor) update(key string, l3xc *l3.L3XConnect) (interface{}, error) {
   128  	ctx := context.TODO()
   129  
   130  	var swIfIndex uint32
   131  	if strings.HasPrefix(l3xc.Interface, "MISSING-") {
   132  		idx := strings.TrimPrefix(l3xc.Interface, "MISSING-")
   133  		x, _ := strconv.ParseUint(idx, 10, 32)
   134  		swIfIndex = uint32(x)
   135  	} else {
   136  		meta, found := d.ifIndexes.LookupByName(l3xc.Interface)
   137  		if !found {
   138  			return nil, errors.Errorf("interface %s not found", l3xc.Interface)
   139  		}
   140  		swIfIndex = meta.SwIfIndex
   141  	}
   142  
   143  	paths := make([]vppcalls.Path, len(l3xc.Paths))
   144  	for i, p := range l3xc.Paths {
   145  		pmeta, found := d.ifIndexes.LookupByName(p.OutgoingInterface)
   146  		if !found {
   147  			return nil, errors.Errorf("interface %s from path #%d not found", p.OutgoingInterface, i)
   148  		}
   149  		paths[i] = vppcalls.Path{
   150  			SwIfIndex:  pmeta.SwIfIndex,
   151  			Weight:     uint8(p.Weight),
   152  			Preference: uint8(p.Preference),
   153  			NextHop:    net.ParseIP(p.NextHopAddr),
   154  		}
   155  	}
   156  
   157  	if err := d.l3xcHandler.UpdateL3XC(ctx, &vppcalls.L3XC{
   158  		SwIfIndex: swIfIndex,
   159  		IsIPv6:    l3xc.Protocol == l3.L3XConnect_IPV6,
   160  		Paths:     paths,
   161  	}); err != nil {
   162  		return nil, err
   163  	}
   164  
   165  	return nil, nil
   166  }
   167  
   168  // Delete removes VPP L3XC entry.
   169  func (d *L3XCDescriptor) Delete(key string, l3xc *l3.L3XConnect, metadata interface{}) error {
   170  	ctx := context.TODO()
   171  
   172  	var swIfIndex uint32
   173  	if strings.HasPrefix(l3xc.Interface, "MISSING-") {
   174  		idx := strings.TrimPrefix(l3xc.Interface, "MISSING-")
   175  		x, _ := strconv.ParseUint(idx, 10, 32)
   176  		swIfIndex = uint32(x)
   177  	} else {
   178  		meta, found := d.ifIndexes.LookupByName(l3xc.Interface)
   179  		if !found {
   180  			return errors.Errorf("interface %s not found", l3xc.Interface)
   181  		}
   182  		swIfIndex = meta.SwIfIndex
   183  	}
   184  	isIPv6 := l3xc.Protocol == l3.L3XConnect_IPV6
   185  
   186  	if err := d.l3xcHandler.DeleteL3XC(ctx, swIfIndex, isIPv6); err != nil {
   187  		return err
   188  	}
   189  
   190  	return nil
   191  }
   192  
   193  // Retrieve returns all L3XC entries associated with interfaces managed by this agent.
   194  func (d *L3XCDescriptor) Retrieve(correlate []adapter.L3XCKVWithMetadata) (
   195  	retrieved []adapter.L3XCKVWithMetadata, err error,
   196  ) {
   197  	ctx := context.TODO()
   198  
   199  	l3xcEntries, err := d.l3xcHandler.DumpAllL3XC(ctx)
   200  	if err != nil {
   201  		return nil, errors.Errorf("dumping VPP L3XCs failed: %v", err)
   202  	}
   203  
   204  	for _, l3xc := range l3xcEntries {
   205  		ifName, _, exists := d.ifIndexes.LookupBySwIfIndex(l3xc.SwIfIndex)
   206  		if !exists {
   207  			ifName = fmt.Sprintf("MISSING-%d", l3xc.SwIfIndex)
   208  			d.log.Warnf("L3XC dump: interface index %d not found", l3xc.SwIfIndex)
   209  
   210  		}
   211  		ipProto := l3.L3XConnect_IPV4
   212  		if l3xc.IsIPv6 {
   213  			ipProto = l3.L3XConnect_IPV6
   214  		}
   215  		paths := make([]*l3.L3XConnect_Path, len(l3xc.Paths))
   216  		for i, p := range l3xc.Paths {
   217  			ifNamePath, _, exists := d.ifIndexes.LookupBySwIfIndex(p.SwIfIndex)
   218  			if !exists {
   219  				ifNamePath = fmt.Sprintf("MISSING-%d", p.SwIfIndex)
   220  				d.log.Warnf("L3XC dump: interface index %d for path #%d not found", p.SwIfIndex, i)
   221  			}
   222  			paths[i] = &l3.L3XConnect_Path{
   223  				OutgoingInterface: ifNamePath,
   224  				NextHopAddr:       p.NextHop.String(),
   225  				Weight:            uint32(p.Weight),
   226  				Preference:        uint32(p.Preference),
   227  			}
   228  		}
   229  		value := &l3.L3XConnect{
   230  			Interface: ifName,
   231  			Protocol:  ipProto,
   232  			Paths:     paths,
   233  		}
   234  		retrieved = append(retrieved, adapter.L3XCKVWithMetadata{
   235  			Key:    models.Key(value),
   236  			Value:  value,
   237  			Origin: kvs.FromNB,
   238  		})
   239  	}
   240  
   241  	return retrieved, nil
   242  }