go.ligato.io/vpp-agent/v3@v3.5.0/plugins/linux/ifplugin/descriptor/interface_vrf.go (about) 1 // Copyright (c) 2020 Pantheon.tech 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 "fmt" 19 20 "github.com/pkg/errors" 21 "go.ligato.io/cn-infra/v2/logging" 22 "google.golang.org/protobuf/proto" 23 24 kvs "go.ligato.io/vpp-agent/v3/plugins/kvscheduler/api" 25 "go.ligato.io/vpp-agent/v3/plugins/linux/ifplugin/descriptor/adapter" 26 "go.ligato.io/vpp-agent/v3/plugins/linux/ifplugin/ifaceidx" 27 iflinuxcalls "go.ligato.io/vpp-agent/v3/plugins/linux/ifplugin/linuxcalls" 28 "go.ligato.io/vpp-agent/v3/plugins/linux/nsplugin" 29 nslinuxcalls "go.ligato.io/vpp-agent/v3/plugins/linux/nsplugin/linuxcalls" 30 interfaces "go.ligato.io/vpp-agent/v3/proto/ligato/linux/interfaces" 31 ) 32 33 const ( 34 // InterfaceVrfDescriptorName is the name of the descriptor for assigning Linux interfaces into a VRF. 35 InterfaceVrfDescriptorName = "linux-interface-vrf" 36 37 // dependency labels 38 vrfDeviceDep = "vrf-device-is-created" 39 externalVrfDep = "inserted-into-vrf-externally" 40 ) 41 42 // InterfaceVrfDescriptor (un)assigns Linux interface to/from VRF. 43 type InterfaceVrfDescriptor struct { 44 log logging.Logger 45 ifHandler iflinuxcalls.NetlinkAPI 46 nsPlugin nsplugin.API 47 intfIndex ifaceidx.LinuxIfMetadataIndex 48 } 49 50 // NewInterfaceVrfDescriptor creates a new instance of InterfaceVrfDescriptor. 51 func NewInterfaceVrfDescriptor(nsPlugin nsplugin.API, 52 ifHandler iflinuxcalls.NetlinkAPI, log logging.PluginLogger) (descr *kvs.KVDescriptor, ctx *InterfaceVrfDescriptor) { 53 54 ctx = &InterfaceVrfDescriptor{ 55 ifHandler: ifHandler, 56 nsPlugin: nsPlugin, 57 log: log.NewLogger("interface-vrf-descriptor"), 58 } 59 typedDescr := &adapter.InterfaceVrfDescriptor{ 60 Name: InterfaceVrfDescriptorName, 61 KeySelector: ctx.IsInterfaceVrfKey, 62 ValueComparator: func(_ string, _, _ *interfaces.Interface) bool { 63 // compare VRF assignments based on keys, not values that contain also other interface attributes 64 // needed by the descriptor 65 // FIXME: we can get rid of this hack once we add Context to descriptor methods 66 return true 67 }, 68 Create: ctx.Create, 69 Delete: ctx.Delete, 70 Dependencies: ctx.Dependencies, 71 } 72 descr = adapter.NewInterfaceVrfDescriptor(typedDescr) 73 return 74 } 75 76 // SetInterfaceIndex should be used to provide interface index immediately after 77 // the descriptor registration. 78 func (d *InterfaceVrfDescriptor) SetInterfaceIndex(intfIndex ifaceidx.LinuxIfMetadataIndex) { 79 d.intfIndex = intfIndex 80 } 81 82 // IsInterfaceVrfKey returns true if the key represents assignment of a Linux interface into a VRF. 83 func (d *InterfaceVrfDescriptor) IsInterfaceVrfKey(key string) bool { 84 _, _, _, isVrfKey := interfaces.ParseInterfaceVrfKey(key) 85 return isVrfKey 86 } 87 88 // Validate validates derived key. 89 func (d *InterfaceVrfDescriptor) Validate(key string, emptyVal proto.Message) (err error) { 90 _, _, invalidKey, _ := interfaces.ParseInterfaceVrfKey(key) 91 if invalidKey { 92 return errors.New("invalid key") 93 } 94 return nil 95 } 96 97 // Create puts interface into a VRF. 98 func (d *InterfaceVrfDescriptor) Create(key string, iface *interfaces.Interface) (metadata interface{}, err error) { 99 ifaceName, vrf, _, _ := interfaces.ParseInterfaceVrfKey(key) 100 ifMeta, found := d.intfIndex.LookupByName(ifaceName) 101 if !found { 102 err = errors.Errorf("failed to find interface %s", iface) 103 d.log.Error(err) 104 return nil, err 105 } 106 vrfMeta, found := d.intfIndex.LookupByName(vrf) 107 if !found { 108 err = errors.Errorf("failed to find VRF device %s", vrf) 109 d.log.Error(err) 110 return nil, err 111 } 112 113 if iface.Type == interfaces.Interface_EXISTING { 114 // Interface is managed externally, including its assignment into the VRF. 115 // While dependencies allow us to require that the interface and the VRF both exist 116 // and that the interface is inside *some* VRF, it is not possible to express requirement 117 // that the actual VRF is the same as the desired one. Therefore we check the condition here 118 // and return error if it is not the case, thus preventing items depending on this 119 // from being created. Once the interface is re-assigned to the proper VRF, this kv will be 120 // re-created with success. 121 ifaceLink, err := d.ifHandler.GetLinkByIndex(ifMeta.LinuxIfIndex) 122 if err != nil { 123 err = fmt.Errorf("failed to obtain interface %s link: %w", iface, err) 124 d.log.Error(err) 125 return nil, err 126 } 127 if ifaceLink.Attrs().MasterIndex != vrfMeta.LinuxIfIndex { 128 err = fmt.Errorf("existing interface %s is not inside VRF %s", iface, vrf) 129 d.log.Error(err) 130 return nil, err 131 } 132 return nil, nil 133 } 134 135 // switch to the namespace with the interface 136 nsCtx := nslinuxcalls.NewNamespaceMgmtCtx() 137 revert, err := d.nsPlugin.SwitchToNamespace(nsCtx, ifMeta.Namespace) 138 if err != nil { 139 d.log.Error(err) 140 return nil, err 141 } 142 defer revert() 143 144 err = d.ifHandler.PutInterfaceIntoVRF(ifMeta.HostIfName, vrfMeta.HostIfName) 145 if err != nil { 146 err = errors.WithMessagef(err, "failed to put interface '%s' into VRF '%s'", 147 ifMeta.HostIfName, vrfMeta.HostIfName) 148 } 149 return nil, err 150 } 151 152 // Delete removes interface from VRF. 153 func (d *InterfaceVrfDescriptor) Delete(key string, iface *interfaces.Interface, metadata interface{}) (err error) { 154 ifaceName, vrf, _, _ := interfaces.ParseInterfaceVrfKey(key) 155 if iface.Type == interfaces.Interface_EXISTING { 156 // interface is managed externally, nothing to do here 157 return nil 158 } 159 160 ifMeta, found := d.intfIndex.LookupByName(ifaceName) 161 if !found { 162 err = errors.Errorf("failed to find interface %s", iface) 163 d.log.Error(err) 164 return err 165 } 166 vrfMeta, found := d.intfIndex.LookupByName(vrf) 167 if !found { 168 err = errors.Errorf("failed to find VRF device %s", vrf) 169 d.log.Error(err) 170 return err 171 } 172 173 // switch to the namespace with the interface 174 nsCtx := nslinuxcalls.NewNamespaceMgmtCtx() 175 revert, err := d.nsPlugin.SwitchToNamespace(nsCtx, ifMeta.Namespace) 176 if err != nil { 177 if _, ok := err.(*nsplugin.UnavailableMicroserviceErr); ok { 178 // Assume that the delete was called by scheduler because the namespace 179 // was removed. Do not return error in this case. 180 d.log.Debugf("Interface %s assumed to be unassigned from VRF %s, required namespace %+v does not exist", 181 iface, vrf, ifMeta.Namespace) 182 return nil 183 } 184 d.log.Error(err) 185 return err 186 } 187 defer revert() 188 189 err = d.ifHandler.RemoveInterfaceFromVRF(ifMeta.HostIfName, vrfMeta.HostIfName) 190 if err != nil { 191 err = errors.WithMessagef(err, "failed to remove interface '%s' from VRF '%s'", 192 ifMeta.HostIfName, vrfMeta.HostIfName) 193 } 194 return err 195 } 196 197 // Dependencies lists the VRF device as the only dependency. 198 func (d *InterfaceVrfDescriptor) Dependencies(key string, iface *interfaces.Interface) (deps []kvs.Dependency) { 199 _, vrf, _, _ := interfaces.ParseInterfaceVrfKey(key) 200 if vrf != "" { 201 deps = append(deps, kvs.Dependency{ 202 Label: vrfDeviceDep, 203 Key: interfaces.InterfaceKey(vrf), 204 }) 205 } 206 if iface.Type == interfaces.Interface_EXISTING { 207 // Interface is added into the VRF externally. 208 // KV dependencies do not allow us to fully express this dependency - we can express requirement 209 // that the interface is inside *some* VRF, but it not may be the desired one. This is because 210 // the VRF host name is not yet known at this point and therefore it is not possible to build the 211 // key to depend on. 212 // Verification that the desired and actual VRFs are the same is therefore done in the Create method 213 // and error is returned if it is not the case. 214 deps = append(deps, kvs.Dependency{ 215 Label: externalVrfDep, 216 AnyOf: kvs.AnyOfDependency{ 217 KeyPrefixes: []string{ 218 interfaces.InterfaceHostNameWithVrfKey(getHostIfName(iface), ""), 219 }, 220 }, 221 }) 222 } 223 return deps 224 }