go.ligato.io/vpp-agent/v3@v3.5.0/plugins/configurator/configurator.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 configurator 16 17 import ( 18 "context" 19 "fmt" 20 "reflect" 21 "runtime/trace" 22 "strconv" 23 "time" 24 25 "go.ligato.io/cn-infra/v2/logging" 26 "google.golang.org/grpc" 27 "google.golang.org/grpc/codes" 28 "google.golang.org/grpc/metadata" 29 "google.golang.org/grpc/status" 30 "google.golang.org/protobuf/proto" 31 32 "go.ligato.io/vpp-agent/v3/pkg/models" 33 "go.ligato.io/vpp-agent/v3/pkg/util" 34 kvs "go.ligato.io/vpp-agent/v3/plugins/kvscheduler/api" 35 "go.ligato.io/vpp-agent/v3/plugins/orchestrator" 36 "go.ligato.io/vpp-agent/v3/plugins/orchestrator/contextdecorator" 37 pb "go.ligato.io/vpp-agent/v3/proto/ligato/configurator" 38 "go.ligato.io/vpp-agent/v3/proto/ligato/kvscheduler" 39 "go.ligato.io/vpp-agent/v3/proto/ligato/linux" 40 "go.ligato.io/vpp-agent/v3/proto/ligato/netalloc" 41 "go.ligato.io/vpp-agent/v3/proto/ligato/vpp" 42 ) 43 44 const ( 45 waitDoneCheckPendingPeriod = time.Millisecond * 10 46 ) 47 48 // configuratorServer implements DataSyncer service. 49 type configuratorServer struct { 50 pb.UnimplementedConfiguratorServiceServer 51 52 dumpService 53 notifyService 54 55 log logging.Logger 56 dispatch orchestrator.Dispatcher 57 } 58 59 func (svc *configuratorServer) Dump(ctx context.Context, req *pb.DumpRequest) (*pb.DumpResponse, error) { 60 return svc.dumpService.Dump(ctx, req) 61 } 62 63 func (svc *configuratorServer) Notify(from *pb.NotifyRequest, server pb.ConfiguratorService_NotifyServer) error { 64 return svc.notifyService.Notify(from, server) 65 } 66 67 // Get retrieves actual configuration data. 68 func (svc *configuratorServer) Get(context.Context, *pb.GetRequest) (*pb.GetResponse, error) { 69 defer trackOperation("Get")() 70 71 config := newConfig() 72 73 util.PlaceProtos(svc.dispatch.ListData(), 74 config.LinuxConfig, 75 config.VppConfig, 76 config.NetallocConfig, 77 ) 78 79 return &pb.GetResponse{Config: config}, nil 80 } 81 82 // Update adds configuration data present in data request to the VPP/Linux 83 func (svc *configuratorServer) Update(ctx context.Context, req *pb.UpdateRequest) (*pb.UpdateResponse, error) { 84 ctx, task := trace.NewTask(ctx, "grpc.Update") 85 defer task.End() 86 trace.Logf(ctx, "updateData", "%+v", req) 87 88 defer trackOperation("Update")() 89 90 protos := util.ExtractProtos( 91 req.GetUpdate().GetVppConfig(), 92 req.GetUpdate().GetLinuxConfig(), 93 req.GetUpdate().GetNetallocConfig(), 94 ) 95 96 var kvPairs []orchestrator.KeyVal 97 for _, p := range protos { 98 key, err := models.GetKey(p) 99 if err != nil { 100 svc.log.WithFields(map[string]interface{}{ 101 "message": proto.MessageName(p), 102 "type": reflect.TypeOf(p).Elem().Name(), 103 }).Debug("models.GetKey error: %s", err) 104 return nil, status.Error(codes.InvalidArgument, err.Error()) 105 } 106 kvPairs = append(kvPairs, orchestrator.KeyVal{ 107 Key: key, 108 Val: p, 109 }) 110 } 111 112 if req.FullResync { 113 ctx = kvs.WithResync(ctx, kvs.FullResync, true) 114 } 115 116 md, hasMeta := metadata.FromIncomingContext(ctx) 117 if hasMeta && len(md["datasrc"]) == 1 { 118 ctx = contextdecorator.DataSrcContext(ctx, md["datasrc"][0]) 119 } else { 120 ctx = contextdecorator.DataSrcContext(ctx, "grpc") 121 } 122 results, err := svc.dispatch.PushData(ctx, kvPairs, nil) 123 124 header := map[string]string{} 125 if seqNum := svc.extractTxnSeqNum(results); seqNum >= 0 { 126 header["seqnum"] = fmt.Sprint(seqNum) 127 } 128 if err := grpc.SetHeader(ctx, metadata.New(header)); err != nil { 129 logging.Warnf("sending grpc header failed: %v", err) 130 } 131 if err != nil { 132 st := status.New(codes.FailedPrecondition, err.Error()) 133 return nil, st.Err() 134 } 135 136 if req.WaitDone { 137 waitStart := time.Now() 138 var pendingKeys []string 139 for _, res := range results { 140 if res.Status.GetState() == kvscheduler.ValueState_PENDING { 141 pendingKeys = append(pendingKeys, res.Key) 142 } 143 } 144 if len(pendingKeys) > 0 { 145 svc.log.Infof("waiting for %d pending keys", len(pendingKeys)) 146 for len(pendingKeys) > 0 { 147 select { 148 case <-time.After(waitDoneCheckPendingPeriod): 149 pendingKeys = svc.listPending(pendingKeys) 150 case <-ctx.Done(): 151 svc.log.Warnf("update returning before %d pending keys are done: %v", len(pendingKeys), ctx.Err()) 152 return nil, ctx.Err() 153 } 154 } 155 } else { 156 svc.log.Debugf("no pendings keys to wait for") 157 } 158 svc.log.Infof("finished waiting for done (took %v)", time.Since(waitStart)) 159 } 160 161 svc.log.Debugf("config update finished with %d results", len(results)) 162 163 return &pb.UpdateResponse{}, nil 164 } 165 166 // Delete removes configuration data present in data request from the VPP/linux 167 func (svc *configuratorServer) Delete(ctx context.Context, req *pb.DeleteRequest) (*pb.DeleteResponse, error) { 168 defer trackOperation("Delete")() 169 170 protos := util.ExtractProtos( 171 req.GetDelete().GetVppConfig(), 172 req.GetDelete().GetLinuxConfig(), 173 req.GetDelete().GetNetallocConfig(), 174 ) 175 176 var kvPairs []orchestrator.KeyVal 177 for _, p := range protos { 178 key, err := models.GetKey(p) 179 if err != nil { 180 svc.log.WithFields(map[string]interface{}{ 181 "message": proto.MessageName(p), 182 "type": reflect.TypeOf(p).Elem().Name(), 183 }).Debug("models.GetKey error: %s", err) 184 return nil, status.Error(codes.InvalidArgument, err.Error()) 185 } 186 kvPairs = append(kvPairs, orchestrator.KeyVal{ 187 Key: key, 188 Val: nil, // delete 189 }) 190 } 191 192 md, hasMeta := metadata.FromIncomingContext(ctx) 193 if hasMeta && len(md["datasrc"]) == 1 { 194 ctx = contextdecorator.DataSrcContext(ctx, md["datasrc"][0]) 195 } else { 196 ctx = contextdecorator.DataSrcContext(ctx, "grpc") 197 } 198 results, err := svc.dispatch.PushData(ctx, kvPairs, nil) 199 200 header := map[string]string{} 201 if seqNum := svc.extractTxnSeqNum(results); seqNum >= 0 { 202 header["seqnum"] = fmt.Sprint(seqNum) 203 } 204 if err := grpc.SendHeader(ctx, metadata.New(header)); err != nil { 205 logging.Warnf("sending grpc header failed: %v", err) 206 } 207 if err != nil { 208 st := status.New(codes.FailedPrecondition, err.Error()) 209 return nil, st.Err() 210 } 211 212 if req.WaitDone { 213 waitStart := time.Now() 214 var pendingKeys []string 215 for _, res := range results { 216 if res.Status.GetState() == kvscheduler.ValueState_PENDING { 217 pendingKeys = append(pendingKeys, res.Key) 218 } 219 } 220 if len(pendingKeys) > 0 { 221 svc.log.Infof("waiting for %d pending keys", len(pendingKeys)) 222 for len(pendingKeys) > 0 { 223 select { 224 case <-time.After(waitDoneCheckPendingPeriod): 225 pendingKeys = svc.listPending(pendingKeys) 226 case <-ctx.Done(): 227 svc.log.Warnf("update returning before %d pending keys are done: %v", len(pendingKeys), ctx.Err()) 228 return nil, ctx.Err() 229 } 230 } 231 } else { 232 svc.log.Debugf("no pendings keys to wait for") 233 } 234 svc.log.Infof("finished waiting for done (took %v)", time.Since(waitStart)) 235 } 236 237 svc.log.Debugf("config delete finished with %d results", len(results)) 238 239 return &pb.DeleteResponse{}, nil 240 } 241 242 func (svc *configuratorServer) listPending(keys []string) []string { 243 var pending []string 244 for _, key := range keys { 245 st, err := svc.dispatch.GetStatus(key) 246 if err != nil { 247 svc.log.Debugf("dispatch.GetStatus for key %q error: %v", key, err) 248 continue 249 } 250 if st.GetState() == kvscheduler.ValueState_PENDING { 251 pending = append(pending, key) 252 } 253 } 254 return pending 255 } 256 257 func (svc *configuratorServer) extractTxnSeqNum(results []orchestrator.Result) int { 258 seqNum := -1 259 for _, result := range results { 260 if result.Key == "seqnum" { 261 str := result.Status.Details[0] 262 if n, err := strconv.Atoi(str); err == nil { 263 seqNum = n 264 } else { 265 svc.log.Debugf("invalid seqnum in result: %q", str) 266 } 267 } 268 } 269 return seqNum 270 } 271 272 func newConfig() *pb.Config { 273 return &pb.Config{ 274 LinuxConfig: &linux.ConfigData{}, 275 VppConfig: &vpp.ConfigData{}, 276 NetallocConfig: &netalloc.ConfigData{}, 277 } 278 }