go.ligato.io/vpp-agent/v3@v3.5.0/examples/kvscheduler/mock_plugins/ifplugin/descriptor/interface.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/idxmap"
    23  	"go.ligato.io/cn-infra/v2/logging"
    24  
    25  	"go.ligato.io/vpp-agent/v3/examples/kvscheduler/mock_plugins/ifplugin/descriptor/adapter"
    26  	"go.ligato.io/vpp-agent/v3/examples/kvscheduler/mock_plugins/ifplugin/mockcalls"
    27  	interfaces "go.ligato.io/vpp-agent/v3/examples/kvscheduler/mock_plugins/ifplugin/model"
    28  	"go.ligato.io/vpp-agent/v3/pkg/idxvpp"
    29  	"go.ligato.io/vpp-agent/v3/pkg/models"
    30  	kvs "go.ligato.io/vpp-agent/v3/plugins/kvscheduler/api"
    31  )
    32  
    33  const (
    34  	// InterfaceDescriptorName is the name of the descriptor for mock interfaces.
    35  	InterfaceDescriptorName = "mock-interface"
    36  
    37  	// how many characters interface name is allowed to have
    38  	// (defined just to showcase Validation)
    39  	nameLengthLimit = 15
    40  )
    41  
    42  // Example of some validation errors:
    43  var (
    44  	// ErrInterfaceWithoutName is returned when interface configuration has
    45  	// undefined Name attribute.
    46  	ErrInterfaceWithoutName = errors.New("mock interface defined without logical name")
    47  
    48  	// ErrInterfaceNameTooLong is returned when mock interface name exceeds the length limit.
    49  	ErrInterfaceNameTooLong = errors.New("mock interface logical name exceeds the length limit (15 characters)")
    50  
    51  	// ErrInterfaceWithoutType is returned when mock interface configuration has undefined
    52  	// Type attribute.
    53  	ErrInterfaceWithoutType = errors.New("mock interface defined without type")
    54  )
    55  
    56  // InterfaceDescriptor teaches KVScheduler how to configure mock interfaces.
    57  type InterfaceDescriptor struct {
    58  
    59  	// dependencies
    60  	log          logging.Logger
    61  	ifaceHandler mockcalls.MockIfaceAPI
    62  }
    63  
    64  // NewInterfaceDescriptor creates a new instance of the Interface descriptor.
    65  func NewInterfaceDescriptor(ifaceHandler mockcalls.MockIfaceAPI, log logging.PluginLogger) *kvs.KVDescriptor {
    66  	// descriptors are supposed to be stateless and this principle is not broken
    67  	// here - we only need to keep context consisting of references to logger
    68  	// and the interface handler for mock SB, to be used inside the CRUD methods.
    69  	descrCtx := &InterfaceDescriptor{
    70  		ifaceHandler: ifaceHandler,
    71  		log:          log.NewLogger("mock-iface-descriptor"),
    72  	}
    73  
    74  	// use adapter to convert typed descriptor into generic descriptor API
    75  	typedDescr := &adapter.InterfaceDescriptor{
    76  		Name:               InterfaceDescriptorName,
    77  		NBKeyPrefix:        interfaces.ModelInterface.KeyPrefix(),
    78  		ValueTypeName:      interfaces.ModelInterface.ProtoName(),
    79  		KeySelector:        interfaces.ModelInterface.IsKeyValid,
    80  		KeyLabel:           interfaces.ModelInterface.StripKeyPrefix,
    81  		ValueComparator:    descrCtx.EquivalentInterfaces,
    82  		WithMetadata:       true,
    83  		MetadataMapFactory: descrCtx.MetadataFactory,
    84  		Validate:           descrCtx.Validate,
    85  		Create:             descrCtx.Create,
    86  		Delete:             descrCtx.Delete,
    87  		Update:             descrCtx.Update,
    88  		UpdateWithRecreate: descrCtx.UpdateWithRecreate,
    89  		Retrieve:           descrCtx.Retrieve,
    90  	}
    91  	return adapter.NewInterfaceDescriptor(typedDescr)
    92  }
    93  
    94  // EquivalentInterfaces is case-insensitive comparison function for
    95  // interfaces.Interface, also ignoring the order of assigned IP addresses.
    96  func (d *InterfaceDescriptor) EquivalentInterfaces(key string, oldIntf, newIntf *interfaces.Interface) bool {
    97  	// attributes compared as usually:
    98  	if oldIntf.Name != newIntf.Name ||
    99  		oldIntf.Type != newIntf.Type ||
   100  		oldIntf.Enabled != newIntf.Enabled {
   101  		return false
   102  	}
   103  
   104  	// compare MAC addresses case-insensitively (also handle unspecified MAC address)
   105  	if newIntf.PhysAddress != "" &&
   106  		strings.ToLower(oldIntf.PhysAddress) != strings.ToLower(newIntf.PhysAddress) {
   107  		return false
   108  	}
   109  
   110  	return true
   111  }
   112  
   113  // MetadataFactory is a factory for index-map customized for VPP interfaces.
   114  func (d *InterfaceDescriptor) MetadataFactory() idxmap.NamedMappingRW {
   115  	return idxvpp.NewNameToIndex(d.log, "mock-iface-index", nil)
   116  }
   117  
   118  // Validate validates VPP interface configuration.
   119  func (d *InterfaceDescriptor) Validate(key string, intf *interfaces.Interface) error {
   120  	// validate name
   121  	if name := intf.GetName(); name == "" {
   122  		return kvs.NewInvalidValueError(ErrInterfaceWithoutName, "name")
   123  	} else if len(name) > nameLengthLimit {
   124  		return kvs.NewInvalidValueError(ErrInterfaceNameTooLong, "name")
   125  	}
   126  
   127  	// validate type
   128  	if intf.Type == interfaces.Interface_UNDEFINED_TYPE {
   129  		return kvs.NewInvalidValueError(ErrInterfaceWithoutType, "type")
   130  	}
   131  
   132  	// validate MAC address
   133  	hwAddr := intf.GetPhysAddress()
   134  	if hwAddr != "" {
   135  		_, err := net.ParseMAC(hwAddr)
   136  		if err != nil {
   137  			return kvs.NewInvalidValueError(err, "phys_address")
   138  		}
   139  	}
   140  
   141  	return nil
   142  }
   143  
   144  // Create creates mock interface.
   145  func (d *InterfaceDescriptor) Create(key string, value *interfaces.Interface) (metadata *idxvpp.OnlyIndex, err error) {
   146  	// create interface of the given type and with the given name
   147  	var sbIfaceHandle uint32
   148  	if value.Type == interfaces.Interface_LOOPBACK {
   149  		sbIfaceHandle, err = d.ifaceHandler.CreateLoopbackInterface(value.Name)
   150  	} else {
   151  		sbIfaceHandle, err = d.ifaceHandler.CreateTapInterface(value.Name)
   152  	}
   153  	if err != nil {
   154  		return nil, err
   155  	}
   156  	metadata = &idxvpp.OnlyIndex{Index: sbIfaceHandle}
   157  
   158  	// set interface UP if requested
   159  	if value.Enabled {
   160  		err = d.ifaceHandler.InterfaceAdminUp(sbIfaceHandle)
   161  		if err != nil {
   162  			return nil, err
   163  		}
   164  	}
   165  
   166  	// set interface MAC address if requested
   167  	if value.PhysAddress != "" {
   168  		err = d.ifaceHandler.SetInterfaceMac(sbIfaceHandle, value.PhysAddress)
   169  		if err != nil {
   170  			return nil, err
   171  		}
   172  	}
   173  
   174  	return metadata, nil
   175  }
   176  
   177  // Delete removes a mock interface.
   178  func (d *InterfaceDescriptor) Delete(key string, value *interfaces.Interface, metadata *idxvpp.OnlyIndex) error {
   179  	var err error
   180  	if value.Type == interfaces.Interface_LOOPBACK {
   181  		err = d.ifaceHandler.DeleteLoopbackInterface(metadata.Index)
   182  	} else {
   183  		err = d.ifaceHandler.DeleteTapInterface(metadata.Index)
   184  	}
   185  	return err
   186  }
   187  
   188  // Update updates parameters of a mock interface.
   189  func (d *InterfaceDescriptor) Update(key string, oldValue, newValue *interfaces.Interface, oldMetadata *idxvpp.OnlyIndex) (newMetadata *idxvpp.OnlyIndex, err error) {
   190  	// no need to handle change of the Name or Type:
   191  	//  - different Name implies different key, i.e. completely different interface
   192  	//  - UpdateWithRecreate specifies that change of Type requires full interface re-creation
   193  
   194  	// update of the admin status
   195  	if oldValue.Enabled != newValue.Enabled {
   196  		if newValue.Enabled {
   197  			err = d.ifaceHandler.InterfaceAdminUp(oldMetadata.Index)
   198  		} else {
   199  			err = d.ifaceHandler.InterfaceAdminDown(oldMetadata.Index)
   200  		}
   201  		if err != nil {
   202  			return nil, err
   203  		}
   204  	}
   205  
   206  	// update of the MAC address
   207  	if oldValue.PhysAddress != newValue.PhysAddress && newValue.PhysAddress != "" {
   208  		err = d.ifaceHandler.SetInterfaceMac(oldMetadata.Index, newValue.PhysAddress)
   209  		if err != nil {
   210  			return nil, err
   211  		}
   212  	}
   213  
   214  	return oldMetadata, nil // metadata (sbIfaceIndex) has not changed
   215  }
   216  
   217  // UpdateWithRecreate returns true if Type is requested to be changed.
   218  func (d *InterfaceDescriptor) UpdateWithRecreate(key string, oldIntf, newIntf *interfaces.Interface, metadata *idxvpp.OnlyIndex) bool {
   219  	if oldIntf.Type != newIntf.Type {
   220  		return true
   221  	}
   222  	return false
   223  }
   224  
   225  // Retrieve returns all interfaces configured in the mock SB.
   226  func (d *InterfaceDescriptor) Retrieve(correlate []adapter.InterfaceKVWithMetadata) (retrieved []adapter.InterfaceKVWithMetadata, err error) {
   227  	ifaces, err := d.ifaceHandler.DumpInterfaces()
   228  	if err != nil {
   229  		return nil, err
   230  	}
   231  
   232  	for sbIfaceHandle, iface := range ifaces {
   233  		retrieved = append(retrieved, adapter.InterfaceKVWithMetadata{
   234  			Key:      models.Key(iface),
   235  			Value:    iface,
   236  			Metadata: &idxvpp.OnlyIndex{Index: sbIfaceHandle},
   237  			Origin:   kvs.FromNB, // not considering OBTAINED interfaces in our simplified example
   238  		})
   239  	}
   240  	return retrieved, nil
   241  }