go.ligato.io/vpp-agent/v3@v3.5.0/plugins/vpp/ifplugin/publish_state.go (about)

     1  package ifplugin
     2  
     3  import (
     4  	"strings"
     5  
     6  	"github.com/pkg/errors"
     7  	"go.ligato.io/cn-infra/v2/datasync"
     8  	"go.ligato.io/cn-infra/v2/health/statuscheck"
     9  	"go.ligato.io/cn-infra/v2/health/statuscheck/model/status"
    10  
    11  	"go.ligato.io/vpp-agent/v3/proto/ligato/vpp"
    12  	interfaces "go.ligato.io/vpp-agent/v3/proto/ligato/vpp/interfaces"
    13  )
    14  
    15  // watchStatusEvents watches for resync event of interface state data.
    16  func (p *IfPlugin) watchStatusEvents() {
    17  	defer p.wg.Done()
    18  	p.Log.Debug("Start watching interface state events")
    19  
    20  	for {
    21  		select {
    22  		case e := <-p.resyncStatusChan:
    23  			p.onStatusResyncEvent(e)
    24  
    25  		case <-p.ctx.Done():
    26  			p.Log.Debug("Stop watching interface state events")
    27  			return
    28  		}
    29  	}
    30  }
    31  
    32  // onStatusResyncEvent is triggered during resync of interface state data
    33  func (p *IfPlugin) onStatusResyncEvent(e datasync.ResyncEvent) {
    34  	p.Log.Debugf("received status resync event (%d prefixes)", len(e.GetValues()))
    35  
    36  	var wasError error
    37  	for prefix, vals := range e.GetValues() {
    38  		var keys []string
    39  		for {
    40  			x, stop := vals.GetNext()
    41  			if stop {
    42  				break
    43  			}
    44  			keys = append(keys, x.GetKey())
    45  		}
    46  		if len(keys) > 0 {
    47  			p.Log.Debugf("- %q (%v items)", prefix, len(keys))
    48  			err := p.resyncIfStateEvents(keys)
    49  			if err != nil {
    50  				wasError = err
    51  			}
    52  		} else {
    53  			p.Log.Debugf("- %q (no items)", prefix)
    54  		}
    55  	}
    56  	e.Done(wasError)
    57  }
    58  
    59  // resyncIfStateEvents deletes obsolete operation status of network interfaces in DB.
    60  func (p *IfPlugin) resyncIfStateEvents(keys []string) error {
    61  	p.publishLock.Lock()
    62  	defer p.publishLock.Unlock()
    63  
    64  	p.Log.Debugf("resync interface state events with %d keys", len(keys))
    65  
    66  	for _, key := range keys {
    67  		ifaceName := strings.TrimPrefix(key, interfaces.StatePrefix)
    68  		if ifaceName == key {
    69  			continue
    70  		}
    71  
    72  		_, found := p.intfIndex.LookupByName(ifaceName)
    73  		if !found {
    74  			err := p.PublishStatistics.Put(key, nil /*means delete*/)
    75  			if err != nil {
    76  				return errors.WithMessagef(err, "publish statistic for key %s failed", key)
    77  			}
    78  			p.Log.Debugf("Obsolete interface status for %v deleted", key)
    79  		} else {
    80  			p.Log.WithField("ifaceName", ifaceName).Debug("interface status is needed")
    81  		}
    82  	}
    83  
    84  	return nil
    85  }
    86  
    87  // publishIfStateEvents goroutine is used to watch interface state notifications
    88  // that are propagated to Messaging topic.
    89  func (p *IfPlugin) publishIfStateEvents() {
    90  	defer p.wg.Done()
    91  
    92  	// store last errors to prevent repeating
    93  	var lastPublishErr error
    94  	var lastNotifErr error
    95  
    96  	for {
    97  		select {
    98  		case ifState := <-p.ifStateChan:
    99  			p.publishLock.Lock()
   100  			key := interfaces.InterfaceStateKey(ifState.State.Name)
   101  
   102  			if debugIfStates {
   103  				p.Log.Debugf("Publishing interface state: %+v", ifState)
   104  			}
   105  
   106  			if p.PublishStatistics != nil {
   107  				err := p.PublishStatistics.Put(key, ifState.State)
   108  				if err != nil {
   109  					if lastPublishErr == nil || lastPublishErr.Error() != err.Error() {
   110  						p.Log.Error(err)
   111  					}
   112  				}
   113  				lastPublishErr = err
   114  			}
   115  
   116  			// Note: state change is sometimes delivered as an unknown notification
   117  			stateChange := ifState.Type == interfaces.InterfaceNotification_UPDOWN ||
   118  				ifState.Type == interfaces.InterfaceNotification_UNKNOWN
   119  
   120  			// Marshall data into JSON & send kafka message.
   121  			if p.NotifyStates != nil && stateChange {
   122  				err := p.NotifyStates.Put(key, ifState.State)
   123  				if err != nil {
   124  					if lastNotifErr == nil || lastNotifErr.Error() != err.Error() {
   125  						p.Log.Error(err)
   126  					}
   127  				}
   128  				lastNotifErr = err
   129  			}
   130  
   131  			// Send interface state data to global agent status
   132  			if p.statusCheckReg && ifState.State.InternalName != "" {
   133  				p.StatusCheck.ReportStateChangeWithMeta(p.PluginName, statuscheck.OK, nil, &status.InterfaceStats_Interface{
   134  					InternalName: ifState.State.InternalName,
   135  					Index:        ifState.State.IfIndex,
   136  					Status:       ifState.State.AdminStatus.String(),
   137  					MacAddress:   ifState.State.PhysAddress,
   138  				})
   139  			}
   140  
   141  			if stateChange || ifState.State.OperStatus == interfaces.InterfaceState_DELETED {
   142  				if debugIfStates {
   143  					p.Log.Debugf("Updating link state: %+v", ifState)
   144  				}
   145  				p.linkStateDescriptor.UpdateLinkState(ifState)
   146  				if p.PushNotification != nil {
   147  					p.PushNotification(&vpp.Notification{
   148  						Interface: ifState,
   149  					})
   150  				}
   151  			}
   152  
   153  			p.publishLock.Unlock()
   154  
   155  		case <-p.ctx.Done():
   156  			// Stop watching for state data updates.
   157  			return
   158  		}
   159  	}
   160  }