blob: 69db753eb16f4e68e58fa47632f6f63c28d5f89f [file] [log] [blame]
khenaidoobf6e7bb2018-08-14 22:27:29 -04001/*
2 * Copyright 2018-present Open Networking Foundation
3
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7
8 * http://www.apache.org/licenses/LICENSE-2.0
9
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
Stephane Barbariedc5022d2018-11-19 15:21:44 -050016
Stephane Barbarie4a2564d2018-07-26 11:02:58 -040017package model
18
19import (
20 "bytes"
21 "compress/gzip"
Stephane Barbarie4a2564d2018-07-26 11:02:58 -040022 "github.com/golang/protobuf/proto"
Stephane Barbarie88fbe7f2018-09-25 12:25:23 -040023 "github.com/opencord/voltha-go/common/log"
Stephane Barbarie4a2564d2018-07-26 11:02:58 -040024 "reflect"
Stephane Barbariedc5022d2018-11-19 15:21:44 -050025 "runtime/debug"
Stephane Barbarie1ab43272018-12-08 21:42:13 -050026 "strings"
Stephane Barbariedc5022d2018-11-19 15:21:44 -050027 "sync"
Stephane Barbarie4a2564d2018-07-26 11:02:58 -040028)
29
Stephane Barbariedc5022d2018-11-19 15:21:44 -050030// PersistedRevision holds information of revision meant to be saved in a persistent storage
Stephane Barbarie4a2564d2018-07-26 11:02:58 -040031type PersistedRevision struct {
Stephane Barbariedc5022d2018-11-19 15:21:44 -050032 mutex sync.RWMutex
Stephane Barbarieec0919b2018-09-05 14:14:29 -040033 Revision
Stephane Barbarie4a2564d2018-07-26 11:02:58 -040034 Compress bool
35 kvStore *Backend
36}
37
Stephane Barbariedc5022d2018-11-19 15:21:44 -050038// NewPersistedRevision creates a new instance of a PersistentRevision structure
Stephane Barbarieec0919b2018-09-05 14:14:29 -040039func NewPersistedRevision(branch *Branch, data interface{}, children map[string][]Revision) Revision {
Stephane Barbarie4a2564d2018-07-26 11:02:58 -040040 pr := &PersistedRevision{}
Stephane Barbariedc5022d2018-11-19 15:21:44 -050041 pr.kvStore = branch.Node.GetRoot().KvStore
42 pr.Revision = NewNonPersistedRevision(nil, branch, data, children)
Stephane Barbarie4a2564d2018-07-26 11:02:58 -040043 return pr
44}
45
Stephane Barbariedc5022d2018-11-19 15:21:44 -050046// Finalize is responsible of saving the revision in the persistent storage
Stephane Barbarie1ab43272018-12-08 21:42:13 -050047func (pr *PersistedRevision) Finalize(skipOnExist bool) {
48 pr.store(skipOnExist)
Stephane Barbarie4a2564d2018-07-26 11:02:58 -040049}
50
51type revData struct {
52 Children map[string][]string
53 Config string
54}
55
Stephane Barbarie1ab43272018-12-08 21:42:13 -050056func (pr *PersistedRevision) store(skipOnExist bool) {
Stephane Barbarie88fbe7f2018-09-25 12:25:23 -040057 if pr.GetBranch().Txid != "" {
58 return
59 }
Stephane Barbarie4a2564d2018-07-26 11:02:58 -040060
Stephane Barbarie1ab43272018-12-08 21:42:13 -050061 if pair, _ := pr.kvStore.Get(pr.GetHash()); pair != nil && skipOnExist {
Stephane Barbariedc5022d2018-11-19 15:21:44 -050062 log.Debugf("Config already exists - hash:%s, stack: %s", pr.GetConfig().Hash, string(debug.Stack()))
Stephane Barbarie4a2564d2018-07-26 11:02:58 -040063 return
Stephane Barbarie1ab43272018-12-08 21:42:13 -050064 //}
Stephane Barbarie4a2564d2018-07-26 11:02:58 -040065 }
Stephane Barbarie1ab43272018-12-08 21:42:13 -050066
Stephane Barbarieec0919b2018-09-05 14:14:29 -040067 if blob, err := proto.Marshal(pr.GetConfig().Data.(proto.Message)); err != nil {
Stephane Barbarie4a2564d2018-07-26 11:02:58 -040068 // TODO report error
69 } else {
70 if pr.Compress {
71 var b bytes.Buffer
72 w := gzip.NewWriter(&b)
73 w.Write(blob)
74 w.Close()
75 blob = b.Bytes()
76 }
Stephane Barbariedc5022d2018-11-19 15:21:44 -050077
Stephane Barbarie1ab43272018-12-08 21:42:13 -050078 if err := pr.kvStore.Put(pr.GetHash(), blob); err != nil {
Stephane Barbariedc5022d2018-11-19 15:21:44 -050079 log.Warnf("Problem storing revision config - error: %s, hash: %s, data: %+v", err.Error(),
Stephane Barbarie1ab43272018-12-08 21:42:13 -050080 pr.GetHash(),
Stephane Barbariedc5022d2018-11-19 15:21:44 -050081 pr.GetConfig().Data)
82 } else {
Stephane Barbarie1ab43272018-12-08 21:42:13 -050083 log.Debugf("Stored config - hash:%s, blob: %+v, stack: %s", pr.GetHash(), pr.GetConfig().Data,
Stephane Barbariedc5022d2018-11-19 15:21:44 -050084 string(debug.Stack()))
85 }
Stephane Barbarie4a2564d2018-07-26 11:02:58 -040086 }
87}
88
Stephane Barbarie1ab43272018-12-08 21:42:13 -050089func (pr *PersistedRevision) LoadFromPersistence(path string, txid string) []Revision {
90 var response []Revision
91 var rev Revision
Stephane Barbarie4a2564d2018-07-26 11:02:58 -040092
Stephane Barbarie1ab43272018-12-08 21:42:13 -050093 rev = pr
94
95 if pr.kvStore != nil {
96 blobMap, _ := pr.kvStore.List(path)
97
98 partition := strings.SplitN(path, "/", 2)
99 name := partition[0]
100
101 if len(partition) < 2 {
102 path = ""
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400103 } else {
Stephane Barbarie1ab43272018-12-08 21:42:13 -0500104 path = partition[1]
105 }
106
107 field := ChildrenFields(rev.GetBranch().Node.Type)[name]
108
109 if field.IsContainer {
110 for _, blob := range blobMap {
111 output := blob.Value.([]byte)
112
113 data := reflect.New(field.ClassType.Elem())
114
115 if err := proto.Unmarshal(output, data.Interface().(proto.Message)); err != nil {
116 // TODO report error
117 } else {
118
119 var children []Revision
120
121 if path == "" {
122 if field.Key != "" {
123 // e.g. /logical_devices/abcde --> path="" name=logical_devices key=abcde
124 if field.Key != "" {
125 children = make([]Revision, len(rev.GetChildren()[name]))
126 copy(children, rev.GetChildren()[name])
127
128 _, key := GetAttributeValue(data.Interface(), field.Key, 0)
129
130 childRev := rev.GetBranch().Node.MakeNode(data.Interface(), txid).Latest(txid)
131 childRev.SetHash(name + "/" + key.String())
132 children = append(children, childRev)
133 rev = rev.UpdateChildren(name, children, rev.GetBranch())
134
135 rev.GetBranch().Node.makeLatest(rev.GetBranch(), rev, nil)
136
137 response = append(response, childRev)
138 continue
139 }
140 }
141 } else if field.Key != "" {
142 // e.g. /logical_devices/abcde/flows/vwxyz --> path=abcde/flows/vwxyz
143
144 partition := strings.SplitN(path, "/", 2)
145 key := partition[0]
146 if len(partition) < 2 {
147 path = ""
148 } else {
149 path = partition[1]
150 }
151 keyValue := field.KeyFromStr(key)
152
153 children = make([]Revision, len(rev.GetChildren()[name]))
154 copy(children, rev.GetChildren()[name])
155
156 idx, childRev := rev.GetBranch().Node.findRevByKey(children, field.Key, keyValue)
157
158 newChildRev := childRev.LoadFromPersistence(path, txid)
159
160 children[idx] = newChildRev[0]
161
162 rev := rev.UpdateChildren(name, rev.GetChildren()[name], rev.GetBranch())
163 rev.GetBranch().Node.makeLatest(rev.GetBranch(), rev, nil)
164
165 response = append(response, newChildRev[0])
166 continue
167 }
168 }
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400169 }
170 }
171 }
Stephane Barbarie1ab43272018-12-08 21:42:13 -0500172 return response
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400173}
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400174
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500175// UpdateData modifies the information in the data model and saves it in the persistent storage
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400176func (pr *PersistedRevision) UpdateData(data interface{}, branch *Branch) Revision {
177 newNPR := pr.Revision.UpdateData(data, branch)
178
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500179 log.Debugf("Updating data %+v", data)
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400180 newPR := &PersistedRevision{
181 Revision: newNPR,
182 Compress: pr.Compress,
khenaidoob9203542018-09-17 22:56:37 -0400183 kvStore: pr.kvStore,
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400184 }
185
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400186 return newPR
187}
188
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500189// UpdateChildren modifies the children of a revision and of a specific component and saves it in the persistent storage
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400190func (pr *PersistedRevision) UpdateChildren(name string, children []Revision, branch *Branch) Revision {
191 newNPR := pr.Revision.UpdateChildren(name, children, branch)
192
193 newPR := &PersistedRevision{
194 Revision: newNPR,
195 Compress: pr.Compress,
khenaidoob9203542018-09-17 22:56:37 -0400196 kvStore: pr.kvStore,
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400197 }
198
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400199 return newPR
200}
201
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500202// UpdateAllChildren modifies the children for all components of a revision and saves it in the peristent storage
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400203func (pr *PersistedRevision) UpdateAllChildren(children map[string][]Revision, branch *Branch) Revision {
204 newNPR := pr.Revision.UpdateAllChildren(children, branch)
205
206 newPR := &PersistedRevision{
207 Revision: newNPR,
208 Compress: pr.Compress,
khenaidoob9203542018-09-17 22:56:37 -0400209 kvStore: pr.kvStore,
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400210 }
211
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400212 return newPR
213}
Stephane Barbarie88fbe7f2018-09-25 12:25:23 -0400214
215// Drop takes care of eliminating a revision hash that is no longer needed
216// and its associated config when required
217func (pr *PersistedRevision) Drop(txid string, includeConfig bool) {
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500218 pr.mutex.Lock()
219 defer pr.mutex.Unlock()
Stephane Barbarie88fbe7f2018-09-25 12:25:23 -0400220 if pr.kvStore != nil && txid == "" {
221 if includeConfig {
222 log.Debugf("removing rev config - hash: %s", pr.GetConfig().Hash)
223 if err := pr.kvStore.Delete(pr.GetConfig().Hash); err != nil {
224 log.Errorf(
225 "failed to remove rev config - hash: %s, err: %s",
226 pr.GetConfig().Hash,
227 err.Error(),
228 )
229 }
230 }
231
232 log.Debugf("removing rev - hash: %s", pr.GetHash())
233 if err := pr.kvStore.Delete(pr.GetHash()); err != nil {
234 log.Errorf("failed to remove rev - hash: %s, err: %s", pr.GetHash(), err.Error())
235 }
236 } else {
237 if includeConfig {
238 log.Debugf("Attempted to remove revision config:%s linked to transaction:%s", pr.GetConfig().Hash, txid)
239 }
240 log.Debugf("Attempted to remove revision:%s linked to transaction:%s", pr.GetHash(), txid)
241 }
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500242
243 pr.Revision.Drop(txid, includeConfig)
Stephane Barbarie88fbe7f2018-09-25 12:25:23 -0400244}