VOL-2138 Use v2 import paths for voltha-lib-go
Change-Id: I853dcce79c4defbe3140f3893651a2f50d63e053
diff --git a/vendor/github.com/opencord/voltha-lib-go/v2/pkg/db/model/non_persisted_revision.go b/vendor/github.com/opencord/voltha-lib-go/v2/pkg/db/model/non_persisted_revision.go
new file mode 100644
index 0000000..384caed
--- /dev/null
+++ b/vendor/github.com/opencord/voltha-lib-go/v2/pkg/db/model/non_persisted_revision.go
@@ -0,0 +1,514 @@
+/*
+ * Copyright 2018-present Open Networking Foundation
+
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+
+ * http://www.apache.org/licenses/LICENSE-2.0
+
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package model
+
+import (
+ "bytes"
+ "context"
+ "crypto/md5"
+ "fmt"
+ "github.com/golang/protobuf/proto"
+ "github.com/opencord/voltha-lib-go/v2/pkg/db/kvstore"
+ "github.com/opencord/voltha-lib-go/v2/pkg/log"
+ "reflect"
+ "sort"
+ "strings"
+ "sync"
+ "time"
+)
+
+// TODO: Cache logic will have to be revisited to cleanup unused entries in memory (disabled for now)
+//
+type revCacheSingleton struct {
+ sync.RWMutex
+ Cache sync.Map
+}
+
+func (s *revCacheSingleton) Get(path string) (interface{}, bool) {
+ return s.Cache.Load(path)
+}
+func (s *revCacheSingleton) Set(path string, value interface{}) {
+ s.Cache.Store(path, value)
+}
+func (s *revCacheSingleton) Delete(path string) {
+ s.Cache.Delete(path)
+}
+
+var revCacheInstance *revCacheSingleton
+var revCacheOnce sync.Once
+
+func GetRevCache() *revCacheSingleton {
+ revCacheOnce.Do(func() {
+ revCacheInstance = &revCacheSingleton{Cache: sync.Map{}}
+ })
+ return revCacheInstance
+}
+
+type NonPersistedRevision struct {
+ mutex sync.RWMutex
+ Root *root
+ Config *DataRevision
+ childrenLock sync.RWMutex
+ Children map[string][]Revision
+ Hash string
+ Branch *Branch
+ WeakRef string
+ Name string
+ lastUpdate time.Time
+}
+
+func NewNonPersistedRevision(root *root, branch *Branch, data interface{}, children map[string][]Revision) Revision {
+ r := &NonPersistedRevision{}
+ r.Root = root
+ r.Branch = branch
+ r.Config = NewDataRevision(root, data)
+ r.Children = children
+ r.Hash = r.hashContent()
+ return r
+}
+
+func (npr *NonPersistedRevision) SetConfig(config *DataRevision) {
+ npr.mutex.Lock()
+ defer npr.mutex.Unlock()
+ npr.Config = config
+}
+
+func (npr *NonPersistedRevision) GetConfig() *DataRevision {
+ npr.mutex.Lock()
+ defer npr.mutex.Unlock()
+ return npr.Config
+}
+
+func (npr *NonPersistedRevision) SetAllChildren(children map[string][]Revision) {
+ npr.childrenLock.Lock()
+ defer npr.childrenLock.Unlock()
+ npr.Children = make(map[string][]Revision)
+
+ for key, value := range children {
+ npr.Children[key] = make([]Revision, len(value))
+ copy(npr.Children[key], value)
+ }
+}
+
+func (npr *NonPersistedRevision) SetChildren(name string, children []Revision) {
+ npr.childrenLock.Lock()
+ defer npr.childrenLock.Unlock()
+
+ npr.Children[name] = make([]Revision, len(children))
+ copy(npr.Children[name], children)
+}
+
+func (npr *NonPersistedRevision) GetAllChildren() map[string][]Revision {
+ npr.childrenLock.Lock()
+ defer npr.childrenLock.Unlock()
+
+ return npr.Children
+}
+
+func (npr *NonPersistedRevision) GetChildren(name string) []Revision {
+ npr.childrenLock.Lock()
+ defer npr.childrenLock.Unlock()
+
+ if _, exists := npr.Children[name]; exists {
+ return npr.Children[name]
+ }
+ return nil
+}
+
+func (npr *NonPersistedRevision) SetHash(hash string) {
+ npr.mutex.Lock()
+ defer npr.mutex.Unlock()
+ npr.Hash = hash
+}
+
+func (npr *NonPersistedRevision) GetHash() string {
+ //npr.mutex.Lock()
+ //defer npr.mutex.Unlock()
+ return npr.Hash
+}
+
+func (npr *NonPersistedRevision) ClearHash() {
+ npr.mutex.Lock()
+ defer npr.mutex.Unlock()
+ npr.Hash = ""
+}
+
+func (npr *NonPersistedRevision) GetName() string {
+ //npr.mutex.Lock()
+ //defer npr.mutex.Unlock()
+ return npr.Name
+}
+
+func (npr *NonPersistedRevision) SetName(name string) {
+ //npr.mutex.Lock()
+ //defer npr.mutex.Unlock()
+ npr.Name = name
+}
+func (npr *NonPersistedRevision) SetBranch(branch *Branch) {
+ npr.mutex.Lock()
+ defer npr.mutex.Unlock()
+ npr.Branch = branch
+}
+
+func (npr *NonPersistedRevision) GetBranch() *Branch {
+ npr.mutex.Lock()
+ defer npr.mutex.Unlock()
+ return npr.Branch
+}
+
+func (npr *NonPersistedRevision) GetData() interface{} {
+ npr.mutex.Lock()
+ defer npr.mutex.Unlock()
+ if npr.Config == nil {
+ return nil
+ }
+ return npr.Config.Data
+}
+
+func (npr *NonPersistedRevision) GetNode() *node {
+ npr.mutex.Lock()
+ defer npr.mutex.Unlock()
+ return npr.Branch.Node
+}
+
+func (npr *NonPersistedRevision) Finalize(skipOnExist bool) {
+ npr.Hash = npr.hashContent()
+}
+
+// hashContent generates a hash string based on the contents of the revision.
+// The string should be unique to avoid conflicts with other revisions
+func (npr *NonPersistedRevision) hashContent() string {
+ var buffer bytes.Buffer
+ var childrenKeys []string
+
+ if npr.Config != nil {
+ buffer.WriteString(npr.Config.Hash)
+ }
+
+ if npr.Name != "" {
+ buffer.WriteString(npr.Name)
+ }
+
+ for key := range npr.Children {
+ childrenKeys = append(childrenKeys, key)
+ }
+
+ sort.Strings(childrenKeys)
+
+ if len(npr.Children) > 0 {
+ // Loop through sorted Children keys
+ for _, key := range childrenKeys {
+ for _, child := range npr.Children[key] {
+ if child != nil && child.GetHash() != "" {
+ buffer.WriteString(child.GetHash())
+ }
+ }
+ }
+ }
+
+ return fmt.Sprintf("%x", md5.Sum(buffer.Bytes()))[:12]
+}
+
+// Get will retrieve the data for the current revision
+func (npr *NonPersistedRevision) Get(depth int) interface{} {
+ // 1. Clone the data to avoid any concurrent access issues
+ // 2. The current rev might still be pointing to an old config
+ // thus, force the revision to get its latest value
+ latestRev := npr.GetBranch().GetLatest()
+ originalData := proto.Clone(latestRev.GetData().(proto.Message))
+ data := originalData
+
+ if depth != 0 {
+ // FIXME: Traversing the struct through reflection sometimes corrupts the data.
+ // Unlike the original python implementation, golang structs are not lazy loaded.
+ // Keeping this non-critical logic for now, but Get operations should be forced to
+ // depth=0 to avoid going through the following loop.
+ for fieldName, field := range ChildrenFields(latestRev.GetData()) {
+ childDataName, childDataHolder := GetAttributeValue(data, fieldName, 0)
+ if field.IsContainer {
+ for _, rev := range latestRev.GetChildren(fieldName) {
+ childData := rev.Get(depth - 1)
+ foundEntry := false
+ for i := 0; i < childDataHolder.Len(); i++ {
+ cdh_if := childDataHolder.Index(i).Interface()
+ if cdh_if.(proto.Message).String() == childData.(proto.Message).String() {
+ foundEntry = true
+ break
+ }
+ }
+ if !foundEntry {
+ // avoid duplicates by adding it only if the child was not found in the holder
+ childDataHolder = reflect.Append(childDataHolder, reflect.ValueOf(childData))
+ }
+ }
+ } else {
+ if revs := npr.GetBranch().GetLatest().GetChildren(fieldName); revs != nil && len(revs) > 0 {
+ rev := revs[0]
+ if rev != nil {
+ childData := rev.Get(depth - 1)
+ if reflect.TypeOf(childData) == reflect.TypeOf(childDataHolder.Interface()) {
+ childDataHolder = reflect.ValueOf(childData)
+ }
+ }
+ }
+ }
+ // Merge child data with cloned object
+ reflect.ValueOf(data).Elem().FieldByName(childDataName).Set(childDataHolder)
+ }
+ }
+
+ result := data
+
+ if result != nil {
+ // We need to send back a copy of the retrieved object
+ result = proto.Clone(data.(proto.Message))
+ }
+
+ return result
+}
+
+// UpdateData will refresh the data content of the revision
+func (npr *NonPersistedRevision) UpdateData(ctx context.Context, data interface{}, branch *Branch) Revision {
+ npr.mutex.Lock()
+ defer npr.mutex.Unlock()
+
+ log.Debugw("update-data", log.Fields{"hash": npr.GetHash(), "current": npr.Config.Data, "provided": data})
+
+ // Do not update the revision if data is the same
+ if npr.Config.Data != nil && npr.Config.hashData(npr.Root, data) == npr.Config.Hash {
+ log.Debugw("stored-data-matches-latest", log.Fields{"stored": npr.Config.Data, "provided": data})
+ return npr
+ }
+
+ // Construct a new revision based on the current one
+ newRev := NonPersistedRevision{}
+ newRev.Config = NewDataRevision(npr.Root, data)
+ newRev.Hash = npr.Hash
+ newRev.Root = npr.Root
+ newRev.Name = npr.Name
+ newRev.Branch = branch
+ newRev.lastUpdate = npr.lastUpdate
+
+ newRev.Children = make(map[string][]Revision)
+ for entryName, childrenEntry := range branch.GetLatest().GetAllChildren() {
+ newRev.Children[entryName] = append(newRev.Children[entryName], childrenEntry...)
+ }
+
+ newRev.Finalize(false)
+
+ log.Debugw("update-data-complete", log.Fields{"updated": newRev.Config.Data, "provided": data})
+
+ return &newRev
+}
+
+// UpdateChildren will refresh the list of children with the provided ones
+// It will carefully go through the list and ensure that no child is lost
+func (npr *NonPersistedRevision) UpdateChildren(ctx context.Context, name string, children []Revision, branch *Branch) Revision {
+ npr.mutex.Lock()
+ defer npr.mutex.Unlock()
+
+ // Construct a new revision based on the current one
+ updatedRev := &NonPersistedRevision{}
+ updatedRev.Config = NewDataRevision(npr.Root, npr.Config.Data)
+ updatedRev.Hash = npr.Hash
+ updatedRev.Branch = branch
+ updatedRev.Name = npr.Name
+ updatedRev.lastUpdate = npr.lastUpdate
+
+ updatedRev.Children = make(map[string][]Revision)
+ for entryName, childrenEntry := range branch.GetLatest().GetAllChildren() {
+ updatedRev.Children[entryName] = append(updatedRev.Children[entryName], childrenEntry...)
+ }
+
+ var updatedChildren []Revision
+
+ // Verify if the map contains already contains an entry matching the name value
+ // If so, we need to retain the contents of that entry and merge them with the provided children revision list
+ if existingChildren := branch.GetLatest().GetChildren(name); existingChildren != nil {
+ // Construct a map of unique child names with the respective index value
+ // for the children in the existing revision as well as the new ones
+ existingNames := make(map[string]int)
+ newNames := make(map[string]int)
+
+ for i, newChild := range children {
+ newNames[newChild.GetName()] = i
+ }
+
+ for i, existingChild := range existingChildren {
+ existingNames[existingChild.GetName()] = i
+
+ // If an existing entry is not in the new list, add it to the updated list, so it is not forgotten
+ if _, exists := newNames[existingChild.GetName()]; !exists {
+ updatedChildren = append(updatedChildren, existingChild)
+ }
+ }
+
+ log.Debugw("existing-children-names", log.Fields{"hash": npr.GetHash(), "names": existingNames})
+
+ // Merge existing and new children
+ for _, newChild := range children {
+ nameIndex, nameExists := existingNames[newChild.GetName()]
+
+ // Does the existing list contain a child with that name?
+ if nameExists {
+ // Check if the data has changed or not
+ if existingChildren[nameIndex].GetData().(proto.Message).String() != newChild.GetData().(proto.Message).String() {
+ log.Debugw("replacing-existing-child", log.Fields{
+ "old-hash": existingChildren[nameIndex].GetHash(),
+ "old-data": existingChildren[nameIndex].GetData(),
+ "new-hash": newChild.GetHash(),
+ "new-data": newChild.GetData(),
+ })
+
+ // replace entry
+ newChild.GetNode().SetRoot(existingChildren[nameIndex].GetNode().GetRoot())
+ updatedChildren = append(updatedChildren, newChild)
+ } else {
+ log.Debugw("keeping-existing-child", log.Fields{
+ "old-hash": existingChildren[nameIndex].GetHash(),
+ "old-data": existingChildren[nameIndex].GetData(),
+ "new-hash": newChild.GetHash(),
+ "new-data": newChild.GetData(),
+ })
+
+ // keep existing entry
+ updatedChildren = append(updatedChildren, existingChildren[nameIndex])
+ }
+ } else {
+ log.Debugw("adding-unknown-child", log.Fields{
+ "hash": newChild.GetHash(),
+ "data": newChild.GetData(),
+ })
+
+ // new entry ... just add it
+ updatedChildren = append(updatedChildren, newChild)
+ }
+ }
+
+ // Save children in new revision
+ updatedRev.SetChildren(name, updatedChildren)
+
+ updatedNames := make(map[string]int)
+ for i, updatedChild := range updatedChildren {
+ updatedNames[updatedChild.GetName()] = i
+ }
+
+ log.Debugw("updated-children-names", log.Fields{"hash": npr.GetHash(), "names": updatedNames})
+
+ } else {
+ // There are no children available, just save the provided ones
+ updatedRev.SetChildren(name, children)
+ }
+
+ updatedRev.Finalize(false)
+
+ return updatedRev
+}
+
+// UpdateAllChildren will replace the current list of children with the provided ones
+func (npr *NonPersistedRevision) UpdateAllChildren(children map[string][]Revision, branch *Branch) Revision {
+ npr.mutex.Lock()
+ defer npr.mutex.Unlock()
+
+ newRev := npr
+ newRev.Config = npr.Config
+ newRev.Hash = npr.Hash
+ newRev.Branch = branch
+ newRev.Name = npr.Name
+ newRev.lastUpdate = npr.lastUpdate
+
+ newRev.Children = make(map[string][]Revision)
+ for entryName, childrenEntry := range children {
+ newRev.Children[entryName] = append(newRev.Children[entryName], childrenEntry...)
+ }
+ newRev.Finalize(false)
+
+ return newRev
+}
+
+// Drop is used to indicate when a revision is no longer required
+func (npr *NonPersistedRevision) Drop(txid string, includeConfig bool) {
+ log.Debugw("dropping-revision", log.Fields{"hash": npr.GetHash(), "name": npr.GetName()})
+}
+
+// ChildDrop will remove a child entry matching the provided parameters from the current revision
+func (npr *NonPersistedRevision) ChildDrop(childType string, childHash string) {
+ if childType != "" {
+ children := make([]Revision, len(npr.GetChildren(childType)))
+ copy(children, npr.GetChildren(childType))
+ for i, child := range children {
+ if child.GetHash() == childHash {
+ children = append(children[:i], children[i+1:]...)
+ npr.SetChildren(childType, children)
+ break
+ }
+ }
+ }
+}
+
+/// ChildDropByName will remove a child entry matching the type and name
+func (npr *NonPersistedRevision) ChildDropByName(childName string) {
+ // Extract device type
+ parts := strings.SplitN(childName, "/", 2)
+ childType := parts[0]
+
+ if childType != "" {
+ children := make([]Revision, len(npr.GetChildren(childType)))
+ copy(children, npr.GetChildren(childType))
+ for i, child := range children {
+ if child.GetName() == childName {
+ children = append(children[:i], children[i+1:]...)
+ npr.SetChildren(childType, children)
+ break
+ }
+ }
+ }
+}
+
+func (npr *NonPersistedRevision) SetLastUpdate(ts ...time.Time) {
+ npr.mutex.Lock()
+ defer npr.mutex.Unlock()
+
+ if ts != nil && len(ts) > 0 {
+ npr.lastUpdate = ts[0]
+ } else {
+ npr.lastUpdate = time.Now()
+ }
+}
+
+func (npr *NonPersistedRevision) GetLastUpdate() time.Time {
+ npr.mutex.RLock()
+ defer npr.mutex.RUnlock()
+
+ return npr.lastUpdate
+}
+
+func (npr *NonPersistedRevision) LoadFromPersistence(ctx context.Context, path string, txid string, blobs map[string]*kvstore.KVPair) []Revision {
+ // stub... required by interface
+ return nil
+}
+
+func (npr *NonPersistedRevision) SetupWatch(key string) {
+ // stub ... required by interface
+}
+
+func (npr *NonPersistedRevision) StorageDrop(txid string, includeConfig bool) {
+ // stub ... required by interface
+}
+
+func (npr *NonPersistedRevision) getVersion() int64 {
+ return -1
+}