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 }