go.ligato.io/vpp-agent/v3@v3.5.0/plugins/kvscheduler/refresh.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 kvscheduler 16 17 import ( 18 "fmt" 19 "os" 20 "strings" 21 22 "go.ligato.io/cn-infra/v2/logging" 23 "google.golang.org/protobuf/encoding/prototext" 24 25 kvs "go.ligato.io/vpp-agent/v3/plugins/kvscheduler/api" 26 "go.ligato.io/vpp-agent/v3/plugins/kvscheduler/internal/graph" 27 "go.ligato.io/vpp-agent/v3/plugins/kvscheduler/internal/utils" 28 "go.ligato.io/vpp-agent/v3/proto/ligato/kvscheduler" 29 ) 30 31 var enableGraphDump = os.Getenv("KVSCHEDULER_GRAPHDUMP") != "" 32 33 const ( 34 nodeVisitBeginMark = "[BEGIN]" 35 nodeVisitEndMark = "[END]" 36 ) 37 38 // resyncData stores data to be used for resync after refresh. 39 type resyncData struct { 40 first bool // true if startup-resync 41 values []kvForTxn 42 } 43 44 // refreshGraph updates all/some values in the graph to their *real* state 45 // using the Retrieve methods from descriptors. 46 func (s *Scheduler) refreshGraph(graphW graph.RWAccess, 47 keys utils.KeySet, resyncData *resyncData, verbose bool, 48 ) { 49 if s.logGraphWalk { 50 keysToRefresh := "<ALL>" 51 if keys != nil && keys.Length() > 0 { 52 keysToRefresh = keys.String() 53 } 54 msg := fmt.Sprintf("refreshGrap (keys=%s)", keysToRefresh) 55 fmt.Printf("%s %s\n", nodeVisitBeginMark, msg) 56 defer fmt.Printf("%s %s\n", nodeVisitEndMark, msg) 57 } 58 refreshedKeys := utils.NewMapBasedKeySet() 59 60 // iterate over all descriptors, in order given by retrieve dependencies 61 for _, descriptor := range s.registry.GetAllDescriptors() { 62 handler := newDescriptorHandler(descriptor) 63 64 // get base values for this descriptor from memory before refresh 65 // (including those marked as unavailable which may need metadata update) 66 descrNodes := graphW.GetNodes(nil, 67 descrValsSelectors(descriptor.Name, false)...) 68 69 // check if this descriptor's key space should be refreshed as well 70 var skip bool 71 if keys != nil { 72 skip = keys.Length() > 0 73 for _, key := range keys.Iterate() { 74 if descriptor.KeySelector(key) { 75 skip = false 76 break 77 } 78 } 79 } 80 if skip { 81 // nothing to refresh in the key space of this descriptor 82 s.skipRefresh(descrNodes, nil, refreshedKeys) 83 continue 84 } 85 86 // get key-value pairs for correlation 87 var correlateCap int 88 if resyncData != nil && resyncData.first { 89 correlateCap = len(resyncData.values) 90 } else { 91 correlateCap = len(descrNodes) 92 } 93 correlate := make([]kvs.KVWithMetadata, 0, correlateCap) 94 if resyncData != nil && resyncData.first { 95 // for startup resync, use data received from NB 96 for _, kv := range resyncData.values { 97 if descriptor.KeySelector(kv.key) { 98 correlate = append(correlate, 99 kvs.KVWithMetadata{ 100 Key: kv.key, 101 Value: kv.value, 102 Origin: kv.origin, 103 }) 104 } 105 } 106 } else { 107 // for refresh of failed values or run-time resync, use in-memory 108 // kv-pairs for correlation 109 for _, node := range descrNodes { 110 if isNodeAvailable(node) { 111 correlate = append(correlate, nodeToKVPairWithMetadata(node)) 112 } 113 } 114 } 115 116 // execute Retrieve operation 117 retrieved, ableToRetrieve, err := handler.retrieve(correlate) 118 119 // mark un-retrievable as refreshed 120 if !ableToRetrieve || err != nil { 121 l := s.Log.WithField("descriptor", descriptor.Name) 122 if err != nil { 123 l.Errorf("failed to retrieve values: %v", err) 124 l.Debugf("skipping refresh for the descriptor") 125 } 126 s.skipRefresh(descrNodes, nil, refreshedKeys) 127 continue 128 } else if verbose { 129 plural := "s" 130 if len(retrieved) == 1 { 131 plural = "" 132 } 133 134 var list strings.Builder 135 for i, d := range retrieved { 136 num := fmt.Sprintf("%d.", i+1) 137 list.WriteString(fmt.Sprintf("\n - %3s [%s]: %q (%s)\n %v", 138 num, descriptor.Name, d.Key, d.Origin, utils.ProtoToString(d.Value))) 139 if d.Metadata != nil { 140 list.WriteString(fmt.Sprintf("\n Metadata: %+v", d.Metadata)) 141 } 142 } 143 s.Log.Debugf("%s descriptor retrieved %d item%s: %v", 144 descriptor.Name, len(retrieved), plural, list.String()) 145 146 } 147 148 if keys != nil && keys.Length() > 0 { 149 // mark keys that should not be touched as refreshed 150 s.skipRefresh(descrNodes, keys, refreshedKeys) 151 } 152 153 // process retrieved kv-pairs 154 for _, retrievedKV := range retrieved { 155 if keys != nil && keys.Length() > 0 { 156 // do no touch values that aren't meant to be refreshed 157 if toRefresh := keys.Has(retrievedKV.Key); !toRefresh { 158 continue 159 } 160 } 161 if !s.validRetrievedKV(retrievedKV, descriptor, refreshedKeys) { 162 continue 163 } 164 165 // 1st attempt to determine value origin 166 if retrievedKV.Origin == kvs.UnknownOrigin { 167 // determine value origin based on the values for correlation 168 for _, kv := range correlate { 169 if kv.Key == retrievedKV.Key { 170 retrievedKV.Origin = kv.Origin 171 break 172 } 173 } 174 } 175 176 // 2nd attempt to determine value origin 177 if retrievedKV.Origin == kvs.UnknownOrigin { 178 // determine value origin based on the last revision 179 timeline := graphW.GetNodeTimeline(retrievedKV.Key) 180 if len(timeline) > 0 { 181 lastRev := timeline[len(timeline)-1] 182 valueStateFlag := lastRev.Flags.GetFlag(ValueStateFlagIndex) 183 valueState := valueStateFlag.(*ValueStateFlag).valueState 184 retrievedKV.Origin = valueStateToOrigin(valueState) 185 } 186 } 187 188 if retrievedKV.Origin == kvs.UnknownOrigin { 189 // will assume this is from SB 190 retrievedKV.Origin = kvs.FromSB 191 } 192 193 // refresh node that represents this kv-pair 194 s.refreshValue(graphW, retrievedKV, handler, refreshedKeys, 2) 195 } 196 197 // unset the metadata from base NB values that do not actually exists 198 for _, node := range descrNodes { 199 if refreshed := refreshedKeys.Has(node.GetKey()); !refreshed { 200 if getNodeOrigin(node) == kvs.FromNB && node.GetMetadata() != nil { 201 if s.logGraphWalk { 202 fmt.Printf(" -> unset metadata for key=%s\n", node.GetKey()) 203 } 204 missingNode := graphW.SetNode(node.GetKey()) 205 missingNode.SetMetadata(nil) 206 } 207 } 208 } 209 } 210 211 // update state of values that do not actually exist 212 for _, node := range graphW.GetNodes(nil) { 213 if refreshed := refreshedKeys.Has(node.GetKey()); refreshed { 214 continue 215 } 216 s.refreshUnavailNode(graphW, node, refreshedKeys, 2) 217 } 218 219 if enableGraphDump && verbose && s.config.PrintTxnSummary { 220 fmt.Println(dumpGraph(graphW)) 221 } 222 } 223 224 // refreshValue refreshes node that represents the given retrieved key-value pair. 225 func (s *Scheduler) refreshValue(graphW graph.RWAccess, retrievedKV kvs.KVWithMetadata, 226 handler *descriptorHandler, refreshed utils.KeySet, indent int) { 227 if s.logGraphWalk { 228 indentStr := strings.Repeat(" ", indent) 229 msg := fmt.Sprintf("refreshValue (key=%s)", retrievedKV.Key) 230 fmt.Printf("%s%s %s\n", indentStr, nodeVisitBeginMark, msg) 231 defer fmt.Printf("%s%s %s\n", indentStr, nodeVisitEndMark, msg) 232 } 233 234 // refresh node that represents this kv-pair 235 node := graphW.SetNode(retrievedKV.Key) 236 node.SetLabel(handler.keyLabel(node.GetKey())) 237 node.SetValue(retrievedKV.Value) 238 if handler.descriptor.WithMetadata { 239 node.SetMetadataMap(handler.descriptor.Name) 240 node.SetMetadata(retrievedKV.Metadata) 241 } 242 s.refreshAvailNode(graphW, node, retrievedKV.Origin, false, node.GetKey(), refreshed, indent+2) 243 244 // determine the set of unavailable derived values 245 obsolete := getDerivedKeys(node) 246 derives := handler.derivedValues(node.GetKey(), node.GetValue()) 247 for _, newDerived := range derives { 248 obsolete.Del(newDerived.Key) 249 } 250 251 // keep obsolete derived values still in the relation 252 for _, key := range obsolete.Iterate() { 253 derives = append(derives, kvs.KeyValuePair{Key: key}) // value unused 254 } 255 256 // refresh relations 257 dependencies := handler.dependencies(node.GetKey(), node.GetValue()) 258 node.SetTargets(constructTargets(dependencies, derives)) 259 260 // refresh derived values 261 for _, kv := range derives { 262 isObsolete := obsolete.Has(kv.Key) 263 derNode := graphW.SetNode(kv.Key) 264 if !isObsolete { 265 derDescr := s.registry.GetDescriptorForKey(kv.Key) 266 derHandler := newDescriptorHandler(derDescr) 267 derNode.SetValue(kv.Value) 268 dependencies := derHandler.dependencies(derNode.GetKey(), derNode.GetValue()) 269 derNode.SetTargets(constructTargets(dependencies, nil)) 270 s.refreshAvailNode(graphW, derNode, retrievedKV.Origin, true, node.GetKey(), refreshed, indent+2) 271 } else { 272 s.refreshUnavailNode(graphW, derNode, refreshed, indent+2) 273 } 274 } 275 } 276 277 // refreshAvailNode refreshes state of a node whose value was returned by Retrieve. 278 func (s *Scheduler) refreshAvailNode(graphW graph.RWAccess, node graph.NodeRW, 279 origin kvs.ValueOrigin, derived bool, baseKey string, refreshed utils.KeySet, indent int) { 280 if s.logGraphWalk { 281 indentStr := strings.Repeat(" ", indent) 282 var derivedMark string 283 if derived { 284 derivedMark = ", is-derived" 285 } 286 msg := fmt.Sprintf("refreshAvailNode (key=%s%s)", node.GetKey(), derivedMark) 287 fmt.Printf("%s%s %s\n", indentStr, nodeVisitBeginMark, msg) 288 defer fmt.Printf("%s%s %s\n", indentStr, nodeVisitEndMark, msg) 289 } 290 291 // validate first 292 descriptor := s.registry.GetDescriptorForKey(node.GetKey()) // nil for properties 293 if derived && !s.validRetrievedDerivedKV(node, descriptor, refreshed) { 294 graphW.DeleteNode(node.GetKey()) 295 return 296 } 297 298 // update availability 299 if !isNodeAvailable(node) { 300 s.updatedStates.Add(baseKey) 301 node.DelFlags(UnavailValueFlagIndex) 302 } 303 refreshed.Add(node.GetKey()) 304 305 // refresh state 306 if getNodeState(node) == kvscheduler.ValueState_NONEXISTENT { 307 // newly found node 308 if origin == kvs.FromSB { 309 s.refreshNodeState(node, kvscheduler.ValueState_OBTAINED, indent) 310 } else { 311 s.refreshNodeState(node, kvscheduler.ValueState_DISCOVERED, indent) 312 } 313 } 314 if getNodeState(node) == kvscheduler.ValueState_PENDING { 315 // no longer pending apparently 316 s.refreshNodeState(node, kvscheduler.ValueState_CONFIGURED, indent) 317 } 318 319 // update descriptor flag 320 if descriptor != nil { 321 node.SetFlags(&DescriptorFlag{descriptor.Name}) 322 } else { 323 node.DelFlags(DescriptorFlagIndex) 324 } 325 326 // updated flags for derived values 327 if !derived { 328 node.DelFlags(DerivedFlagIndex) 329 } else { 330 node.SetFlags(&DerivedFlag{baseKey}) 331 } 332 } 333 334 // refreshUnavailNode refreshes state of a node whose value is found to be unavailable. 335 func (s *Scheduler) refreshUnavailNode(graphW graph.RWAccess, node graph.Node, refreshed utils.KeySet, indent int) { 336 if s.logGraphWalk { 337 indentStr := strings.Repeat(" ", indent) 338 msg := fmt.Sprintf("refreshUnavailNode (key=%s, isDerived=%t)", node.GetKey(), isNodeDerived(node)) 339 fmt.Printf("%s%s %s\n", indentStr, nodeVisitBeginMark, msg) 340 defer fmt.Printf("%s%s %s\n", indentStr, nodeVisitEndMark, msg) 341 } 342 343 refreshed.Add(node.GetKey()) 344 if isNodeAvailable(node) { 345 s.updatedStates.Add(getNodeBaseKey(node)) 346 } 347 state := getNodeState(node) 348 if getNodeOrigin(node) == kvs.FromSB || state == kvscheduler.ValueState_DISCOVERED { 349 // just remove from the graph 350 graphW.DeleteNode(node.GetKey()) 351 return 352 } 353 354 // mark node as unavailable, but do not delete 355 nodeW := graphW.SetNode(node.GetKey()) 356 if isNodeAvailable(node) { 357 nodeW.SetFlags(&UnavailValueFlag{}) 358 } 359 360 // update state 361 if state == kvscheduler.ValueState_UNIMPLEMENTED { 362 // it is expected that unimplemented value is not retrieved 363 return 364 } 365 if state == kvscheduler.ValueState_CONFIGURED { 366 if getNodeLastUpdate(node).value == nil { 367 s.refreshNodeState(nodeW, kvscheduler.ValueState_REMOVED, indent) 368 } else { 369 s.refreshNodeState(nodeW, kvscheduler.ValueState_MISSING, indent) 370 } 371 } 372 } 373 374 func (s *Scheduler) refreshNodeState(node graph.NodeRW, newState kvscheduler.ValueState, indent int) { 375 if getNodeState(node) != newState { 376 if s.logGraphWalk { 377 fmt.Printf("%s -> change value state from %v to %v\n", 378 strings.Repeat(" ", indent), getNodeState(node), newState) 379 } 380 node.SetFlags(&ValueStateFlag{valueState: newState}) 381 } 382 } 383 384 // skipRefresh is used to mark nodes as refreshed without actual refreshing 385 // if they should not (or cannot) be refreshed. 386 func (s *Scheduler) skipRefresh(nodes []graph.Node, except utils.KeySet, refreshed utils.KeySet) { 387 for _, node := range nodes { 388 if except != nil { 389 if toRefresh := except.Has(node.GetKey()); toRefresh { 390 continue 391 } 392 } 393 refreshed.Add(node.GetKey()) 394 395 // skip refresh for derived nodes 396 for _, derivedNode := range getDerivedNodes(node) { 397 refreshed.Add(derivedNode.GetKey()) 398 } 399 } 400 } 401 402 func dumpGraph(g graph.RWAccess) string { 403 keys := g.GetKeys() 404 405 var buf strings.Builder 406 graphInfo := fmt.Sprintf("%d nodes", len(keys)) 407 buf.WriteString("+======================================================================================================================+\n") 408 buf.WriteString(fmt.Sprintf("| GRAPH %110s |\n", graphInfo)) 409 buf.WriteString("+======================================================================================================================+\n") 410 writeLine := func(left, right string) { 411 n := 115 - len(left) 412 buf.WriteString(fmt.Sprintf("| %s %"+fmt.Sprint(n)+"s |\n", left, right)) 413 414 } 415 writeLines := func(linesStr string, prefix string) { 416 lines := strings.Split(linesStr, "\n") 417 for _, line := range lines { 418 if line == "" { 419 continue 420 } 421 writeLine(fmt.Sprintf("%s%s", prefix, line), "") 422 } 423 } 424 for i, key := range keys { 425 node := g.GetNode(key) 426 keyLabel := key 427 if label := node.GetLabel(); label != key && label != "" { 428 keyLabel = fmt.Sprintf("%s (%s)", key, label) 429 } 430 descriptor := "" 431 if f := node.GetFlag(DescriptorFlagIndex); f != nil { 432 descriptor = fmt.Sprintf("[%s] ", f.GetValue()) 433 } 434 lastUpdate := "-" 435 if f := node.GetFlag(LastUpdateFlagIndex); f != nil { 436 lastUpdate = f.GetValue() 437 } 438 unavailable := "" 439 if f := node.GetFlag(UnavailValueFlagIndex); f != nil { 440 unavailable = "<UNAVAILABLE> " 441 } 442 writeLine(fmt.Sprintf("%s%s", descriptor, keyLabel), fmt.Sprintf("%s %s %s", 443 unavailable, 444 lastUpdate, 445 getNodeState(node).String(), 446 )) 447 writeLines(prototext.Format(node.GetValue()), " ") 448 449 if f := node.GetTargets(DependencyRelation); len(f) > 0 { 450 writeLine("Depends on:", "") 451 for _, dep := range f { 452 var nodeDeps []string 453 for _, node := range dep.Nodes { 454 nodeDeps = append(nodeDeps, node.GetKey()) 455 } 456 if len(nodeDeps) > 1 { 457 writeLine(fmt.Sprintf(" - %s", dep.Label), "") 458 writeLines(strings.Join(nodeDeps, "\n"), " -> ") 459 } else if len(nodeDeps) == 1 { 460 writeLine(fmt.Sprintf(" - %s -> %v", dep.Label, strings.Join(nodeDeps, " ")), "") 461 } else { 462 writeLine(fmt.Sprintf(" - %s -> <UNAVAILABLE>", dep.Label), "") 463 } 464 } 465 } 466 if f := node.GetTargets(DerivesRelation); len(f) > 0 { 467 writeLine("Derives:", "") 468 var nodeDers []string 469 for _, der := range f { 470 if len(der.Nodes) == 0 { 471 nodeDers = append(nodeDers, der.Label) 472 } else { 473 for _, node := range der.Nodes { 474 desc := "" 475 if d := node.GetFlag(DescriptorFlagIndex); d != nil { 476 desc = fmt.Sprintf("[%s] ", d.GetValue()) 477 } 478 nodeDers = append(nodeDers, fmt.Sprintf("%s%s", desc, node.GetKey())) 479 } 480 } 481 } 482 writeLines(strings.Join(nodeDers, "\n"), " - ") 483 } 484 if f := node.GetSources(DependencyRelation); len(f) > 0 { 485 writeLine("Dependency for:", "") 486 var nodeDeps []string 487 for _, perLabel := range f { 488 for _, node := range perLabel.Nodes { 489 desc := "" 490 if d := node.GetFlag(DescriptorFlagIndex); d != nil { 491 desc = fmt.Sprintf("[%s] ", d.GetValue()) 492 } 493 nodeDeps = append(nodeDeps, fmt.Sprintf("%s%s", desc, node.GetKey())) 494 } 495 } 496 writeLines(strings.Join(nodeDeps, "\n"), " - ") 497 } 498 if f := node.GetSources(DerivesRelation); len(f) > 0 { 499 var nodeDers []string 500 for _, perLabel := range f { 501 for _, der := range perLabel.Nodes { 502 nodeDers = append(nodeDers, der.GetKey()) 503 } 504 } 505 writeLine(fmt.Sprintf("Derived from: %s", strings.Join(nodeDers, " ")), "") 506 } 507 if f := node.GetMetadata(); f != nil { 508 writeLine(fmt.Sprintf("Metadata: %+v", f), "") 509 } 510 if f := node.GetFlag(ErrorFlagIndex); f != nil { 511 writeLine(fmt.Sprintf("Errors: %+v", f.GetValue()), "") 512 } 513 514 if i+1 != len(keys) { 515 buf.WriteString("+----------------------------------------------------------------------------------------------------------------------+\n") 516 } 517 } 518 buf.WriteString("+======================================================================================================================+\n") 519 520 return buf.String() 521 } 522 523 // validRetrievedKV verifies validity of a retrieved KV-pair. 524 func (s *Scheduler) validRetrievedKV(kv kvs.KVWithMetadata, descriptor *kvs.KVDescriptor, refreshed utils.KeySet) bool { 525 if kv.Key == "" { 526 s.Log.WithFields(logging.Fields{ 527 "descriptor": descriptor.Name, 528 }).Warn("Descriptor retrieved value with empty key") 529 return false 530 } 531 if alreadyRetrieved := refreshed.Has(kv.Key); alreadyRetrieved { 532 s.Log.WithFields(logging.Fields{ 533 "descriptor": descriptor.Name, 534 "key": kv.Key, 535 }).Warn("The same value was retrieved more than once") 536 return false 537 } 538 if kv.Value == nil { 539 s.Log.WithFields(logging.Fields{ 540 "descriptor": descriptor.Name, 541 "key": kv.Key, 542 }).Warn("Descriptor retrieved nil value") 543 return false 544 } 545 if !descriptor.KeySelector(kv.Key) { 546 s.Log.WithFields(logging.Fields{ 547 "descriptor": descriptor.Name, 548 "key": kv.Key, 549 "value": kv.Value, 550 }).Warn("Descriptor retrieved value outside of its key space") 551 return false 552 } 553 return true 554 } 555 556 // validRetrievedDerivedKV verifies validity of a KV-pair derived from a retrieved value. 557 func (s *Scheduler) validRetrievedDerivedKV(node graph.Node, descriptor *kvs.KVDescriptor, refreshed utils.KeySet) bool { 558 descriptorName := "<NONE>" 559 if descriptor != nil { 560 descriptorName = descriptor.Name 561 } 562 if node.GetValue() == nil { 563 s.Log.WithFields(logging.Fields{ 564 "descriptor": descriptorName, 565 "key": node.GetKey(), 566 }).Warn("Derived nil value") 567 return false 568 } 569 if alreadyRetrieved := refreshed.Has(node.GetKey()); alreadyRetrieved { 570 s.Log.WithFields(logging.Fields{ 571 "descriptor": descriptorName, 572 "key": node.GetKey(), 573 }).Warn("The same value was retrieved more than once") 574 // return true -> let's overwrite invalidly retrieved derived value 575 } 576 return true 577 }