go.ligato.io/vpp-agent/v3@v3.5.0/plugins/linux/ifplugin/descriptor/interface_vrf.go (about)

     1  // Copyright (c) 2020 Pantheon.tech
     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  	"fmt"
    19  
    20  	"github.com/pkg/errors"
    21  	"go.ligato.io/cn-infra/v2/logging"
    22  	"google.golang.org/protobuf/proto"
    23  
    24  	kvs "go.ligato.io/vpp-agent/v3/plugins/kvscheduler/api"
    25  	"go.ligato.io/vpp-agent/v3/plugins/linux/ifplugin/descriptor/adapter"
    26  	"go.ligato.io/vpp-agent/v3/plugins/linux/ifplugin/ifaceidx"
    27  	iflinuxcalls "go.ligato.io/vpp-agent/v3/plugins/linux/ifplugin/linuxcalls"
    28  	"go.ligato.io/vpp-agent/v3/plugins/linux/nsplugin"
    29  	nslinuxcalls "go.ligato.io/vpp-agent/v3/plugins/linux/nsplugin/linuxcalls"
    30  	interfaces "go.ligato.io/vpp-agent/v3/proto/ligato/linux/interfaces"
    31  )
    32  
    33  const (
    34  	// InterfaceVrfDescriptorName is the name of the descriptor for assigning Linux interfaces into a VRF.
    35  	InterfaceVrfDescriptorName = "linux-interface-vrf"
    36  
    37  	// dependency labels
    38  	vrfDeviceDep   = "vrf-device-is-created"
    39  	externalVrfDep = "inserted-into-vrf-externally"
    40  )
    41  
    42  // InterfaceVrfDescriptor (un)assigns Linux interface to/from VRF.
    43  type InterfaceVrfDescriptor struct {
    44  	log       logging.Logger
    45  	ifHandler iflinuxcalls.NetlinkAPI
    46  	nsPlugin  nsplugin.API
    47  	intfIndex ifaceidx.LinuxIfMetadataIndex
    48  }
    49  
    50  // NewInterfaceVrfDescriptor creates a new instance of InterfaceVrfDescriptor.
    51  func NewInterfaceVrfDescriptor(nsPlugin nsplugin.API,
    52  	ifHandler iflinuxcalls.NetlinkAPI, log logging.PluginLogger) (descr *kvs.KVDescriptor, ctx *InterfaceVrfDescriptor) {
    53  
    54  	ctx = &InterfaceVrfDescriptor{
    55  		ifHandler: ifHandler,
    56  		nsPlugin:  nsPlugin,
    57  		log:       log.NewLogger("interface-vrf-descriptor"),
    58  	}
    59  	typedDescr := &adapter.InterfaceVrfDescriptor{
    60  		Name:        InterfaceVrfDescriptorName,
    61  		KeySelector: ctx.IsInterfaceVrfKey,
    62  		ValueComparator: func(_ string, _, _ *interfaces.Interface) bool {
    63  			// compare VRF assignments based on keys, not values that contain also other interface attributes
    64  			// needed by the descriptor
    65  			// FIXME: we can get rid of this hack once we add Context to descriptor methods
    66  			return true
    67  		},
    68  		Create:       ctx.Create,
    69  		Delete:       ctx.Delete,
    70  		Dependencies: ctx.Dependencies,
    71  	}
    72  	descr = adapter.NewInterfaceVrfDescriptor(typedDescr)
    73  	return
    74  }
    75  
    76  // SetInterfaceIndex should be used to provide interface index immediately after
    77  // the descriptor registration.
    78  func (d *InterfaceVrfDescriptor) SetInterfaceIndex(intfIndex ifaceidx.LinuxIfMetadataIndex) {
    79  	d.intfIndex = intfIndex
    80  }
    81  
    82  // IsInterfaceVrfKey returns true if the key represents assignment of a Linux interface into a VRF.
    83  func (d *InterfaceVrfDescriptor) IsInterfaceVrfKey(key string) bool {
    84  	_, _, _, isVrfKey := interfaces.ParseInterfaceVrfKey(key)
    85  	return isVrfKey
    86  }
    87  
    88  // Validate validates derived key.
    89  func (d *InterfaceVrfDescriptor) Validate(key string, emptyVal proto.Message) (err error) {
    90  	_, _, invalidKey, _ := interfaces.ParseInterfaceVrfKey(key)
    91  	if invalidKey {
    92  		return errors.New("invalid key")
    93  	}
    94  	return nil
    95  }
    96  
    97  // Create puts interface into a VRF.
    98  func (d *InterfaceVrfDescriptor) Create(key string, iface *interfaces.Interface) (metadata interface{}, err error) {
    99  	ifaceName, vrf, _, _ := interfaces.ParseInterfaceVrfKey(key)
   100  	ifMeta, found := d.intfIndex.LookupByName(ifaceName)
   101  	if !found {
   102  		err = errors.Errorf("failed to find interface %s", iface)
   103  		d.log.Error(err)
   104  		return nil, err
   105  	}
   106  	vrfMeta, found := d.intfIndex.LookupByName(vrf)
   107  	if !found {
   108  		err = errors.Errorf("failed to find VRF device %s", vrf)
   109  		d.log.Error(err)
   110  		return nil, err
   111  	}
   112  
   113  	if iface.Type == interfaces.Interface_EXISTING {
   114  		// Interface is managed externally, including its assignment into the VRF.
   115  		// While dependencies allow us to require that the interface and the VRF both exist
   116  		// and that the interface is inside *some* VRF, it is not possible to express requirement
   117  		// that the actual VRF is the same as the desired one. Therefore we check the condition here
   118  		// and return error if it is not the case, thus preventing items depending on this
   119  		// from being created. Once the interface is re-assigned to the proper VRF, this kv will be
   120  		// re-created with success.
   121  		ifaceLink, err := d.ifHandler.GetLinkByIndex(ifMeta.LinuxIfIndex)
   122  		if err != nil {
   123  			err = fmt.Errorf("failed to obtain interface %s link: %w", iface, err)
   124  			d.log.Error(err)
   125  			return nil, err
   126  		}
   127  		if ifaceLink.Attrs().MasterIndex != vrfMeta.LinuxIfIndex {
   128  			err = fmt.Errorf("existing interface %s is not inside VRF %s", iface, vrf)
   129  			d.log.Error(err)
   130  			return nil, err
   131  		}
   132  		return nil, nil
   133  	}
   134  
   135  	// switch to the namespace with the interface
   136  	nsCtx := nslinuxcalls.NewNamespaceMgmtCtx()
   137  	revert, err := d.nsPlugin.SwitchToNamespace(nsCtx, ifMeta.Namespace)
   138  	if err != nil {
   139  		d.log.Error(err)
   140  		return nil, err
   141  	}
   142  	defer revert()
   143  
   144  	err = d.ifHandler.PutInterfaceIntoVRF(ifMeta.HostIfName, vrfMeta.HostIfName)
   145  	if err != nil {
   146  		err = errors.WithMessagef(err, "failed to put interface '%s' into VRF '%s'",
   147  			ifMeta.HostIfName, vrfMeta.HostIfName)
   148  	}
   149  	return nil, err
   150  }
   151  
   152  // Delete removes interface from VRF.
   153  func (d *InterfaceVrfDescriptor) Delete(key string, iface *interfaces.Interface, metadata interface{}) (err error) {
   154  	ifaceName, vrf, _, _ := interfaces.ParseInterfaceVrfKey(key)
   155  	if iface.Type == interfaces.Interface_EXISTING {
   156  		// interface is managed externally, nothing to do here
   157  		return nil
   158  	}
   159  
   160  	ifMeta, found := d.intfIndex.LookupByName(ifaceName)
   161  	if !found {
   162  		err = errors.Errorf("failed to find interface %s", iface)
   163  		d.log.Error(err)
   164  		return err
   165  	}
   166  	vrfMeta, found := d.intfIndex.LookupByName(vrf)
   167  	if !found {
   168  		err = errors.Errorf("failed to find VRF device %s", vrf)
   169  		d.log.Error(err)
   170  		return err
   171  	}
   172  
   173  	// switch to the namespace with the interface
   174  	nsCtx := nslinuxcalls.NewNamespaceMgmtCtx()
   175  	revert, err := d.nsPlugin.SwitchToNamespace(nsCtx, ifMeta.Namespace)
   176  	if err != nil {
   177  		if _, ok := err.(*nsplugin.UnavailableMicroserviceErr); ok {
   178  			// Assume that the delete was called by scheduler because the namespace
   179  			// was removed. Do not return error in this case.
   180  			d.log.Debugf("Interface %s assumed to be unassigned from VRF %s, required namespace %+v does not exist",
   181  				iface, vrf, ifMeta.Namespace)
   182  			return nil
   183  		}
   184  		d.log.Error(err)
   185  		return err
   186  	}
   187  	defer revert()
   188  
   189  	err = d.ifHandler.RemoveInterfaceFromVRF(ifMeta.HostIfName, vrfMeta.HostIfName)
   190  	if err != nil {
   191  		err = errors.WithMessagef(err, "failed to remove interface '%s' from VRF '%s'",
   192  			ifMeta.HostIfName, vrfMeta.HostIfName)
   193  	}
   194  	return err
   195  }
   196  
   197  // Dependencies lists the VRF device as the only dependency.
   198  func (d *InterfaceVrfDescriptor) Dependencies(key string, iface *interfaces.Interface) (deps []kvs.Dependency) {
   199  	_, vrf, _, _ := interfaces.ParseInterfaceVrfKey(key)
   200  	if vrf != "" {
   201  		deps = append(deps, kvs.Dependency{
   202  			Label: vrfDeviceDep,
   203  			Key:   interfaces.InterfaceKey(vrf),
   204  		})
   205  	}
   206  	if iface.Type == interfaces.Interface_EXISTING {
   207  		// Interface is added into the VRF externally.
   208  		// KV dependencies do not allow us to fully express this dependency - we can express requirement
   209  		// that the interface is inside *some* VRF, but it not may be the desired one. This is because
   210  		// the VRF host name is not yet known at this point and therefore it is not possible to build the
   211  		// key to depend on.
   212  		// Verification that the desired and actual VRFs are the same is therefore done in the Create method
   213  		// and error is returned if it is not the case.
   214  		deps = append(deps, kvs.Dependency{
   215  			Label: externalVrfDep,
   216  			AnyOf: kvs.AnyOfDependency{
   217  				KeyPrefixes: []string{
   218  					interfaces.InterfaceHostNameWithVrfKey(getHostIfName(iface), ""),
   219  				},
   220  			},
   221  		})
   222  	}
   223  	return deps
   224  }