go.ligato.io/vpp-agent/v3@v3.5.0/plugins/vpp/handlers.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 vpp
    16  
    17  import (
    18  	"errors"
    19  	"fmt"
    20  	"sort"
    21  
    22  	govppapi "go.fd.io/govpp/api"
    23  	"go.ligato.io/cn-infra/v2/logging"
    24  )
    25  
    26  // HandlerVersion defines handler implementation for specific version used by AddVersion.
    27  type HandlerVersion struct {
    28  	Version    Version
    29  	Check      func(Client) error
    30  	NewHandler func(Client, ...interface{}) HandlerAPI
    31  	New        interface{}
    32  }
    33  
    34  // HandlerAPI is an empty interface representing handler interface.
    35  type HandlerAPI interface{}
    36  
    37  // Handler is a handler for managing implementations for multiple versions.
    38  type Handler struct {
    39  	desc     *HandlerDesc
    40  	versions map[Version]*HandlerVersion
    41  }
    42  
    43  func (h *Handler) Name() string {
    44  	return h.desc.Name
    45  }
    46  
    47  // AddVersion adds handler version to a list of available versions.
    48  // Handler versions can be overwritten by calling AddVersion multiple times.
    49  func (h *Handler) AddVersion(hv HandlerVersion) {
    50  	if _, ok := h.versions[hv.Version]; ok {
    51  		logging.Warnf("overwritting %s handler version: %s", h.desc.Name, hv.Version)
    52  	}
    53  	if hv.Check == nil {
    54  		panic(fmt.Sprintf("Check not defined for %s handler version: %s", h.desc.Name, hv.Version))
    55  	}
    56  	// TODO: check if given handler version implementes handler API interface
    57  	/*ht := reflect.TypeOf(h.desc.HandlerAPI).Elem()
    58  	  hc := reflect.TypeOf(hv.New).Out(0)
    59  	  if !hc.Implements(ht) {
    60  	  	logging.DefaultLogger.Warnf("vpphandlers: AddVersion found the handler of type %v that does not satisfy %v", hc, ht)
    61  	  }*/
    62  	h.versions[hv.Version] = &hv
    63  }
    64  
    65  // FindCompatibleVersion iterates over all available handler versions and calls
    66  // their Check method to check compatibility.
    67  func (h *Handler) FindCompatibleVersion(c Client) *HandlerVersion {
    68  	v, err := h.GetCompatibleVersion(c)
    69  	if err != nil {
    70  		logging.Debugf("no compatible version found for handler %v: %v", h.Name(), err)
    71  		return nil
    72  	}
    73  	logging.Debugf("found compatible version for handler %v: %v", h.Name(), v.Version)
    74  	return v
    75  }
    76  
    77  // GetCompatibleVersion iterates over all available handler versions and calls
    78  // their Check method to check compatibility.
    79  func (h *Handler) GetCompatibleVersion(c Client) (*HandlerVersion, error) {
    80  	if len(h.versions) == 0 {
    81  		logging.Debugf("VPP handler %s has no registered versions", h.desc.Name)
    82  		return nil, ErrNoVersions
    83  	}
    84  	// try preferred binapi version first
    85  	if ver := c.BinapiVersion(); ver != "" {
    86  		if v, ok := h.versions[ver]; ok {
    87  			logging.Debugf("VPP handler %s using preferred version: %s", h.desc.Name, v.Version)
    88  			return v, nil
    89  		}
    90  	}
    91  	// fallback to checking all registered versions
    92  	for _, v := range h.versions {
    93  		var compErr *govppapi.CompatibilityError
    94  		if err := v.Check(c); errors.As(err, &compErr) {
    95  			logging.Debugf("VPP handler %s incompatible with %s (%d messages)", h.desc.Name, v.Version, len(compErr.IncompatibleMessages))
    96  		} else if err != nil {
    97  			logging.Warnf("VPP handler %s version %s check failed: %v", h.desc.Name, v.Version, err)
    98  		} else {
    99  			logging.Debugf("VPP handler %s COMPATIBLE with version: %s", h.desc.Name, v.Version)
   100  			return v, nil
   101  		}
   102  	}
   103  	return nil, ErrIncompatible
   104  }
   105  
   106  // Versions returns list of versions from list of available handler versions.
   107  func (h *Handler) Versions() []Version {
   108  	vs := make([]Version, 0, len(h.versions))
   109  	for _, v := range h.versions {
   110  		vs = append(vs, v.Version)
   111  	}
   112  	sort.Sort(versions(vs))
   113  	return vs
   114  }
   115  
   116  type versions []Version
   117  
   118  func (v versions) Len() int           { return len(v) }
   119  func (v versions) Less(i, j int) bool { return v[i] < v[j] }
   120  func (v versions) Swap(i, j int)      { v[i], v[j] = v[j], v[i] }
   121  
   122  var (
   123  	registeredHandlers = map[string]*Handler{}
   124  )
   125  
   126  // HandlerDesc represents a VPP handler's specification.
   127  type HandlerDesc struct {
   128  	Name       string
   129  	HandlerAPI interface{}
   130  	NewFunc    interface{}
   131  }
   132  
   133  // RegisterHandler creates new handler described by handle descriptor.
   134  func RegisterHandler(hd HandlerDesc) *Handler {
   135  	if _, ok := registeredHandlers[hd.Name]; ok {
   136  		panic(fmt.Sprintf("VPP handler %s is already registered", hd.Name))
   137  	}
   138  	h := &Handler{
   139  		desc:     &hd,
   140  		versions: make(map[Version]*HandlerVersion),
   141  	}
   142  	registeredHandlers[hd.Name] = h
   143  	return h
   144  }
   145  
   146  // GetHandlers returns map for all registered handlers.
   147  func GetHandlers() map[string]*Handler {
   148  	return registeredHandlers
   149  }