go.ligato.io/vpp-agent/v3@v3.5.0/plugins/kvscheduler/internal/graph/graph_read.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 graph 16 17 import ( 18 "fmt" 19 "sort" 20 "strings" 21 "time" 22 23 "go.ligato.io/cn-infra/v2/idxmap" 24 25 . "go.ligato.io/vpp-agent/v3/plugins/kvscheduler/api" 26 "go.ligato.io/vpp-agent/v3/plugins/kvscheduler/internal/utils" 27 ) 28 29 // printDelimiter is used in pretty-printing of the graph. 30 const printDelimiter = ", " 31 32 // edge lookup re-used between benchmark scale tests. 33 var benchEl *edgeLookup 34 35 // graphR implements ReadAccess. 36 type graphR struct { 37 *edgeLookup 38 39 parent *kvgraph 40 41 nodes map[string]*node 42 mappings map[string]idxmap.NamedMappingRW 43 timeline map[string][]*RecordedNode // key -> node records (from the oldest to the newest) 44 45 wCopy bool 46 unsaved utils.KeySet 47 } 48 49 // newGraphR creates and initializes a new instance of graphR. 50 func newGraphR(mt MethodTracker) *graphR { 51 var el *edgeLookup 52 if benchEl != nil { 53 // this is a benchmark 54 el = benchEl 55 el.reset() 56 } else { 57 el = newEdgeLookup(mt) 58 } 59 return &graphR{ 60 edgeLookup: el, 61 nodes: make(map[string]*node), 62 mappings: make(map[string]idxmap.NamedMappingRW), 63 timeline: make(map[string][]*RecordedNode), 64 } 65 } 66 67 // GetMetadataMap returns registered metadata map. 68 func (graph *graphR) GetMetadataMap(mapName string) idxmap.NamedMapping { 69 metadataMap, has := graph.mappings[mapName] 70 if !has { 71 return nil 72 } 73 return metadataMap 74 } 75 76 // GetNode returns node with the given key or nil if the key is unused. 77 func (graph *graphR) GetNode(key string) Node { 78 node, has := graph.nodes[key] 79 if !has { 80 return nil 81 } 82 return node.nodeR 83 } 84 85 // GetNodes returns a set of nodes matching the key selector (can be nil) 86 // and every provided flag selector. 87 func (graph *graphR) GetNodes(keySelector KeySelector, flagSelectors ...FlagSelector) (nodes []Node) { 88 if graph.parent.methodTracker != nil { 89 defer graph.parent.methodTracker("GetNodes")() 90 } 91 92 for key, node := range graph.nodes { 93 if keySelector != nil && !keySelector(key) { 94 continue 95 } 96 selected := true 97 for _, flagSelector := range flagSelectors { 98 for _, flag := range flagSelector.flags { 99 nodeFlag := node.flags[flag.GetIndex()] 100 hasFlag := nodeFlag != nil && 101 (flag.GetValue() == "" || (nodeFlag.GetValue() == flag.GetValue())) 102 if hasFlag != flagSelector.with { 103 selected = false 104 break 105 } 106 } 107 if !selected { 108 break 109 } 110 } 111 if !selected { 112 continue 113 } 114 nodes = append(nodes, node.nodeR) 115 } 116 return nodes 117 } 118 119 // GetNodeTimeline returns timeline of all node revisions, ordered from 120 // the oldest to the newest. 121 func (graph *graphR) GetNodeTimeline(key string) []*RecordedNode { 122 timeline, has := graph.timeline[key] 123 if !has { 124 return nil 125 } 126 return timeline 127 } 128 129 // GetFlagStats returns stats for a given flag. 130 func (graph *graphR) GetFlagStats(flagIndex int, selector KeySelector) FlagStats { 131 if graph.parent.methodTracker != nil { 132 defer graph.parent.methodTracker("GetFlagStats")() 133 } 134 135 stats := FlagStats{PerValueCount: make(map[string]uint)} 136 137 for key, timeline := range graph.timeline { 138 if selector != nil && !selector(key) { 139 continue 140 } 141 for /*idx*/ _, record := range timeline { 142 if record.TargetUpdateOnly { 143 continue 144 } 145 if flag := record.Flags.GetFlag(flagIndex); flag != nil { 146 //fmt.Printf("Found flag %s/%s in %dth record of %s\n", flagName, flag.GetValue(), idx, record.Key) 147 flagValue := flag.GetValue() 148 stats.TotalCount++ 149 if _, hasValue := stats.PerValueCount[flagValue]; !hasValue { 150 stats.PerValueCount[flagValue] = 0 151 } 152 stats.PerValueCount[flagValue]++ 153 } 154 } 155 } 156 157 return stats 158 } 159 160 // GetSnapshot returns the snapshot of the graph at a given time. 161 func (graph *graphR) GetSnapshot(time time.Time) (nodes []*RecordedNode) { 162 if graph.parent.methodTracker != nil { 163 defer graph.parent.methodTracker("GetSnapshot")() 164 } 165 166 for _, timeline := range graph.timeline { 167 for _, record := range timeline { 168 if record.Since.Before(time) && 169 (record.Until.IsZero() || record.Until.After(time)) { 170 nodes = append(nodes, record) 171 break 172 } 173 } 174 } 175 return nodes 176 } 177 178 // GetKeys returns sorted keys. 179 func (graph *graphR) GetKeys() []string { 180 if graph.parent.methodTracker != nil { 181 defer graph.parent.methodTracker("GetKeys")() 182 } 183 184 var keys []string 185 for key := range graph.nodes { 186 keys = append(keys, key) 187 } 188 sort.Slice(keys, func(i, j int) bool { 189 return keys[i] < keys[j] 190 }) 191 return keys 192 } 193 194 // Dump returns a human-readable string representation of the current graph 195 // content for debugging purposes. 196 func (graph *graphR) Dump() string { 197 if graph.parent.methodTracker != nil { 198 defer graph.parent.methodTracker("Dump")() 199 } 200 201 // order nodes by keys 202 var keys []string 203 for key := range graph.nodes { 204 keys = append(keys, key) 205 } 206 sort.Slice(keys, func(i, j int) bool { 207 return keys[i] < keys[j] 208 }) 209 210 var buf strings.Builder 211 graphInfo := fmt.Sprintf("%d nodes", len(keys)) 212 buf.WriteString("+======================================================================================================================+\n") 213 buf.WriteString(fmt.Sprintf("| GRAPH DUMP %105s |\n", graphInfo)) 214 buf.WriteString("+======================================================================================================================+\n") 215 216 for i, key := range keys { 217 node := graph.nodes[key] 218 219 buf.WriteString(fmt.Sprintf("| Key: %111q |\n", key)) 220 if label := node.GetLabel(); label != key { 221 buf.WriteString(fmt.Sprintf("| Label: %109s |\n", label)) 222 } 223 buf.WriteString(fmt.Sprintf("| Value: %109s |\n", utils.ProtoToString(node.GetValue()))) 224 buf.WriteString(fmt.Sprintf("| Flags: %109v |\n", prettyPrintFlags(node.flags))) 225 if len(node.targets) > 0 { 226 buf.WriteString(fmt.Sprintf("| Targets: %107v |\n", prettyPrintTargets(node.targets))) 227 } 228 if len(node.sources) > 0 { 229 buf.WriteString(fmt.Sprintf("| Sources: %107v |\n", prettyPrintTargets(node.sources))) 230 } 231 if metadata := graph.getMetadataFields(node); len(metadata) > 0 { 232 buf.WriteString(fmt.Sprintf("| Metadata: %106v |\n", metadata)) 233 } 234 if i+1 != len(keys) { 235 buf.WriteString("+----------------------------------------------------------------------------------------------------------------------+\n") 236 } 237 } 238 buf.WriteString("+----------------------------------------------------------------------------------------------------------------------+\n") 239 240 return buf.String() 241 } 242 243 // Release releases the graph handle (both Read() & Write() should end with 244 // release). 245 func (graph *graphR) Release() { 246 graph.parent.rwLock.RUnlock() 247 } 248 249 // ValidateEdges checks if targets and sources of all nodes correspond with each 250 // other. 251 // Use only for UTs, debugging, etc. 252 func (graph *graphR) ValidateEdges() error { 253 for key, node := range graph.nodes { 254 // validate targets 255 for _, target := range node.targets { 256 for _, targetKey := range target.MatchingKeys.Iterate() { 257 targetNode, ok := graph.nodes[targetKey] 258 if !ok { 259 return fmt.Errorf("broken target %s -> %s", key, targetKey) 260 } 261 source, _ := targetNode.sources.GetTargetForLabel(target.Relation, target.Label) 262 if source == nil || !source.MatchingKeys.Has(key) { 263 return fmt.Errorf("missing source for target %s -> %s", key, targetKey) 264 } 265 } 266 } 267 // validate sources 268 for _, source := range node.sources { 269 for _, sourceKey := range source.MatchingKeys.Iterate() { 270 sourceNode, ok := graph.nodes[sourceKey] 271 if !ok { 272 return fmt.Errorf("broken source %s -> %s", key, sourceKey) 273 } 274 target, _ := sourceNode.targets.GetTargetForLabel(source.Relation, source.Label) 275 if target == nil || !target.MatchingKeys.Has(key) { 276 return fmt.Errorf("missing target for source %s -> %s", key, sourceKey) 277 } 278 } 279 } 280 } 281 return nil 282 } 283 284 // copyNodesOnly returns a deep-copy of the graph, excluding the timelines 285 // and the map with mappings. 286 func (graph *graphR) copyNodesOnly() *graphR { 287 graphCopy := &graphR{ 288 edgeLookup: graph.edgeLookup.makeOverlay(), 289 parent: graph.parent, 290 nodes: make(map[string]*node), 291 wCopy: true, 292 } 293 for key, node := range graph.nodes { 294 nodeCopy := node.copy() 295 nodeCopy.graph = graphCopy 296 graphCopy.nodes[key] = newNode(nodeCopy) 297 } 298 return graphCopy 299 } 300 301 // recordNode builds a record for the node to be added into the timeline. 302 func (graph *graphR) recordNode(node *node, targetUpdateOnly bool) *RecordedNode { 303 targets := node.targets 304 node.targets = targets.copy() // COW for node, original for record 305 record := &RecordedNode{ 306 Since: time.Now(), 307 Key: node.key, 308 Label: node.label, 309 Value: utils.RecordProtoMessage(node.value), 310 Flags: RecordedFlags{Flags: node.flags}, 311 MetadataFields: graph.getMetadataFields(node), // returned is already copied 312 Targets: targets, 313 TargetUpdateOnly: targetUpdateOnly, 314 } 315 return record 316 } 317 318 // getMetadataFields returns secondary fields from metadata attached to the given node. 319 func (graph *graphR) getMetadataFields(node *node) map[string][]string { 320 writeCopy := graph.parent.graph != graph 321 if !writeCopy && node.metadataAdded { 322 mapping := graph.mappings[node.metadataMap] 323 return mapping.ListFields(node.label) 324 } 325 return nil 326 } 327 328 // prettyPrintFlags returns nicely formatted string representation of the given list of flags. 329 func prettyPrintFlags(flags [maxFlags]Flag) string { 330 var str string 331 for _, flag := range flags { 332 if flag == nil { 333 continue 334 } 335 if str != "" { 336 str += printDelimiter 337 } 338 if flag.GetValue() == "" { 339 str += flag.GetName() 340 } else { 341 str += fmt.Sprintf("%s:<%s>", flag.GetName(), flag.GetValue()) 342 } 343 } 344 return str 345 } 346 347 // prettyPrintTargets returns nicely formatted relation targets. 348 func prettyPrintTargets(targets Targets) string { 349 if len(targets) == 0 { 350 return "<NONE>" 351 } 352 idx := 0 353 relation := targets[0].Relation 354 str := fmt.Sprintf("[%s]{", relation) 355 for _, target := range targets { 356 if target.Relation != relation { 357 relation = target.Relation 358 str += fmt.Sprintf("}%s[%s]{", printDelimiter, relation) 359 idx = 0 360 } 361 if idx > 0 { 362 str += printDelimiter 363 } 364 if target.MatchingKeys.Length() == 1 && target.MatchingKeys.Has(target.Label) { 365 // special case: there 1:1 between label and the key 366 str += target.Label 367 } else { 368 str += target.Label + " -> " + target.MatchingKeys.String() 369 } 370 idx++ 371 } 372 str += "}" 373 return str 374 }