go.ligato.io/vpp-agent/v3@v3.5.0/examples/kvscheduler/mock_plugins/l2plugin/descriptor/fib.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/logging" 23 24 ifdescriptor "go.ligato.io/vpp-agent/v3/examples/kvscheduler/mock_plugins/ifplugin/descriptor" 25 "go.ligato.io/vpp-agent/v3/examples/kvscheduler/mock_plugins/l2plugin/descriptor/adapter" 26 "go.ligato.io/vpp-agent/v3/examples/kvscheduler/mock_plugins/l2plugin/mockcalls" 27 l2 "go.ligato.io/vpp-agent/v3/examples/kvscheduler/mock_plugins/l2plugin/model" 28 "go.ligato.io/vpp-agent/v3/pkg/models" 29 kvs "go.ligato.io/vpp-agent/v3/plugins/kvscheduler/api" 30 ) 31 32 const ( 33 // FIBDescriptorName is the name of the descriptor for L2 FIBs in the mock SB. 34 FIBDescriptorName = "mock-l2-fib" 35 36 // dependency labels 37 bridgedInterfaceDep = "bridged-interface" 38 bridgeDomainDep = "bridge-domain" 39 ) 40 41 // Example of some validation errors: 42 var ( 43 // ErrFIBWithoutHwAddr is returned when mock L2 FIB has undefined hardware 44 // address. 45 ErrFIBWithoutHwAddr = errors.New("mock L2 FIB defined without hardware address") 46 47 // ErrFIBWithoutBD is returned when mock L2 FIB has undefined bridge domain. 48 ErrFIBWithoutBD = errors.New("mock L2 FIB defined without bridge domain") 49 50 // ErrForwardFIBWithoutInterface is returned when mock L2 FORWARD FIB has undefined outgoing interface. 51 ErrForwardFIBWithoutInterface = errors.New("mock L2 FORWARD FIB defined without outgoing interface") 52 ) 53 54 // FIBDescriptor teaches KVScheduler how to configure L2 FIBs in the mock SB. 55 type FIBDescriptor struct { 56 // dependencies 57 log logging.Logger 58 fibHandler mockcalls.MockFIBAPI 59 } 60 61 // NewFIBDescriptor creates a new instance of the FIB descriptor. 62 func NewFIBDescriptor(fibHandler mockcalls.MockFIBAPI, log logging.PluginLogger) *kvs.KVDescriptor { 63 // descriptors are supposed to be stateless and this principle is not broken 64 // here - we only need to keep context consisting of references to logger 65 // and the FIB handler for mock SB, to be used inside the CRUD methods. 66 descrCtx := &FIBDescriptor{ 67 fibHandler: fibHandler, 68 log: log.NewLogger("mock-l2-fib-descriptor"), 69 } 70 71 // use adapter to convert typed descriptor into generic descriptor API 72 typedDescr := &adapter.FIBDescriptor{ 73 Name: FIBDescriptorName, 74 NBKeyPrefix: l2.ModelFIBEntry.KeyPrefix(), 75 ValueTypeName: l2.ModelFIBEntry.ProtoName(), 76 KeySelector: l2.ModelFIBEntry.IsKeyValid, 77 KeyLabel: l2.ModelFIBEntry.StripKeyPrefix, 78 ValueComparator: descrCtx.EquivalentFIBs, 79 Validate: descrCtx.Validate, 80 Create: descrCtx.Create, 81 Delete: descrCtx.Delete, 82 Retrieve: descrCtx.Retrieve, 83 Dependencies: descrCtx.Dependencies, 84 85 // Note: Update operation is not defined, which will cause any change 86 // in the FIB configuration to be applied via full re-creation 87 // (Delete for the obsolete config, followed by Create for the new 88 // config). 89 90 // Note: L2 FIBs do not need any metadata in our example with mock SB. 91 92 // Retrieve interfaces and bridge domain first to have the indexes with 93 // interface and BD metadata up-to-date when Retrieve for FIBs is called, 94 // which then uses the index to translate interface and BD names to the 95 // corresponding integer handles used in the mock SB. 96 97 RetrieveDependencies: []string{ifdescriptor.InterfaceDescriptorName, BridgeDomainDescriptorName}, 98 } 99 return adapter.NewFIBDescriptor(typedDescr) 100 } 101 102 // EquivalentFIBs is case-insensitive comparison function for l2.FIBEntry. 103 func (d *FIBDescriptor) EquivalentFIBs(key string, oldFIB, newFIB *l2.FIBEntry) bool { 104 // parameters compared as usually 105 if oldFIB.Action != newFIB.Action || oldFIB.BridgeDomain != newFIB.BridgeDomain { 106 return false 107 } 108 109 // outgoing interface is relevant only for FORWARD FIBs 110 if oldFIB.Action == l2.FIBEntry_FORWARD { 111 if oldFIB.OutgoingInterface != newFIB.OutgoingInterface { 112 return false 113 } 114 } 115 116 // MAC addresses compared case-insensitively 117 return strings.ToLower(oldFIB.PhysAddress) == strings.ToLower(newFIB.PhysAddress) 118 } 119 120 // Validate validates mock L2 FIB configuration. 121 func (d *FIBDescriptor) Validate(key string, fib *l2.FIBEntry) error { 122 // validate MAC address 123 if fib.PhysAddress == "" { 124 return kvs.NewInvalidValueError(ErrFIBWithoutHwAddr, "phys_address") 125 } 126 _, err := net.ParseMAC(fib.PhysAddress) 127 if err != nil { 128 return kvs.NewInvalidValueError(err, "phys_address") 129 } 130 131 // validate outgoing interface reference 132 if fib.Action == l2.FIBEntry_FORWARD && fib.OutgoingInterface == "" { 133 return kvs.NewInvalidValueError(ErrForwardFIBWithoutInterface, "action", "outgoing_interface") 134 } 135 136 // validate bridge domain reference 137 if fib.BridgeDomain == "" { 138 return kvs.NewInvalidValueError(ErrFIBWithoutBD, "bridge_domain") 139 } 140 return nil 141 } 142 143 // Create adds new L2 FIB. 144 func (d *FIBDescriptor) Create(key string, fib *l2.FIBEntry) (metadata interface{}, err error) { 145 // add L2 FIB 146 err = d.fibHandler.CreateL2FIB(fib) 147 if err != nil { 148 d.log.Error(err) 149 } 150 return nil, err 151 } 152 153 // Delete removes VPP L2 FIB. 154 func (d *FIBDescriptor) Delete(key string, fib *l2.FIBEntry, metadata interface{}) error { 155 err := d.fibHandler.DeleteL2FIB(fib) 156 if err != nil { 157 d.log.Error(err) 158 } 159 return err 160 } 161 162 // Retrieve returns all configured VPP L2 FIBs. 163 func (d *FIBDescriptor) Retrieve(correlate []adapter.FIBKVWithMetadata) (retrieved []adapter.FIBKVWithMetadata, err error) { 164 fibs, err := d.fibHandler.DumpL2FIBs() 165 if err != nil { 166 return nil, err 167 } 168 169 for _, fib := range fibs { 170 retrieved = append(retrieved, adapter.FIBKVWithMetadata{ 171 Key: models.Key(fib), 172 Value: fib, 173 Origin: kvs.FromNB, // not considering OBTAINED FIBs in our simplified example 174 }) 175 } 176 return retrieved, nil 177 } 178 179 // Dependencies for FIBs are: 180 // * FORWARD FIB: bridge domain + outgoing interface already put into the bridge domain 181 // * DROP FIB: bridge domain 182 func (d *FIBDescriptor) Dependencies(key string, fib *l2.FIBEntry) (dependencies []kvs.Dependency) { 183 if fib.Action == l2.FIBEntry_FORWARD { 184 // example of a dependency on a derived value 185 dependencies = append(dependencies, kvs.Dependency{ 186 Label: bridgedInterfaceDep, 187 Key: l2.BDInterfaceKey(fib.BridgeDomain, fib.OutgoingInterface), 188 }) 189 } else { 190 dependencies = append(dependencies, kvs.Dependency{ 191 Label: bridgeDomainDep, 192 Key: l2.BridgeDomainKey(fib.BridgeDomain), 193 }) 194 } 195 return dependencies 196 }