go.ligato.io/vpp-agent/v3@v3.5.0/plugins/kvscheduler/api/kv_scheduler_api.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 api
    16  
    17  import (
    18  	"context"
    19  	"time"
    20  
    21  	"go.ligato.io/cn-infra/v2/idxmap"
    22  	"google.golang.org/protobuf/proto"
    23  
    24  	"go.ligato.io/vpp-agent/v3/proto/ligato/kvscheduler"
    25  )
    26  
    27  // KeySelector is used to filter keys.
    28  type KeySelector func(key string) bool
    29  
    30  // KeyValuePair groups key with value.
    31  type KeyValuePair struct {
    32  	// Key identifies value.
    33  	Key string
    34  
    35  	// Value may represent some object, action or property.
    36  	//
    37  	// Value can be created either via northbound transaction (NB-value,
    38  	// ValueOrigin = FromNB) or pushed (as already created) through SB notification
    39  	// (SB-value, ValueOrigin = FromSB). Values from NB take priority as they
    40  	// overwrite existing SB values (via Modify operation), whereas notifications
    41  	// for existing NB values are ignored. For values retrieved with unknown
    42  	// origin the scheduler reviews the value's history to determine where it came
    43  	// from.
    44  	//
    45  	// For descriptors the values are mutable objects - Create, Update and Delete
    46  	// methods should reflect the value content without changing it.
    47  	// To add and maintain extra (runtime) attributes alongside the value, descriptor
    48  	// can use the value metadata.
    49  	Value proto.Message
    50  }
    51  
    52  // Metadata are extra information carried alongside non-derived (base) value
    53  // that descriptor may use for runtime attributes, secondary lookups, etc. This
    54  // data are opaque for the scheduler and fully owned by the descriptor.
    55  // Descriptor is supposed to create/edit (and use) metadata inside the Create,
    56  // Update, Delete methods and return the latest state with Retrieve.
    57  // Metadata, however, should not be used to determine the list of derived values
    58  // and dependencies for a value - this needs to be fixed for a given value
    59  // (Update is effectively replace) and known even before the value is created.
    60  //
    61  // The only way how scheduler can learn anything from metadata, is if MetadataMap
    62  // is enabled by descriptor (using WithMetadata method) and a custom NamedMapping
    63  // implementation is provided that defines secondary indexes (over metadata).
    64  // The scheduler exposes the current snapshot of secondary indexes, but otherwise
    65  // is not familiar with their semantics.
    66  type Metadata interface{}
    67  
    68  // KeyWithError stores error for a key whose value failed to get updated.
    69  type KeyWithError struct {
    70  	Key          string
    71  	TxnOperation kvscheduler.TxnOperation
    72  	Error        error
    73  }
    74  
    75  // KVWithMetadata encapsulates key-value pair with metadata and the origin mark.
    76  type KVWithMetadata struct {
    77  	Key      string
    78  	Value    proto.Message
    79  	Metadata Metadata
    80  	Origin   ValueOrigin
    81  }
    82  
    83  // View chooses from which point of view to look at the key-value space when
    84  // retrieving values.
    85  type View int
    86  
    87  const (
    88  	// SBView means to look directly into SB via Retrieve methods of descriptors
    89  	// to learn the real and up-to-date state of the system.
    90  	SBView View = iota
    91  
    92  	// NBView means to look at the key-value space from NB point of view, i.e.
    93  	// what key-values were requested and are assumed by NB to be applied.
    94  	NBView
    95  
    96  	// CachedView means to obtain the kvscheduler's current view of SB.
    97  	CachedView
    98  )
    99  
   100  // String converts View to string.
   101  func (v View) String() string {
   102  	switch v {
   103  	case SBView:
   104  		return "SB"
   105  	case NBView:
   106  		return "NB"
   107  	default:
   108  		return "cached"
   109  	}
   110  }
   111  
   112  // KVScheduler synchronizes the *desired* system state described by northbound
   113  // (NB) components via transactions with the *actual* state of the southbound (SB).
   114  // The  system state is represented as a set of inter-dependent key-value pairs
   115  // that can be created, updated, deleted from within NB transactions or be notified
   116  // about via notifications from the SB plane.
   117  // The scheduling basically implements "state reconciliation" - periodically and
   118  // on any change the scheduler attempts to update every value which has satisfied
   119  // dependencies but is out-of-sync with the desired state given by NB.
   120  //
   121  // For the scheduler, the key-value pairs are just abstract items that need
   122  // to be managed in a synchronized fashion according to the described relations.
   123  // It is up to the SB components to assign actual meaning to the individual
   124  // values via provided implementations for CRUD operations.
   125  //
   126  // The idea behind scheduler is based on the Mediator pattern - SB components
   127  // do not communicate directly, but instead interact through the mediator.
   128  // This reduces the dependencies between communicating objects, thereby reducing
   129  // coupling.
   130  //
   131  // The values are described for scheduler by registered KVDescriptor-s.
   132  // The scheduler learns two kinds of relations between values that have to be
   133  // respected by the scheduling algorithm:
   134  //   -> A depends on B:
   135  //          - A cannot exist without B
   136  //          - request to create A without B existing must be postponed by storing
   137  //            A into the cache of values with unmet dependencies (a.k.a. pending)
   138  //          - if B is to be removed and A exists, A must be removed first
   139  //            and cached in case B is restored in the future
   140  //          - Note: values pushed from SB are not checked for dependencies
   141  //   -> B is derived from A:
   142  //          - value B is not created directly (by NB or SB) but gets derived
   143  //            from base value A (using the DerivedValues() method of the base
   144  //            value's descriptor)
   145  //          - derived value exists only as long as its base does and gets removed
   146  //            (without caching) once the base value goes away
   147  //          - derived value may be described by a different descriptor than
   148  //            the base and usually represents property of the base value (that
   149  //            other values may depend on) or an extra action to be taken
   150  //            when additional dependencies are met.
   151  //
   152  // Every key-value pair must have at most one descriptor associated with it.
   153  // Base NB value without descriptor is considered unimplemented and will never
   154  // be created.
   155  // On the other hand, derived value is allowed to have no descriptor associated
   156  // with it. Typically, properties of base values are implemented as derived
   157  // (often empty) values without attached SB operations, used as targets for
   158  // dependencies.
   159  //
   160  // For descriptors the values are mutable objects - Create, Update and Delete
   161  // methods should reflect the value content without changing it.
   162  // To add and maintain extra (runtime) attributes alongside the value, scheduler
   163  // allows descriptors to append metadata - of any type - to each created
   164  // non-derived Object value. Descriptor can also use the metadata to define
   165  // secondary lookups, exposed via MetadataMap.
   166  //
   167  // Advantages of the centralized scheduling are:
   168  //   - easy to add new descriptors and dependencies
   169  //   - decreases the likelihood of race conditions and deadlocks in systems with
   170  //     complex dependencies
   171  //   - allows to write loosely-coupled SB components (mediator pattern)
   172  //   - descriptor API will force new SB components to follow the same
   173  //     code structure which will make them easier to familiarize with
   174  //   - NB components should never worry about dependencies between requests -
   175  //     it is taken care of by the scheduler
   176  //   - single cache for all (not only pending) values (exposed via REST,
   177  //     easier to debug)
   178  //
   179  // Apart from scheduling and execution, KVScheduler also offers the following
   180  // features:
   181  //   - collecting and counting present and past errors individually for every
   182  //     key
   183  //   - retry for previously failed actions
   184  //   - transaction reverting
   185  //   - exposing history of actions, errors and past value revisions over the REST
   186  //     interface
   187  //   - clearly describing the sequence of actions to be executed and postponed
   188  //     in the log file
   189  //   - allows to print verbose log messages describing graph traversal during
   190  //     transactions for debugging purposes
   191  //   - exposing graph snapshot, in the present state or after a given transaction,
   192  //     as a plotted graph (returned via REST) with values as nodes (colored to
   193  //     distinguish various value states) and dependencies/derivations as edges.
   194  type KVScheduler interface {
   195  	ValueProvider
   196  
   197  	// RegisterKVDescriptor registers descriptor(s) for a set of selected
   198  	// keys. It should be called in the Init phase of agent plugins.
   199  	// Every key-value pair must have at most one descriptor associated with it
   200  	// (none for derived values expressing properties).
   201  	RegisterKVDescriptor(descriptor ...*KVDescriptor) error
   202  
   203  	// GetRegisteredNBKeyPrefixes returns a list of key prefixes from NB with values
   204  	// described by registered descriptors and therefore managed by the scheduler.
   205  	GetRegisteredNBKeyPrefixes() []string
   206  
   207  	// StartNBTransaction starts a new transaction from NB to SB plane.
   208  	// The enqueued actions are scheduled for execution by Txn.Commit().
   209  	StartNBTransaction() Txn
   210  
   211  	// TransactionBarrier ensures that all notifications received prior to the call
   212  	// are associated with transactions that have already finalized.
   213  	TransactionBarrier()
   214  
   215  	// PushSBNotification notifies about a spontaneous value change in the SB
   216  	// plane (i.e. not triggered by NB transaction).
   217  	//
   218  	// Pass <value> as nil if the value was removed, non-nil otherwise.
   219  	//
   220  	// Values pushed from SB do not trigger Create/Update/Delete operations
   221  	// on the descriptors - the change has already happened in SB - only
   222  	// dependencies and derived values are updated.
   223  	//
   224  	// Values pushed from SB are overwritten by those created via NB transactions,
   225  	// however. For example, notifications for values already created by NB
   226  	// are ignored. But otherwise, SB values (not managed by NB) are untouched
   227  	// by reconciliation or any other operation of the scheduler/descriptor.
   228  	// Note: Origin in KVWithMetadata is ignored and can be left unset
   229  	// (automatically assumed to be FromSB).
   230  	PushSBNotification(notif ...KVWithMetadata) error
   231  
   232  	// GetMetadataMap returns (read-only) map associating value label with value
   233  	// metadata of a given descriptor.
   234  	// Returns nil if the descriptor does not expose metadata.
   235  	GetMetadataMap(descriptor string) idxmap.NamedMapping
   236  
   237  	// GetValueStatus returns the status of a non-derived value with the given
   238  	// key.
   239  	GetValueStatus(key string) *kvscheduler.BaseValueStatus
   240  
   241  	// WatchValueStatus allows to watch for changes in the status of non-derived
   242  	// values with keys selected by the selector (all if keySelector==nil).
   243  	WatchValueStatus(channel chan<- *kvscheduler.BaseValueStatus, keySelector KeySelector)
   244  
   245  	// GetTransactionHistory returns history of transactions started within
   246  	// the specified time window, or the full recorded history if the timestamps
   247  	// are zero values.
   248  	GetTransactionHistory(since, until time.Time) (history RecordedTxns)
   249  
   250  	// GetRecordedTransaction returns record of a transaction referenced
   251  	// by the sequence number.
   252  	GetRecordedTransaction(SeqNum uint64) (txn *RecordedTxn)
   253  
   254  	// ValidateSemantically validates given proto messages according to semantic validation(KVDescriptor.Validate)
   255  	// from registered KVDescriptors. If all locally known messages are valid, nil is returned. If some locally known
   256  	// messages are invalid, kvscheduler.MessageValidationErrors is returned. In any other case, error is returned.
   257  	//
   258  	// Usage of dynamic proto messages (dynamicpb.Message) described by remotely known models is not supported.
   259  	// The reason for this is that the KVDescriptors can validate only statically generated proto messages and
   260  	// remotely retrieved dynamic proto messages can't be converted to such proto messages (there are
   261  	// no locally available statically generated proto models).
   262  	ValidateSemantically([]proto.Message) error
   263  }
   264  
   265  // ValueProvider provides key/value data from different sources in system (NB, SB, KVProvider cache of SB)
   266  type ValueProvider interface {
   267  	// DumpValuesByDescriptor dumps values associated with the given
   268  	// descriptor as viewed from either NB (what was requested to be applied),
   269  	// SB (what is actually applied) or from the inside (what kvscheduler's
   270  	// cached view of SB is).
   271  	DumpValuesByDescriptor(descriptor string, view View) (kvs []KVWithMetadata, err error)
   272  
   273  	// DumpValuesByKeyPrefix like DumpValuesByDescriptor returns a dump of values,
   274  	// but the descriptor is selected based on the key prefix.
   275  	DumpValuesByKeyPrefix(keyPrefix string, view View) (kvs []KVWithMetadata, err error)
   276  }
   277  
   278  // Txn represent a single transaction.
   279  // Scheduler starts to plan and execute actions only after Commit is called.
   280  type Txn interface {
   281  	// SetValue changes (non-derived) value.
   282  	// If <value> is nil, the value will get deleted.
   283  	SetValue(key string, value proto.Message) Txn
   284  
   285  	// Commit orders scheduler to execute enqueued operations.
   286  	// Operations with unmet dependencies will get postponed and possibly
   287  	// executed later.
   288  	// <ctx> allows to pass transaction options (see With* functions from
   289  	// txn_options.go) or to cancel waiting for the end of a blocking transaction.
   290  	//
   291  	// For blocking transactions, the method returns the sequence number
   292  	// of the (finalized) transaction or ^uint64(0) (max uint64) if the transaction
   293  	// failed to even get initialized. In case of failures during the initialization
   294  	// or transaction processing, the method will return non-nil error, which is
   295  	// always an instance of TransactionError (see errors.go), wrapping all errors
   296  	// encountered during the transaction processing.
   297  	//
   298  	// Non-blocking transactions return immediately and always without errors.
   299  	// Subscribe with KVScheduler.WatchValueStatus() to get notified about all
   300  	// changes/errors, including those related to actions triggered later
   301  	// or asynchronously by a SB notification.
   302  	Commit(ctx context.Context) (txnSeqNum uint64, err error)
   303  }