go.ligato.io/vpp-agent/v3@v3.5.0/pkg/idxvpp/idxvpp.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 idxvpp
    16  
    17  import (
    18  	"strconv"
    19  	"time"
    20  
    21  	"go.ligato.io/cn-infra/v2/idxmap"
    22  	"go.ligato.io/cn-infra/v2/idxmap/mem"
    23  	"go.ligato.io/cn-infra/v2/logging"
    24  )
    25  
    26  var DefaultNotifTimeout = time.Millisecond * 100
    27  
    28  // WithIndex is interface that items with integer handle must implement to get
    29  // indexed by NameToIndex.
    30  type WithIndex interface {
    31  	// GetIndex should return integer handle assigned to the item.
    32  	GetIndex() uint32
    33  }
    34  
    35  // NameToIndex is the "user API" to the registry of items with integer handles.
    36  // It provides read-only access intended for plugins that need to do the conversions
    37  // between logical names from NB and VPP/Linux item IDs.
    38  type NameToIndex interface {
    39  	// LookupByName retrieves a previously stored item identified by
    40  	// <name>. If there is no item associated with the give name in the mapping,
    41  	// the <exists> is returned as *false* and <item> as *nil*.
    42  	LookupByName(name string) (item WithIndex, exists bool)
    43  
    44  	// LookupByIndex retrieves a previously stored item identified in VPP/Linux
    45  	// by the given <index>.
    46  	// If there is no item associated with the given index, <exists> is returned
    47  	// as *false* with <name> and <item> both set to empty values.
    48  	LookupByIndex(index uint32) (name string, item WithIndex, exists bool)
    49  
    50  	// WatchItems subscribes to receive notifications about the changes in the
    51  	// mapping related to items with integer handles.
    52  	WatchItems(subscriber string, channel chan<- NameToIndexDto)
    53  }
    54  
    55  // NameToIndexRW is the "owner API" to the NameToIndex registry. Using this
    56  // API the owner is able to add/update and delete associations between logical
    57  // names and VPP/Linux items identified by integer handles.
    58  type NameToIndexRW interface {
    59  	NameToIndex
    60  	idxmap.NamedMappingRW
    61  }
    62  
    63  // OnlyIndex can be used to add items into NameToIndex with the integer handle
    64  // as the only information associated with each item.
    65  type OnlyIndex struct {
    66  	Index uint32
    67  }
    68  
    69  // GetIndex returns index assigned to the item.
    70  func (item *OnlyIndex) GetIndex() uint32 {
    71  	return item.Index
    72  }
    73  
    74  // NameToIndexDto represents an item sent through watch channel in NameToIndex.
    75  // In contrast to NamedMappingGenericEvent, it contains item casted to WithIndex.
    76  type NameToIndexDto struct {
    77  	idxmap.NamedMappingEvent
    78  	Item WithIndex
    79  }
    80  
    81  // nameToIndex implements NamedMapping for items with integer handles.
    82  type nameToIndex struct {
    83  	idxmap.NamedMappingRW
    84  	log logging.Logger
    85  }
    86  
    87  const (
    88  	// indexKey is a secondary index used to create association between
    89  	// item name and the integer handle.
    90  	indexKey = "index"
    91  )
    92  
    93  // NewNameToIndex creates a new instance implementing NameToIndexRW.
    94  // User can optionally extend the secondary indexes through <indexFunction>.
    95  func NewNameToIndex(logger logging.Logger, title string,
    96  	indexFunction mem.IndexFunction) NameToIndexRW {
    97  	return &nameToIndex{
    98  		NamedMappingRW: mem.NewNamedMapping(logger, title,
    99  			func(item interface{}) map[string][]string {
   100  				idxs := internalIndexFunction(item)
   101  
   102  				if indexFunction != nil {
   103  					userIdxs := indexFunction(item)
   104  					for k, v := range userIdxs {
   105  						idxs[k] = v
   106  					}
   107  				}
   108  				return idxs
   109  			}),
   110  	}
   111  }
   112  
   113  // LookupByName retrieves a previously stored item identified by
   114  // <name>. If there is no item associated with the give name in the mapping,
   115  // the <exists> is returned as *false* and <item> as *nil*.
   116  func (idx *nameToIndex) LookupByName(name string) (item WithIndex, exists bool) {
   117  	value, found := idx.GetValue(name)
   118  	if found {
   119  		if itemWithIndex, ok := value.(WithIndex); ok {
   120  			return itemWithIndex, found
   121  		}
   122  	}
   123  	return nil, false
   124  }
   125  
   126  // LookupByIndex retrieves a previously stored item identified in VPP/Linux
   127  // by the given <index>.
   128  // If there is no item associated with the given index, <exists> is returned
   129  // as *false* with <name> and <item> both set to empty values.
   130  func (idx *nameToIndex) LookupByIndex(index uint32) (name string, item WithIndex, exists bool) {
   131  	res := idx.ListNames(indexKey, strconv.FormatUint(uint64(index), 10))
   132  	if len(res) != 1 {
   133  		return
   134  	}
   135  	value, found := idx.GetValue(res[0])
   136  	if found {
   137  		if itemWithIndex, ok := value.(WithIndex); ok {
   138  			return res[0], itemWithIndex, found
   139  		}
   140  	}
   141  	return
   142  }
   143  
   144  // WatchItems subscribes to receive notifications about the changes in the
   145  // mapping related to items with integer handles.
   146  func (idx *nameToIndex) WatchItems(subscriber string, channel chan<- NameToIndexDto) {
   147  	watcher := func(dto idxmap.NamedMappingGenericEvent) {
   148  		itemWithIndex, ok := dto.Value.(WithIndex)
   149  		if !ok {
   150  			return
   151  		}
   152  		msg := NameToIndexDto{
   153  			NamedMappingEvent: dto.NamedMappingEvent,
   154  			Item:              itemWithIndex,
   155  		}
   156  		select {
   157  		case channel <- msg:
   158  		case <-time.After(DefaultNotifTimeout):
   159  			idx.log.Warn("Unable to deliver notification")
   160  		}
   161  	}
   162  	if err := idx.Watch(subscriber, watcher); err != nil {
   163  		idx.log.Error(err)
   164  	}
   165  }
   166  
   167  // internalIndexFunction is an index function used internally for nameToIndex.
   168  func internalIndexFunction(item interface{}) map[string][]string {
   169  	indexes := map[string][]string{}
   170  	itemWithIndex, ok := item.(WithIndex)
   171  	if !ok || itemWithIndex == nil {
   172  		return indexes
   173  	}
   174  
   175  	indexes[indexKey] = []string{strconv.FormatUint(uint64(itemWithIndex.GetIndex()), 10)}
   176  	return indexes
   177  }