go.ligato.io/vpp-agent/v3@v3.5.0/plugins/orchestrator/meta_service.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  	"fmt"
    20  	"strings"
    21  
    22  	"go.ligato.io/cn-infra/v2/logging"
    23  	"google.golang.org/grpc/codes"
    24  	"google.golang.org/grpc/metadata"
    25  	"google.golang.org/grpc/status"
    26  	"google.golang.org/protobuf/encoding/prototext"
    27  	"google.golang.org/protobuf/proto"
    28  	"google.golang.org/protobuf/reflect/protodesc"
    29  	"google.golang.org/protobuf/reflect/protoreflect"
    30  	"google.golang.org/protobuf/types/descriptorpb"
    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  )
    37  
    38  type genericService struct {
    39  	generic.UnimplementedMetaServiceServer
    40  	generic.UnimplementedManagerServiceServer
    41  
    42  	log      logging.Logger
    43  	dispatch Dispatcher
    44  }
    45  
    46  func (s *genericService) KnownModels(ctx context.Context, req *generic.KnownModelsRequest) (*generic.KnownModelsResponse, error) {
    47  	var infos []*generic.ModelDetail
    48  	for _, model := range models.RegisteredModels() {
    49  		if req.Class == "" || model.Spec().Class == req.Class {
    50  			infos = append(infos, model.ModelDetail())
    51  		}
    52  	}
    53  	resp := &generic.KnownModelsResponse{
    54  		KnownModels: infos,
    55  	}
    56  	return resp, nil
    57  }
    58  
    59  func (s *genericService) ProtoFileDescriptor(ctx context.Context, req *generic.ProtoFileDescriptorRequest) (*generic.ProtoFileDescriptorResponse, error) {
    60  	for _, model := range models.RegisteredModels() {
    61  		if req.FullProtoFileName == model.ProtoFile() {
    62  			fileDesc := model.NewInstance().ProtoReflect().Descriptor().ParentFile()
    63  			if fileDesc != nil {
    64  				return &generic.ProtoFileDescriptorResponse{
    65  					FileDescriptor:        protodesc.ToFileDescriptorProto(fileDesc),
    66  					FileImportDescriptors: toImportSet(allImports(fileDesc)),
    67  				}, nil
    68  			}
    69  		}
    70  	}
    71  	return nil, status.Error(codes.NotFound, fmt.Sprintf("Can't find proto file %v "+
    72  		"using registered models.", req.FullProtoFileName))
    73  }
    74  
    75  func (s *genericService) SetConfig(ctx context.Context, req *generic.SetConfigRequest) (*generic.SetConfigResponse, error) {
    76  	s.log.Debug("------------------------------")
    77  	s.log.Debugf("=> GenericMgr.SetConfig: %d items", len(req.Updates))
    78  	s.log.Debug("------------------------------")
    79  	for _, item := range req.Updates {
    80  		s.log.Debugf(" - %v", item)
    81  	}
    82  	s.log.Debug("------------------------------")
    83  
    84  	var ops = make(map[string]generic.UpdateResult_Operation)
    85  	var kvPairs []KeyVal
    86  	var keyLabels = make(map[string]Labels)
    87  
    88  	for _, update := range req.Updates {
    89  		item := update.Item
    90  		if item == nil {
    91  			return nil, status.Error(codes.InvalidArgument, "change item is nil")
    92  		}
    93  		var (
    94  			key string
    95  			val proto.Message
    96  			err error
    97  		)
    98  		if item.Data != nil {
    99  			val, err = models.UnmarshalItem(item)
   100  			if err != nil {
   101  				return nil, status.Error(codes.InvalidArgument, err.Error())
   102  			}
   103  			key, err = models.GetKey(val)
   104  			if err != nil {
   105  				return nil, status.Error(codes.InvalidArgument, err.Error())
   106  			}
   107  			ops[key] = generic.UpdateResult_UPDATE
   108  		} else if item.Id != nil {
   109  			model, err := models.GetModelForItem(item)
   110  			if err != nil {
   111  				return nil, status.Error(codes.InvalidArgument, err.Error())
   112  			}
   113  			key = model.KeyPrefix() + item.Id.Name
   114  			ops[key] = generic.UpdateResult_DELETE
   115  		} else {
   116  			return nil, status.Error(codes.InvalidArgument, "ProtoItem has no key or val defined.")
   117  		}
   118  		kvPairs = append(kvPairs, KeyVal{
   119  			Key: key,
   120  			Val: val,
   121  		})
   122  		keyLabels[key] = update.GetLabels()
   123  	}
   124  
   125  	md, hasMeta := metadata.FromIncomingContext(ctx)
   126  	if hasMeta && len(md["datasrc"]) == 1 {
   127  		ctx = contextdecorator.DataSrcContext(ctx, md["datasrc"][0])
   128  	} else {
   129  		ctx = contextdecorator.DataSrcContext(ctx, "grpc")
   130  	}
   131  	if req.OverwriteAll {
   132  		ctx = kvs.WithResync(ctx, kvs.FullResync, true)
   133  	}
   134  	ctx = kvs.WithRetryDefault(ctx)
   135  	results, err := s.dispatch.PushData(ctx, kvPairs, keyLabels)
   136  	if err != nil {
   137  		st := status.New(codes.FailedPrecondition, err.Error())
   138  		return nil, st.Err()
   139  	}
   140  
   141  	updateResults := []*generic.UpdateResult{}
   142  	for _, res := range results {
   143  		var msg string
   144  		if details := res.Status.GetDetails(); len(details) > 0 {
   145  			msg = strings.Join(res.Status.GetDetails(), ", ")
   146  		} else {
   147  			msg = res.Status.GetError()
   148  		}
   149  		updateResults = append(updateResults, &generic.UpdateResult{
   150  			Key: res.Key,
   151  			Status: &generic.ItemStatus{
   152  				Status:  res.Status.State.String(),
   153  				Message: msg,
   154  			},
   155  			// Op: res.Status.LastOperation.String(),
   156  		})
   157  	}
   158  
   159  	/*
   160  		// commit the transaction
   161  		if err := txn.Commit(); err != nil {
   162  			st := status.New(codes.FailedPrecondition, err.Error())
   163  			return nil, st.Err()
   164  			// TODO: use the WithDetails to return extra info to clients.
   165  			//ds, err := st.WithDetails(&rpc.DebugInfo{Detail: "Local transaction failed!"})
   166  			//if err != nil {
   167  			//	return nil, st.Err()
   168  			//}
   169  			//return nil, ds.Err()
   170  		}
   171  	*/
   172  
   173  	return &generic.SetConfigResponse{Results: updateResults}, nil
   174  }
   175  
   176  func (s *genericService) GetConfig(ctx context.Context, req *generic.GetConfigRequest) (*generic.GetConfigResponse, error) {
   177  	var configItems []*generic.ConfigItem
   178  
   179  	if req.Ids != nil && req.Labels != nil {
   180  		return nil, status.Error(codes.InvalidArgument, "both fields of the request are not nil!")
   181  	}
   182  	for key, data := range s.dispatch.ListData() {
   183  		labels := s.dispatch.ListLabels(key)
   184  		if !HasCorrectLabels(req.Labels, labels) {
   185  			continue
   186  		}
   187  		item, err := models.MarshalItem(data)
   188  		if err != nil {
   189  			return nil, status.Error(codes.InvalidArgument, err.Error())
   190  		}
   191  		if !ContainsItemID(req.Ids, item.Id) {
   192  			continue
   193  		}
   194  		var itemStatus *generic.ItemStatus
   195  		status, err := s.dispatch.GetStatus(key)
   196  		if err != nil {
   197  			s.log.Warnf("GetStatus failed: %v", err)
   198  		} else {
   199  			var msg string
   200  			if details := status.GetDetails(); len(details) > 0 {
   201  				msg = strings.Join(status.GetDetails(), ", ")
   202  			} else {
   203  				msg = status.GetError()
   204  			}
   205  			itemStatus = &generic.ItemStatus{
   206  				Status:  status.GetState().String(),
   207  				Message: msg,
   208  			}
   209  		}
   210  		configItems = append(configItems, &generic.ConfigItem{
   211  			Item:   item,
   212  			Status: itemStatus,
   213  			Labels: labels,
   214  		})
   215  	}
   216  
   217  	return &generic.GetConfigResponse{Items: configItems}, nil
   218  }
   219  
   220  func (s *genericService) DumpState(context.Context, *generic.DumpStateRequest) (*generic.DumpStateResponse, error) {
   221  	pairs, err := s.dispatch.ListState()
   222  	if err != nil {
   223  		return nil, err
   224  	}
   225  
   226  	fmt.Printf("dispatch.ListState: %d pairs", len(pairs))
   227  	for key, val := range pairs {
   228  		b, err := prototext.Marshal(val)
   229  		if err != nil {
   230  			return nil, err
   231  		}
   232  		fmt.Printf(" - [%s] %+v\n", key, string(b))
   233  	}
   234  
   235  	var states []*generic.StateItem
   236  	for _, kv := range pairs {
   237  		item, err := models.MarshalItem(kv)
   238  		if err != nil {
   239  			return nil, status.Error(codes.InvalidArgument, err.Error())
   240  		}
   241  		md := map[string]string{}
   242  		states = append(states, &generic.StateItem{
   243  			Item:     item,
   244  			Metadata: md,
   245  		})
   246  	}
   247  
   248  	return &generic.DumpStateResponse{Items: states}, nil
   249  }
   250  
   251  func (s *genericService) Subscribe(req *generic.SubscribeRequest, server generic.ManagerService_SubscribeServer) error {
   252  	return status.Error(codes.Unimplemented, "Not implemented yet")
   253  }
   254  
   255  // toImportSet performs convenient format conversion to descriptor.FileDescriptorSet
   256  func toImportSet(importFDs []protoreflect.FileDescriptor) *descriptorpb.FileDescriptorSet {
   257  	fdProtoimports := &descriptorpb.FileDescriptorSet{
   258  		File: make([]*descriptorpb.FileDescriptorProto, 0, len(importFDs)),
   259  	}
   260  	for _, importFD := range importFDs {
   261  		fdProtoimports.File = append(fdProtoimports.File, protodesc.ToFileDescriptorProto(importFD))
   262  	}
   263  	return fdProtoimports
   264  }
   265  
   266  // allImports extract direct and transitive imports from file descriptor.
   267  func allImports(desc protoreflect.FileDescriptor) []protoreflect.FileDescriptor {
   268  	results := make([]protoreflect.FileDescriptor, 0)
   269  	imports := desc.Imports()
   270  	for i := 0; i < imports.Len(); i++ {
   271  		importFD := imports.Get(i).FileDescriptor
   272  		results = append(results, importFD)
   273  		results = append(results, allImports(importFD)...)
   274  	}
   275  	return results
   276  }