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

     1  //  Copyright (c) 2019 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 orchestrator
    16  
    17  import (
    18  	"context"
    19  	"os"
    20  	"strings"
    21  	"sync"
    22  
    23  	"github.com/go-errors/errors"
    24  	"go.ligato.io/cn-infra/v2/datasync"
    25  	"go.ligato.io/cn-infra/v2/datasync/resync"
    26  	"go.ligato.io/cn-infra/v2/infra"
    27  	"go.ligato.io/cn-infra/v2/logging"
    28  	"go.ligato.io/cn-infra/v2/rpc/grpc"
    29  	"google.golang.org/grpc/reflection"
    30  	"google.golang.org/protobuf/proto"
    31  
    32  	"go.ligato.io/vpp-agent/v3/pkg/models"
    33  	kvs "go.ligato.io/vpp-agent/v3/plugins/kvscheduler/api"
    34  	"go.ligato.io/vpp-agent/v3/plugins/orchestrator/contextdecorator"
    35  	"go.ligato.io/vpp-agent/v3/proto/ligato/generic"
    36  	"go.ligato.io/vpp-agent/v3/proto/ligato/kvscheduler"
    37  )
    38  
    39  var (
    40  	// EnableStatusPublishing enables status publishing.
    41  	EnableStatusPublishing = os.Getenv("ENABLE_STATUS_PUBLISHING") != ""
    42  
    43  	debugOrchestrator = os.Getenv("DEBUG_ORCHESTRATOR") != ""
    44  )
    45  
    46  // Plugin implements sync service for GRPC.
    47  type Plugin struct {
    48  	Deps
    49  
    50  	*dispatcher
    51  	manager *genericService
    52  
    53  	reflection bool
    54  
    55  	// datasync channels
    56  	changeChan   chan datasync.ChangeEvent
    57  	resyncChan   chan datasync.ResyncEvent
    58  	watchDataReg datasync.WatchRegistration
    59  
    60  	wg   sync.WaitGroup
    61  	quit chan struct{}
    62  }
    63  
    64  // Deps represents dependencies for the plugin.
    65  type Deps struct {
    66  	infra.PluginDeps
    67  
    68  	GRPC            grpc.Server
    69  	KVScheduler     kvs.KVScheduler
    70  	Watcher         datasync.KeyValProtoWatcher
    71  	StatusPublisher datasync.KeyProtoValWriter
    72  }
    73  
    74  // Init registers the service to GRPC server.
    75  func (p *Plugin) Init() (err error) {
    76  	p.quit = make(chan struct{})
    77  
    78  	p.dispatcher = &dispatcher{
    79  		log: logging.DefaultRegistry.NewLogger("dispatcher"),
    80  		db:  newMemStore(),
    81  		kvs: p.KVScheduler,
    82  	}
    83  
    84  	// register grpc service
    85  	p.manager = &genericService{
    86  		log:      p.log,
    87  		dispatch: p.dispatcher,
    88  	}
    89  
    90  	if grpcServer := p.GRPC.GetServer(); grpcServer != nil {
    91  		p.Log.Debugf("registering generic manager and meta service")
    92  		generic.RegisterManagerServiceServer(grpcServer, p.manager)
    93  		generic.RegisterMetaServiceServer(grpcServer, p.manager)
    94  
    95  		// register grpc services for reflection
    96  		if p.reflection {
    97  			p.Log.Debugf("registering grpc reflection service")
    98  			reflection.Register(grpcServer)
    99  		}
   100  	} else {
   101  		p.log.Infof("grpc server is not available")
   102  	}
   103  
   104  	p.Log.Infof("Found %d registered models", len(models.RegisteredModels()))
   105  	for _, model := range models.RegisteredModels() {
   106  		p.debugf("- model: %+v", *model.Spec())
   107  	}
   108  
   109  	var prefixes []string
   110  	if nbPrefixes := p.kvs.GetRegisteredNBKeyPrefixes(); len(nbPrefixes) > 0 {
   111  		p.log.Infof("Watching %d key prefixes from KVScheduler", len(nbPrefixes))
   112  		for _, prefix := range nbPrefixes {
   113  			p.debugf("- prefix: %s", prefix)
   114  			prefixes = append(prefixes, prefix)
   115  		}
   116  	} else {
   117  		p.log.Warnf("No key prefixes found in KVScheduler (ensure that all KVDescriptors are registered before this)")
   118  	}
   119  
   120  	// initialize datasync channels
   121  	p.resyncChan = make(chan datasync.ResyncEvent)
   122  	p.changeChan = make(chan datasync.ChangeEvent)
   123  
   124  	p.watchDataReg, err = p.Watcher.Watch(p.PluginName.String(),
   125  		p.changeChan, p.resyncChan, prefixes...)
   126  	if err != nil {
   127  		return err
   128  	}
   129  
   130  	return nil
   131  }
   132  
   133  // AfterInit subscribes to known NB prefixes.
   134  func (p *Plugin) AfterInit() (err error) {
   135  	// watch datasync events
   136  	p.wg.Add(1)
   137  	go p.watchEvents()
   138  
   139  	statusChan := make(chan *kvscheduler.BaseValueStatus, 100)
   140  	p.kvs.WatchValueStatus(statusChan, nil)
   141  
   142  	// watch KVSchedular status changes
   143  	p.wg.Add(1)
   144  	go p.watchStatus(statusChan)
   145  
   146  	return nil
   147  }
   148  
   149  func (p *Plugin) Close() (err error) {
   150  	close(p.quit)
   151  	p.wg.Wait()
   152  	return nil
   153  }
   154  
   155  // InitialSync will start initial synchronization.
   156  func (p *Plugin) InitialSync() error {
   157  	// SB resync
   158  	p.Log.Debugf("starting initial SB sync")
   159  	txn := p.KVScheduler.StartNBTransaction()
   160  	ctx := kvs.WithResync(context.Background(), kvs.DownstreamResync, true)
   161  	if _, err := txn.Commit(ctx); err != nil {
   162  		return errors.Errorf("initial SB sync failed: %v", err)
   163  	}
   164  	p.Log.Infof("initial SB sync complete")
   165  
   166  	// NB resync
   167  	p.Log.Debugf("starting initial NB sync")
   168  	resync.DefaultPlugin.DoResync() // NB init file data is also resynced here
   169  	p.Log.Infof("initial NB sync complete")
   170  
   171  	return nil
   172  }
   173  
   174  func (p *Plugin) watchEvents() {
   175  	defer p.wg.Done()
   176  
   177  	p.Log.Debugf("watching datasync events")
   178  	defer p.Log.Debugf("done watching datasync events")
   179  
   180  	for {
   181  		select {
   182  		case e := <-p.changeChan:
   183  			p.log.Debugf("=> received CHANGE event (%v changes)", len(e.GetChanges()))
   184  
   185  			var err error
   186  			var kvPairs []KeyVal
   187  
   188  			for _, x := range e.GetChanges() {
   189  				kv := KeyVal{
   190  					Key: x.GetKey(),
   191  				}
   192  				if x.GetChangeType() != datasync.Delete {
   193  					kv.Val, err = UnmarshalLazyValue(kv.Key, x)
   194  					if err != nil {
   195  						p.log.Errorf("decoding value for key %q failed: %v", kv.Key, err)
   196  						continue
   197  					}
   198  				}
   199  				kvPairs = append(kvPairs, kv)
   200  			}
   201  
   202  			if len(kvPairs) == 0 {
   203  				p.log.Warn("no valid kv pairs received in change event")
   204  				e.Done(nil)
   205  				continue
   206  			}
   207  
   208  			p.log.Debugf("Change with %d items", len(kvPairs))
   209  
   210  			ctx := e.GetContext()
   211  			if ctx == nil {
   212  				ctx = context.Background()
   213  			}
   214  			_, withDataSrc := contextdecorator.DataSrcFromContext(ctx)
   215  			if !withDataSrc {
   216  				ctx = contextdecorator.DataSrcContext(ctx, "datasync")
   217  			}
   218  			ctx = kvs.WithRetryDefault(ctx)
   219  			_, err = p.PushData(ctx, kvPairs, nil)
   220  			e.Done(err)
   221  
   222  		case e := <-p.resyncChan:
   223  			p.log.Debugf("=> received RESYNC event (%v prefixes)", len(e.GetValues()))
   224  
   225  			var kvPairs []KeyVal
   226  
   227  			for prefix, iter := range e.GetValues() {
   228  				var keyVals []datasync.KeyVal
   229  				for x, done := iter.GetNext(); !done; x, done = iter.GetNext() {
   230  					key := x.GetKey()
   231  					val, err := UnmarshalLazyValue(key, x)
   232  					if err != nil {
   233  						p.log.Errorf("unmarshal value for key %q failed: %v", key, err)
   234  						continue
   235  					}
   236  					kvPairs = append(kvPairs, KeyVal{
   237  						Key: key,
   238  						Val: val,
   239  					})
   240  					p.log.Debugf(" -- key: %s", x.GetKey())
   241  					keyVals = append(keyVals, x)
   242  				}
   243  				if len(keyVals) > 0 {
   244  					p.log.Debugf("- %q (%v items)", prefix, len(keyVals))
   245  				} else {
   246  					p.log.Debugf("- %q (no items)", prefix)
   247  				}
   248  				for _, x := range keyVals {
   249  					p.log.Debugf("\t - %q: (rev: %v)", x.GetKey(), x.GetRevision())
   250  				}
   251  			}
   252  
   253  			p.log.Debugf("Resync with %d items", len(kvPairs))
   254  
   255  			ctx := e.GetContext()
   256  			if ctx == nil {
   257  				ctx = context.Background()
   258  			}
   259  			_, withDataSrc := contextdecorator.DataSrcFromContext(ctx)
   260  			if !withDataSrc {
   261  				ctx = contextdecorator.DataSrcContext(ctx, "datasync")
   262  			}
   263  			ctx = kvs.WithResync(ctx, kvs.FullResync, true)
   264  			ctx = kvs.WithRetryDefault(ctx)
   265  
   266  			_, err := p.PushData(ctx, kvPairs, nil)
   267  			e.Done(err)
   268  
   269  		case <-p.quit:
   270  			return
   271  		}
   272  	}
   273  }
   274  
   275  func (p *Plugin) watchStatus(ch <-chan *kvscheduler.BaseValueStatus) {
   276  	defer p.wg.Done()
   277  
   278  	p.Log.Debugf("watching status changes")
   279  	defer p.Log.Debugf("done watching status events")
   280  
   281  	for {
   282  		select {
   283  		case s := <-ch:
   284  			p.debugf("incoming status change: %15s %v ===> %v (%v) %v",
   285  				s.Value.State, s.Value.Details, s.Value.Key, s.Value.LastOperation, s.Value.Error)
   286  			for _, dv := range s.DerivedValues {
   287  				p.debugf(" \t%15s %v ---> %v (%v) %v",
   288  					dv.State, dv.Details, dv.Key, dv.LastOperation, dv.Error)
   289  			}
   290  
   291  			if EnableStatusPublishing {
   292  				p.publishStatuses([]Result{
   293  					{Key: s.Value.Key, Status: s.Value},
   294  				})
   295  			}
   296  
   297  		case <-p.quit:
   298  			return
   299  		}
   300  	}
   301  }
   302  
   303  func (p *Plugin) publishStatuses(results []Result) {
   304  	if p.StatusPublisher == nil {
   305  		return
   306  	}
   307  
   308  	p.debugf("publishing %d statuses", len(results))
   309  	for _, res := range results {
   310  		statusKey := strings.Replace(res.Key, "config/", "config-status/", 1)
   311  		if statusKey == res.Key {
   312  			p.debugf("replace for key %q failed", res.Key)
   313  			continue
   314  		}
   315  		if err := p.StatusPublisher.Put(statusKey, res.Status, datasync.WithClientLifetimeTTL()); err != nil {
   316  			p.debugf("publishing status for key %q failed: %v", statusKey, err)
   317  		}
   318  	}
   319  }
   320  
   321  func (p *Plugin) debugf(f string, a ...interface{}) {
   322  	if debugOrchestrator {
   323  		p.log.Debugf(f, a...)
   324  	}
   325  }
   326  
   327  // UnmarshalLazyValue is helper function for unmarshalling from datasync.LazyValue.
   328  func UnmarshalLazyValue(key string, lazy datasync.LazyValue) (proto.Message, error) {
   329  	model, err := models.GetModelForKey(key)
   330  	if err != nil {
   331  		return nil, err
   332  	}
   333  	instance := model.NewInstance()
   334  	// try to deserialize the value into instance
   335  	if err := lazy.GetValue(instance); err != nil {
   336  		return nil, err
   337  	}
   338  	return instance, nil
   339  }
   340  
   341  func HasCorrectLabels(want map[string]string, have Labels) bool {
   342  	include := make(map[string]string)
   343  	exclude := make(map[string]string)
   344  	for wk, wl := range want {
   345  		if len(wk) > 0 && wk[0] == '!' {
   346  			exclude[wk[1:]] = wl
   347  		} else {
   348  			include[wk] = wl
   349  		}
   350  	}
   351  	for ik, iv := range include {
   352  		if hv, ok := have[ik]; !ok || iv != "" && iv != hv {
   353  			return false
   354  		}
   355  	}
   356  	for ek, ev := range exclude {
   357  		if hv, ok := have[ek]; ok && ev == hv || ok && ev == "" {
   358  			return false
   359  		}
   360  	}
   361  	return true
   362  }
   363  
   364  func ContainsItemID(want []*generic.Item_ID, have *generic.Item_ID) bool {
   365  	if len(want) == 0 {
   366  		return true
   367  	}
   368  	for _, w := range want {
   369  		if w.Model == have.Model && w.Name == have.Name {
   370  			return true
   371  		}
   372  	}
   373  	return false
   374  }