go.ligato.io/vpp-agent/v3@v3.5.0/plugins/orchestrator/dispatcher.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 "runtime/trace" 21 "sync" 22 "time" 23 24 "github.com/pkg/errors" 25 "go.ligato.io/cn-infra/v2/logging" 26 "google.golang.org/protobuf/proto" 27 28 "go.ligato.io/vpp-agent/v3/pkg/models" 29 kvs "go.ligato.io/vpp-agent/v3/plugins/kvscheduler/api" 30 "go.ligato.io/vpp-agent/v3/plugins/orchestrator/contextdecorator" 31 "go.ligato.io/vpp-agent/v3/proto/ligato/kvscheduler" 32 ) 33 34 // KeyVal associates value with its key. 35 type KeyVal struct { 36 Key string 37 Val proto.Message 38 } 39 40 // KVPairs represents key-value pairs. 41 type KVPairs map[string]proto.Message 42 43 // Label is string key-value pair associated with configuration item. 44 // Label key format guidelines: label key should be a lower-case alphanumeric string 45 // which may contain periods and hyphens (but it should not contain consecutive 46 // periods/hyphens and it should not start with period/hyphen). Labels for configuration 47 // items should be prefixed with the reverse DNS notation of a domain they originate from 48 // (with domain owner's permission) for example: com.example.foo-bar-label. 49 // The io.ligato.* and ligato.* prefixes are reserved by vpp-agent for internal use. 50 type Labels map[string]string 51 52 type Status = kvscheduler.ValueStatus 53 54 type Result struct { 55 Key string 56 Status *Status 57 } 58 59 type Dispatcher interface { 60 ListData() KVPairs 61 PushData(context.Context, []KeyVal, map[string]Labels) ([]Result, error) 62 GetStatus(key string) (*Status, error) 63 ListState() (KVPairs, error) 64 ListLabels(key string) Labels 65 } 66 67 type dispatcher struct { 68 log logging.Logger 69 kvs kvs.KVScheduler 70 mu sync.Mutex 71 db Store 72 } 73 74 // ListData retrieves actual data. 75 func (p *dispatcher) ListData() KVPairs { 76 p.mu.Lock() 77 defer p.mu.Unlock() 78 79 return p.db.ListAll() 80 } 81 82 func (p *dispatcher) GetStatus(key string) (*Status, error) { 83 s := p.kvs.GetValueStatus(key) 84 status := s.GetValue() 85 if status == nil { 86 return nil, errors.Errorf("status for key %q not found", key) 87 } 88 return status, nil 89 } 90 91 // PushData updates actual data. 92 func (p *dispatcher) PushData(ctx context.Context, kvPairs []KeyVal, keyLabels map[string]Labels) (results []Result, err error) { 93 trace.Logf(ctx, "pushData", "%d KV pairs", len(kvPairs)) 94 95 // check key-value pairs for uniqness and validate key 96 uniq := make(map[string]proto.Message) 97 for _, kv := range kvPairs { 98 if kv.Val != nil { 99 // check if given key matches the key generated from value 100 if k := models.Key(kv.Val); k != kv.Key { 101 return nil, errors.Errorf("given key %q does not match with key generated from value: %q (value: %#v)", kv.Key, k, kv.Val) 102 } 103 } 104 // check if key is unique 105 if oldVal, ok := uniq[kv.Key]; ok { 106 return nil, errors.Errorf("found multiple key-value pairs with same key: %q (value 1: %#v, value 2: %#v)", kv.Key, kv.Val, oldVal) 107 } 108 uniq[kv.Key] = kv.Val 109 } 110 111 p.mu.Lock() 112 defer p.mu.Unlock() 113 114 pr := trace.StartRegion(ctx, "prepare kv data") 115 116 dataSrc, ok := contextdecorator.DataSrcFromContext(ctx) 117 if !ok { 118 dataSrc = "global" 119 } 120 121 p.log.Debugf("Push data with %d KV pairs (source: %s)", len(kvPairs), dataSrc) 122 123 txn := p.kvs.StartNBTransaction() 124 125 if typ, _ := kvs.IsResync(ctx); typ == kvs.FullResync { 126 trace.Log(ctx, "resyncType", typ.String()) 127 p.db.Reset(dataSrc) 128 for _, kv := range kvPairs { 129 if kv.Val == nil { 130 p.log.Debugf(" - PUT: %q (skipped nil value for resync)", kv.Key) 131 continue 132 } 133 p.log.Debugf(" - PUT: %q ", kv.Key) 134 p.db.Update(dataSrc, kv.Key, kv.Val) 135 p.db.ResetLabels(kv.Key) 136 for lkey, lval := range keyLabels[kv.Key] { 137 p.db.AddLabel(kv.Key, lkey, lval) 138 } 139 } 140 allPairs := p.db.ListAll() 141 p.log.Debugf("will resync %d pairs", len(allPairs)) 142 for k, v := range allPairs { 143 txn.SetValue(k, v) 144 } 145 } else { 146 for _, kv := range kvPairs { 147 if kv.Val == nil { 148 p.log.Debugf(" - DELETE: %q", kv.Key) 149 txn.SetValue(kv.Key, nil) 150 p.db.Delete(dataSrc, kv.Key) 151 for lkey := range keyLabels[kv.Key] { 152 p.db.DeleteLabel(kv.Key, lkey) 153 } 154 } else { 155 p.log.Debugf(" - UPDATE: %q ", kv.Key) 156 txn.SetValue(kv.Key, kv.Val) 157 p.db.Update(dataSrc, kv.Key, kv.Val) 158 p.db.ResetLabels(kv.Key) 159 for lkey, lval := range keyLabels[kv.Key] { 160 p.db.AddLabel(kv.Key, lkey, lval) 161 } 162 } 163 } 164 } 165 166 pr.End() 167 168 t := time.Now() 169 170 seqID, err := txn.Commit(ctx) 171 p.kvs.TransactionBarrier() 172 results = append(results, Result{ 173 Key: "seqnum", 174 Status: &Status{ 175 Details: []string{fmt.Sprint(seqID)}, 176 }, 177 }) 178 for key := range uniq { 179 s := p.kvs.GetValueStatus(key) 180 results = append(results, Result{ 181 Key: key, 182 Status: s.GetValue(), 183 }) 184 } 185 if err != nil { 186 if txErr, ok := err.(*kvs.TransactionError); ok && len(txErr.GetKVErrors()) > 0 { 187 kvErrs := txErr.GetKVErrors() 188 var errInfo = "" 189 for i, kvErr := range kvErrs { 190 errInfo += fmt.Sprintf(" - %3d. error (%s) %s - %v\n", i+1, kvErr.TxnOperation, kvErr.Key, kvErr.Error) 191 } 192 p.log.Errorf("Transaction #%d finished with %d errors\n%s", seqID, len(kvErrs), errInfo) 193 } else { 194 p.log.Errorf("Transaction failed: %v", err) 195 return nil, err 196 } 197 return results, err 198 } else { 199 took := time.Since(t) 200 p.log.Infof("Transaction #%d successful! (took %v)", seqID, took.Round(time.Microsecond*100)) 201 } 202 203 return results, nil 204 } 205 206 // ListState retrieves running state. 207 func (p *dispatcher) ListState() (KVPairs, error) { 208 p.mu.Lock() 209 defer p.mu.Unlock() 210 211 pairs := KVPairs{} 212 for _, prefix := range p.kvs.GetRegisteredNBKeyPrefixes() { 213 data, err := p.kvs.DumpValuesByKeyPrefix(prefix, kvs.CachedView) 214 if err != nil { 215 return nil, err 216 } 217 for _, d := range data { 218 // status := p.kvs.GetValueStatus(d.Key) 219 pairs[d.Key] = d.Value 220 } 221 } 222 223 return pairs, nil 224 } 225 226 func (p *dispatcher) ListLabels(key string) Labels { 227 p.mu.Lock() 228 defer p.mu.Unlock() 229 230 return p.db.ListLabels(key) 231 }