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 }