go.ligato.io/vpp-agent/v3@v3.5.0/plugins/vpp/puntplugin/descriptor/punt_exception.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  	"errors"
    19  	"strings"
    20  
    21  	"go.ligato.io/cn-infra/v2/logging"
    22  	"google.golang.org/protobuf/proto"
    23  
    24  	"go.ligato.io/vpp-agent/v3/pkg/models"
    25  	kvs "go.ligato.io/vpp-agent/v3/plugins/kvscheduler/api"
    26  	"go.ligato.io/vpp-agent/v3/plugins/vpp/puntplugin/descriptor/adapter"
    27  	"go.ligato.io/vpp-agent/v3/plugins/vpp/puntplugin/vppcalls"
    28  	punt "go.ligato.io/vpp-agent/v3/proto/ligato/vpp/punt"
    29  )
    30  
    31  const (
    32  	// PuntExceptionDescriptorName is the name of the descriptor for the VPP punt exception
    33  	PuntExceptionDescriptorName = "vpp-punt-exception"
    34  )
    35  
    36  // A list of non-retriable errors:
    37  var (
    38  	// ErrPuntExceptionWithoutReason is returned when VPP punt exception has undefined reason.
    39  	ErrPuntExceptionWithoutReason = errors.New("VPP punt exception defined without reason")
    40  )
    41  
    42  // PuntExceptionDescriptor teaches KVScheduler how to configure VPP putn exception.
    43  type PuntExceptionDescriptor struct {
    44  	RegisterSocketFn func(register bool, toHost *punt.Exception, socketPath string)
    45  
    46  	// dependencies
    47  	log         logging.Logger
    48  	puntHandler vppcalls.PuntVppAPI
    49  }
    50  
    51  // NewPuntExceptionDescriptor creates a new instance of the punt exception.
    52  func NewPuntExceptionDescriptor(puntHandler vppcalls.PuntVppAPI, log logging.LoggerFactory) *PuntExceptionDescriptor {
    53  	return &PuntExceptionDescriptor{
    54  		log:         log.NewLogger("punt-exception-descriptor"),
    55  		puntHandler: puntHandler,
    56  	}
    57  }
    58  
    59  // GetDescriptor returns descriptor suitable for registration (via adapter) with
    60  // the KVScheduler.
    61  func (d *PuntExceptionDescriptor) GetDescriptor() *adapter.PuntExceptionDescriptor {
    62  	return &adapter.PuntExceptionDescriptor{
    63  		Name:            PuntExceptionDescriptorName,
    64  		NBKeyPrefix:     punt.ModelException.KeyPrefix(),
    65  		ValueTypeName:   punt.ModelException.ProtoName(),
    66  		KeySelector:     punt.ModelException.IsKeyValid,
    67  		KeyLabel:        punt.ModelException.StripKeyPrefix,
    68  		ValueComparator: d.EquivalentPuntException,
    69  		Validate:        d.Validate,
    70  		Create:          d.Create,
    71  		Delete:          d.Delete,
    72  		Retrieve:        d.Retrieve,
    73  	}
    74  }
    75  
    76  // EquivalentPuntException is case-insensitive comparison function for punt.Exception.
    77  func (d *PuntExceptionDescriptor) EquivalentPuntException(key string, oldPunt, newPunt *punt.Exception) bool {
    78  	if oldPunt.Reason != newPunt.Reason {
    79  		return false
    80  	}
    81  
    82  	// if the socket path contains '!' as prefix we return false
    83  	// to force scheduler to recreate (register) punt socket
    84  	if strings.HasPrefix(oldPunt.SocketPath, "!") {
    85  		return false
    86  	}
    87  
    88  	return true
    89  }
    90  
    91  // Validate validates VPP punt configuration.
    92  func (d *PuntExceptionDescriptor) Validate(key string, puntCfg *punt.Exception) error {
    93  	// validate reason
    94  	if puntCfg.GetReason() == "" {
    95  		return ErrPuntExceptionWithoutReason
    96  	}
    97  
    98  	if puntCfg.SocketPath == "" {
    99  		return kvs.NewInvalidValueError(ErrPuntWithoutSocketPath, "socket_path")
   100  	}
   101  
   102  	return nil
   103  }
   104  
   105  // Create adds new punt to host entry or registers new punt to unix domain socket.
   106  func (d *PuntExceptionDescriptor) Create(key string, punt *punt.Exception) (interface{}, error) {
   107  	// register punt exception
   108  	pathname, err := d.puntHandler.AddPuntException(punt)
   109  	if err != nil {
   110  		d.log.Error(err)
   111  		return nil, err
   112  	}
   113  
   114  	if d.RegisterSocketFn != nil {
   115  		d.RegisterSocketFn(true, punt, pathname)
   116  	}
   117  
   118  	return nil, nil
   119  }
   120  
   121  // Delete removes VPP punt configuration.
   122  func (d *PuntExceptionDescriptor) Delete(key string, p *punt.Exception, metadata interface{}) error {
   123  	// check if the socketpath contains '!' as prefix from retrieve
   124  	punt := proto.Clone(p).(*punt.Exception)
   125  	punt.SocketPath = strings.TrimPrefix(punt.SocketPath, "!")
   126  
   127  	// delete punt exception
   128  	err := d.puntHandler.DeletePuntException(punt)
   129  	if err != nil {
   130  		d.log.Error(err)
   131  		return err
   132  	}
   133  
   134  	if d.RegisterSocketFn != nil {
   135  		d.RegisterSocketFn(false, punt, "")
   136  	}
   137  
   138  	return nil
   139  }
   140  
   141  // Retrieve returns all configured VPP punt exception entries.
   142  func (d *PuntExceptionDescriptor) Retrieve(correlate []adapter.PuntExceptionKVWithMetadata) (retrieved []adapter.PuntExceptionKVWithMetadata, err error) {
   143  	// Dump punt exceptions
   144  	punts, err := d.puntHandler.DumpExceptions()
   145  	if err == vppcalls.ErrUnsupported {
   146  		return nil, nil
   147  	} else if err != nil {
   148  		return nil, err
   149  	}
   150  
   151  	// for all dumped punts that were not yet registered and for which
   152  	// the VPP socket is unknown we prepend '!' as prefix
   153  	// to allow descriptor to recognize this in equivalent
   154  	// and force recreation or make it possible to delete it
   155  	for _, p := range punts {
   156  		if p.Exception.SocketPath == "" && p.SocketPath != "" {
   157  			p.Exception.SocketPath = "!" + p.SocketPath
   158  		}
   159  	}
   160  
   161  	for _, p := range punts {
   162  		retrieved = append(retrieved, adapter.PuntExceptionKVWithMetadata{
   163  			Key:    models.Key(p.Exception),
   164  			Value:  p.Exception,
   165  			Origin: kvs.FromNB,
   166  		})
   167  	}
   168  
   169  	return retrieved, nil
   170  }