go.ligato.io/vpp-agent/v3@v3.5.0/examples/kvscheduler/mock_plugins/ifplugin/descriptor/interface.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/idxmap" 23 "go.ligato.io/cn-infra/v2/logging" 24 25 "go.ligato.io/vpp-agent/v3/examples/kvscheduler/mock_plugins/ifplugin/descriptor/adapter" 26 "go.ligato.io/vpp-agent/v3/examples/kvscheduler/mock_plugins/ifplugin/mockcalls" 27 interfaces "go.ligato.io/vpp-agent/v3/examples/kvscheduler/mock_plugins/ifplugin/model" 28 "go.ligato.io/vpp-agent/v3/pkg/idxvpp" 29 "go.ligato.io/vpp-agent/v3/pkg/models" 30 kvs "go.ligato.io/vpp-agent/v3/plugins/kvscheduler/api" 31 ) 32 33 const ( 34 // InterfaceDescriptorName is the name of the descriptor for mock interfaces. 35 InterfaceDescriptorName = "mock-interface" 36 37 // how many characters interface name is allowed to have 38 // (defined just to showcase Validation) 39 nameLengthLimit = 15 40 ) 41 42 // Example of some validation errors: 43 var ( 44 // ErrInterfaceWithoutName is returned when interface configuration has 45 // undefined Name attribute. 46 ErrInterfaceWithoutName = errors.New("mock interface defined without logical name") 47 48 // ErrInterfaceNameTooLong is returned when mock interface name exceeds the length limit. 49 ErrInterfaceNameTooLong = errors.New("mock interface logical name exceeds the length limit (15 characters)") 50 51 // ErrInterfaceWithoutType is returned when mock interface configuration has undefined 52 // Type attribute. 53 ErrInterfaceWithoutType = errors.New("mock interface defined without type") 54 ) 55 56 // InterfaceDescriptor teaches KVScheduler how to configure mock interfaces. 57 type InterfaceDescriptor struct { 58 59 // dependencies 60 log logging.Logger 61 ifaceHandler mockcalls.MockIfaceAPI 62 } 63 64 // NewInterfaceDescriptor creates a new instance of the Interface descriptor. 65 func NewInterfaceDescriptor(ifaceHandler mockcalls.MockIfaceAPI, log logging.PluginLogger) *kvs.KVDescriptor { 66 // descriptors are supposed to be stateless and this principle is not broken 67 // here - we only need to keep context consisting of references to logger 68 // and the interface handler for mock SB, to be used inside the CRUD methods. 69 descrCtx := &InterfaceDescriptor{ 70 ifaceHandler: ifaceHandler, 71 log: log.NewLogger("mock-iface-descriptor"), 72 } 73 74 // use adapter to convert typed descriptor into generic descriptor API 75 typedDescr := &adapter.InterfaceDescriptor{ 76 Name: InterfaceDescriptorName, 77 NBKeyPrefix: interfaces.ModelInterface.KeyPrefix(), 78 ValueTypeName: interfaces.ModelInterface.ProtoName(), 79 KeySelector: interfaces.ModelInterface.IsKeyValid, 80 KeyLabel: interfaces.ModelInterface.StripKeyPrefix, 81 ValueComparator: descrCtx.EquivalentInterfaces, 82 WithMetadata: true, 83 MetadataMapFactory: descrCtx.MetadataFactory, 84 Validate: descrCtx.Validate, 85 Create: descrCtx.Create, 86 Delete: descrCtx.Delete, 87 Update: descrCtx.Update, 88 UpdateWithRecreate: descrCtx.UpdateWithRecreate, 89 Retrieve: descrCtx.Retrieve, 90 } 91 return adapter.NewInterfaceDescriptor(typedDescr) 92 } 93 94 // EquivalentInterfaces is case-insensitive comparison function for 95 // interfaces.Interface, also ignoring the order of assigned IP addresses. 96 func (d *InterfaceDescriptor) EquivalentInterfaces(key string, oldIntf, newIntf *interfaces.Interface) bool { 97 // attributes compared as usually: 98 if oldIntf.Name != newIntf.Name || 99 oldIntf.Type != newIntf.Type || 100 oldIntf.Enabled != newIntf.Enabled { 101 return false 102 } 103 104 // compare MAC addresses case-insensitively (also handle unspecified MAC address) 105 if newIntf.PhysAddress != "" && 106 strings.ToLower(oldIntf.PhysAddress) != strings.ToLower(newIntf.PhysAddress) { 107 return false 108 } 109 110 return true 111 } 112 113 // MetadataFactory is a factory for index-map customized for VPP interfaces. 114 func (d *InterfaceDescriptor) MetadataFactory() idxmap.NamedMappingRW { 115 return idxvpp.NewNameToIndex(d.log, "mock-iface-index", nil) 116 } 117 118 // Validate validates VPP interface configuration. 119 func (d *InterfaceDescriptor) Validate(key string, intf *interfaces.Interface) error { 120 // validate name 121 if name := intf.GetName(); name == "" { 122 return kvs.NewInvalidValueError(ErrInterfaceWithoutName, "name") 123 } else if len(name) > nameLengthLimit { 124 return kvs.NewInvalidValueError(ErrInterfaceNameTooLong, "name") 125 } 126 127 // validate type 128 if intf.Type == interfaces.Interface_UNDEFINED_TYPE { 129 return kvs.NewInvalidValueError(ErrInterfaceWithoutType, "type") 130 } 131 132 // validate MAC address 133 hwAddr := intf.GetPhysAddress() 134 if hwAddr != "" { 135 _, err := net.ParseMAC(hwAddr) 136 if err != nil { 137 return kvs.NewInvalidValueError(err, "phys_address") 138 } 139 } 140 141 return nil 142 } 143 144 // Create creates mock interface. 145 func (d *InterfaceDescriptor) Create(key string, value *interfaces.Interface) (metadata *idxvpp.OnlyIndex, err error) { 146 // create interface of the given type and with the given name 147 var sbIfaceHandle uint32 148 if value.Type == interfaces.Interface_LOOPBACK { 149 sbIfaceHandle, err = d.ifaceHandler.CreateLoopbackInterface(value.Name) 150 } else { 151 sbIfaceHandle, err = d.ifaceHandler.CreateTapInterface(value.Name) 152 } 153 if err != nil { 154 return nil, err 155 } 156 metadata = &idxvpp.OnlyIndex{Index: sbIfaceHandle} 157 158 // set interface UP if requested 159 if value.Enabled { 160 err = d.ifaceHandler.InterfaceAdminUp(sbIfaceHandle) 161 if err != nil { 162 return nil, err 163 } 164 } 165 166 // set interface MAC address if requested 167 if value.PhysAddress != "" { 168 err = d.ifaceHandler.SetInterfaceMac(sbIfaceHandle, value.PhysAddress) 169 if err != nil { 170 return nil, err 171 } 172 } 173 174 return metadata, nil 175 } 176 177 // Delete removes a mock interface. 178 func (d *InterfaceDescriptor) Delete(key string, value *interfaces.Interface, metadata *idxvpp.OnlyIndex) error { 179 var err error 180 if value.Type == interfaces.Interface_LOOPBACK { 181 err = d.ifaceHandler.DeleteLoopbackInterface(metadata.Index) 182 } else { 183 err = d.ifaceHandler.DeleteTapInterface(metadata.Index) 184 } 185 return err 186 } 187 188 // Update updates parameters of a mock interface. 189 func (d *InterfaceDescriptor) Update(key string, oldValue, newValue *interfaces.Interface, oldMetadata *idxvpp.OnlyIndex) (newMetadata *idxvpp.OnlyIndex, err error) { 190 // no need to handle change of the Name or Type: 191 // - different Name implies different key, i.e. completely different interface 192 // - UpdateWithRecreate specifies that change of Type requires full interface re-creation 193 194 // update of the admin status 195 if oldValue.Enabled != newValue.Enabled { 196 if newValue.Enabled { 197 err = d.ifaceHandler.InterfaceAdminUp(oldMetadata.Index) 198 } else { 199 err = d.ifaceHandler.InterfaceAdminDown(oldMetadata.Index) 200 } 201 if err != nil { 202 return nil, err 203 } 204 } 205 206 // update of the MAC address 207 if oldValue.PhysAddress != newValue.PhysAddress && newValue.PhysAddress != "" { 208 err = d.ifaceHandler.SetInterfaceMac(oldMetadata.Index, newValue.PhysAddress) 209 if err != nil { 210 return nil, err 211 } 212 } 213 214 return oldMetadata, nil // metadata (sbIfaceIndex) has not changed 215 } 216 217 // UpdateWithRecreate returns true if Type is requested to be changed. 218 func (d *InterfaceDescriptor) UpdateWithRecreate(key string, oldIntf, newIntf *interfaces.Interface, metadata *idxvpp.OnlyIndex) bool { 219 if oldIntf.Type != newIntf.Type { 220 return true 221 } 222 return false 223 } 224 225 // Retrieve returns all interfaces configured in the mock SB. 226 func (d *InterfaceDescriptor) Retrieve(correlate []adapter.InterfaceKVWithMetadata) (retrieved []adapter.InterfaceKVWithMetadata, err error) { 227 ifaces, err := d.ifaceHandler.DumpInterfaces() 228 if err != nil { 229 return nil, err 230 } 231 232 for sbIfaceHandle, iface := range ifaces { 233 retrieved = append(retrieved, adapter.InterfaceKVWithMetadata{ 234 Key: models.Key(iface), 235 Value: iface, 236 Metadata: &idxvpp.OnlyIndex{Index: sbIfaceHandle}, 237 Origin: kvs.FromNB, // not considering OBTAINED interfaces in our simplified example 238 }) 239 } 240 return retrieved, nil 241 }