go.ligato.io/vpp-agent/v3@v3.5.0/plugins/vpp/ifplugin/descriptor/link_state.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 "sync" 19 20 "go.ligato.io/cn-infra/v2/logging" 21 "google.golang.org/protobuf/types/known/emptypb" 22 23 kvs "go.ligato.io/vpp-agent/v3/plugins/kvscheduler/api" 24 "go.ligato.io/vpp-agent/v3/plugins/vpp/ifplugin/ifaceidx" 25 "go.ligato.io/vpp-agent/v3/plugins/vpp/ifplugin/vppcalls" 26 interfaces "go.ligato.io/vpp-agent/v3/proto/ligato/vpp/interfaces" 27 ) 28 29 const ( 30 // LinkStateDescriptorName is the name of the descriptor notifying about the 31 // link state changes of VPP interfaces. 32 LinkStateDescriptorName = "vpp-interface-link-state" 33 ) 34 35 // LinkStateDescriptor notifies kvscheduler about the link state changes of VPP 36 // interfaces. 37 type LinkStateDescriptor struct { 38 // input arguments 39 log logging.Logger 40 kvscheduler kvs.KVScheduler 41 ifaceHandler vppcalls.InterfaceVppAPI 42 ifaceIdx ifaceidx.IfaceMetadataIndex 43 44 linkStatesMx sync.Mutex 45 linkStates map[string]bool // interface name -> link is up 46 } 47 48 // NewLinkStateDescriptor creates a new instance of the Link-State descriptor. 49 func NewLinkStateDescriptor(kvscheduler kvs.KVScheduler, ifaceHandler vppcalls.InterfaceVppAPI, 50 ifaceIdx ifaceidx.IfaceMetadataIndex, log logging.PluginLogger) (descr *kvs.KVDescriptor, ctx *LinkStateDescriptor) { 51 52 descrCtx := &LinkStateDescriptor{ 53 log: log.NewLogger("interface-link-state"), 54 kvscheduler: kvscheduler, 55 ifaceHandler: ifaceHandler, 56 ifaceIdx: ifaceIdx, 57 linkStates: make(map[string]bool), 58 } 59 return &kvs.KVDescriptor{ 60 Name: LinkStateDescriptorName, 61 KeySelector: descrCtx.IsInterfaceLinkStateKey, 62 Retrieve: descrCtx.Retrieve, 63 // Retrieve depends on the interface descriptor: interface index is used 64 // to convert sw_if_index to logical interface name 65 RetrieveDependencies: []string{InterfaceDescriptorName}, 66 }, descrCtx 67 } 68 69 // IsInterfaceLinkStateKey returns <true> for keys representing 70 // link-state of VPP interfaces. 71 func (w *LinkStateDescriptor) IsInterfaceLinkStateKey(key string) bool { 72 _, _, isLinkStateKey := interfaces.ParseLinkStateKey(key) 73 return isLinkStateKey 74 } 75 76 // Retrieve returns key for every VPP interface describing the state of the link 77 // (value is empty). 78 func (w *LinkStateDescriptor) Retrieve(correlate []kvs.KVWithMetadata) (values []kvs.KVWithMetadata, err error) { 79 // TODO: avoid dumping interface details when it was already done in the interface 80 // descriptor within the same Refresh (e.g. during full resync) 81 // - e.g. add context to allow sharing of information across Retrieve(s) of the same Refresh 82 83 ifaceStates, err := w.ifaceHandler.DumpInterfaceStates() 84 if err != nil { 85 w.log.Error(err) 86 return nil, err 87 } 88 89 w.linkStatesMx.Lock() 90 defer w.linkStatesMx.Unlock() 91 w.linkStates = make(map[string]bool) // clear the map 92 93 for ifaceIdx, ifaceState := range ifaceStates { 94 ifaceName, _, found := w.ifaceIdx.LookupBySwIfIndex(ifaceIdx) 95 if !found { 96 // skip interface not configured by NB (e.g. untouched Gbe interface) 97 continue 98 } 99 linkIsUp := ifaceState.LinkState == interfaces.InterfaceState_UP 100 w.linkStates[ifaceName] = linkIsUp 101 values = append(values, kvs.KVWithMetadata{ 102 Key: interfaces.LinkStateKey(ifaceName, linkIsUp), 103 Value: &emptypb.Empty{}, 104 Origin: kvs.FromSB, 105 }) 106 } 107 108 return values, nil 109 } 110 111 // UpdateLinkState notifies scheduler about a change in the link state of an interface. 112 func (w *LinkStateDescriptor) UpdateLinkState(ifaceState *interfaces.InterfaceNotification) { 113 w.linkStatesMx.Lock() 114 defer w.linkStatesMx.Unlock() 115 116 var notifs []kvs.KVWithMetadata 117 118 operStatus := ifaceState.State.OperStatus 119 ifaceName := ifaceState.State.Name 120 linkWasUp, hadLinkState := w.linkStates[ifaceName] 121 linkIsUp := operStatus == interfaces.InterfaceState_UP 122 toDelete := operStatus == interfaces.InterfaceState_DELETED || 123 operStatus == interfaces.InterfaceState_UNKNOWN_STATUS 124 125 if toDelete || (hadLinkState && (linkIsUp != linkWasUp)) { 126 if hadLinkState { 127 // remove now obsolete key-value pair 128 notifs = append(notifs, kvs.KVWithMetadata{ 129 Key: interfaces.LinkStateKey(ifaceState.State.Name, linkWasUp), 130 Value: nil, 131 Metadata: nil, 132 }) 133 delete(w.linkStates, ifaceName) 134 } 135 } 136 137 if !toDelete && (!hadLinkState || (linkIsUp != linkWasUp)) { 138 // push new key-value pair 139 notifs = append(notifs, kvs.KVWithMetadata{ 140 Key: interfaces.LinkStateKey(ifaceState.State.Name, linkIsUp), 141 Value: &emptypb.Empty{}, 142 Metadata: nil, 143 }) 144 w.linkStates[ifaceName] = linkIsUp 145 } 146 147 if len(notifs) != 0 { 148 err := w.kvscheduler.PushSBNotification(notifs...) 149 if err != nil { 150 w.log.Errorf("failed to send notifications to KVScheduler: %v", err) 151 } 152 } 153 }