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 }