blob: b230076afbd4145428ef96d133a544accd560a24 [file] [log] [blame]
Stephane Barbarieec0919b2018-09-05 14:14: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 Barbarieec0919b2018-09-05 14:14:29 -040017package model
18
19import (
20 "github.com/opencord/voltha-go/common/log"
Stephane Barbarieec0919b2018-09-05 14:14:29 -040021)
22
23func revisionsAreEqual(a, b []Revision) bool {
24 // If one is nil, the other must also be nil.
25 if (a == nil) != (b == nil) {
26 return false
27 }
28
29 if len(a) != len(b) {
30 return false
31 }
32
33 for i := range a {
34 if a[i] != b[i] {
35 return false
36 }
37 }
38
39 return true
40}
41
42type changeAnalysis struct {
Stephane Barbarie88fbe7f2018-09-25 12:25:23 -040043 KeyMap1 map[string]int
44 KeyMap2 map[string]int
45 AddedKeys map[string]struct{}
46 RemovedKeys map[string]struct{}
47 ChangedKeys map[string]struct{}
Stephane Barbarieec0919b2018-09-05 14:14:29 -040048}
49
50func newChangeAnalysis(lst1, lst2 []Revision, keyName string) *changeAnalysis {
51 changes := &changeAnalysis{}
52
Stephane Barbarie88fbe7f2018-09-25 12:25:23 -040053 changes.KeyMap1 = make(map[string]int)
54 changes.KeyMap2 = make(map[string]int)
Stephane Barbarieec0919b2018-09-05 14:14:29 -040055
Stephane Barbarie88fbe7f2018-09-25 12:25:23 -040056 changes.AddedKeys = make(map[string]struct{})
57 changes.RemovedKeys = make(map[string]struct{})
58 changes.ChangedKeys = make(map[string]struct{})
Stephane Barbarieec0919b2018-09-05 14:14:29 -040059
60 for i, rev := range lst1 {
61 _, v := GetAttributeValue(rev.GetData(), keyName, 0)
Stephane Barbarie88fbe7f2018-09-25 12:25:23 -040062 changes.KeyMap1[v.String()] = i
Stephane Barbarieec0919b2018-09-05 14:14:29 -040063 }
64 for i, rev := range lst2 {
65 _, v := GetAttributeValue(rev.GetData(), keyName, 0)
Stephane Barbarie88fbe7f2018-09-25 12:25:23 -040066 changes.KeyMap2[v.String()] = i
Stephane Barbarieec0919b2018-09-05 14:14:29 -040067 }
Stephane Barbariedc5022d2018-11-19 15:21:44 -050068 for v := range changes.KeyMap2 {
Stephane Barbarieec0919b2018-09-05 14:14:29 -040069 if _, ok := changes.KeyMap1[v]; !ok {
70 changes.AddedKeys[v] = struct{}{}
71 }
72 }
Stephane Barbariedc5022d2018-11-19 15:21:44 -050073 for v := range changes.KeyMap1 {
Stephane Barbarieec0919b2018-09-05 14:14:29 -040074 if _, ok := changes.KeyMap2[v]; !ok {
75 changes.RemovedKeys[v] = struct{}{}
76 }
77 }
Stephane Barbariedc5022d2018-11-19 15:21:44 -050078 for v := range changes.KeyMap1 {
Stephane Barbariee16186c2018-09-11 10:46:34 -040079 if _, ok := changes.KeyMap2[v]; ok && lst1[changes.KeyMap1[v]].GetHash() != lst2[changes.KeyMap2[v]].GetHash() {
Stephane Barbarieec0919b2018-09-05 14:14:29 -040080 changes.ChangedKeys[v] = struct{}{}
81 }
82 }
83
84 return changes
85}
86
Stephane Barbariedc5022d2018-11-19 15:21:44 -050087// Merge3Way takes care of combining the revision contents of the same data set
Stephane Barbarieec0919b2018-09-05 14:14:29 -040088func Merge3Way(
89 forkRev, srcRev, dstRev Revision,
90 mergeChildFunc func(Revision) Revision,
Stephane Barbarie694e2b92018-09-07 12:17:36 -040091 dryRun bool) (rev Revision, changes []ChangeTuple) {
Stephane Barbarieec0919b2018-09-05 14:14:29 -040092
93 var configChanged bool
Stephane Barbarie88fbe7f2018-09-25 12:25:23 -040094 var revsToDiscard []Revision
Stephane Barbarieec0919b2018-09-05 14:14:29 -040095
96 if dstRev.GetConfig() == forkRev.GetConfig() {
97 configChanged = dstRev.GetConfig() != srcRev.GetConfig()
98 } else {
99 if dstRev.GetConfig().Hash != srcRev.GetConfig().Hash {
100 log.Error("config-collision")
101 }
102 configChanged = true
103 }
104
Stephane Barbarie3cb01222019-01-16 17:15:56 -0500105 //newChildren := reflect.ValueOf(dstRev.GetAllChildren()).Interface().(map[string][]Revision)
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500106 newChildren := make(map[string][]Revision)
Stephane Barbarie3cb01222019-01-16 17:15:56 -0500107 for entryName, childrenEntry := range dstRev.GetAllChildren() {
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500108 //newRev.Children[entryName] = append(newRev.Children[entryName], childrenEntry...)
109 newChildren[entryName] = make([]Revision, len(childrenEntry))
110 copy(newChildren[entryName], childrenEntry)
111 }
112
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400113 childrenFields := ChildrenFields(forkRev.GetData())
114
115 for fieldName, field := range childrenFields {
Stephane Barbarie3cb01222019-01-16 17:15:56 -0500116 forkList := forkRev.GetChildren(fieldName)
117 srcList := srcRev.GetChildren(fieldName)
118 dstList := dstRev.GetChildren(fieldName)
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400119
Stephane Barbariee16186c2018-09-11 10:46:34 -0400120 if revisionsAreEqual(dstList, srcList) {
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400121 for _, rev := range srcList {
122 mergeChildFunc(rev)
123 }
124 continue
125 }
126
127 if field.Key == "" {
128 if revisionsAreEqual(dstList, forkList) {
129 if !revisionsAreEqual(srcList, forkList) {
130 log.Error("we should not be here")
131 } else {
132 for _, rev := range srcList {
133 newChildren[fieldName] = append(newChildren[fieldName], mergeChildFunc(rev))
134 }
135 if field.IsContainer {
Stephane Barbarie694e2b92018-09-07 12:17:36 -0400136 changes = append(
137 changes, ChangeTuple{POST_LISTCHANGE,
Stephane Barbarie8c48b5c2018-10-02 09:45:17 -0400138 NewOperationContext("", nil, fieldName, ""), nil},
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400139 )
140 }
141 }
142 } else {
143 if !revisionsAreEqual(srcList, forkList) {
144 log.Error("cannot merge - single child node or un-keyed children list has changed")
145 }
146 }
147 } else {
148 if revisionsAreEqual(dstList, forkList) {
149 src := newChangeAnalysis(forkList, srcList, field.Key)
150
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500151 newList := make([]Revision, len(srcList))
152 copy(newList, srcList)
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400153
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500154 for key := range src.AddedKeys {
Stephane Barbariee16186c2018-09-11 10:46:34 -0400155 idx := src.KeyMap2[key]
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400156 newRev := mergeChildFunc(newList[idx])
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400157
Stephane Barbariee16186c2018-09-11 10:46:34 -0400158 // FIXME: newRev may come back as nil... exclude those entries for now
159 if newRev != nil {
160 newList[idx] = newRev
Stephane Barbarie8c48b5c2018-10-02 09:45:17 -0400161 changes = append(changes, ChangeTuple{POST_ADD, newList[idx].GetData(), newRev.GetData()})
Stephane Barbariee16186c2018-09-11 10:46:34 -0400162 }
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400163 }
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500164 for key := range src.RemovedKeys {
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400165 oldRev := forkList[src.KeyMap1[key]]
Stephane Barbarie88fbe7f2018-09-25 12:25:23 -0400166 revsToDiscard = append(revsToDiscard, oldRev)
Stephane Barbarie8c48b5c2018-10-02 09:45:17 -0400167 changes = append(changes, ChangeTuple{POST_REMOVE, oldRev.GetData(), nil})
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400168 }
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500169 for key := range src.ChangedKeys {
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400170 idx := src.KeyMap2[key]
171 newRev := mergeChildFunc(newList[idx])
Stephane Barbariee16186c2018-09-11 10:46:34 -0400172
173 // FIXME: newRev may come back as nil... exclude those entries for now
174 if newRev != nil {
175 newList[idx] = newRev
176 }
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400177 }
178
Stephane Barbarie88fbe7f2018-09-25 12:25:23 -0400179 if !dryRun {
180 newChildren[fieldName] = newList
181 }
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400182 } else {
183 src := newChangeAnalysis(forkList, srcList, field.Key)
184 dst := newChangeAnalysis(forkList, dstList, field.Key)
185
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500186 newList := make([]Revision, len(dstList))
187 copy(newList, dstList)
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400188
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500189 for key := range src.AddedKeys {
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400190 if _, exists := dst.AddedKeys[key]; exists {
191 childDstRev := dstList[dst.KeyMap2[key]]
192 childSrcRev := srcList[src.KeyMap2[key]]
193 if childDstRev.GetHash() == childSrcRev.GetHash() {
194 mergeChildFunc(childDstRev)
195 } else {
196 log.Error("conflict error - revision has been added is different")
197 }
198 } else {
199 newRev := mergeChildFunc(srcList[src.KeyMap2[key]])
200 newList = append(newList, newRev)
Stephane Barbarie8c48b5c2018-10-02 09:45:17 -0400201 changes = append(changes, ChangeTuple{POST_ADD, srcList[src.KeyMap2[key]], newRev.GetData()})
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400202 }
203 }
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500204 for key := range src.ChangedKeys {
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400205 if _, removed := dst.RemovedKeys[key]; removed {
206 log.Error("conflict error - revision has been removed")
207 } else if _, changed := dst.ChangedKeys[key]; changed {
208 childDstRev := dstList[dst.KeyMap2[key]]
209 childSrcRev := srcList[src.KeyMap2[key]]
210 if childDstRev.GetHash() == childSrcRev.GetHash() {
211 mergeChildFunc(childSrcRev)
212 } else if childDstRev.GetConfig().Hash != childSrcRev.GetConfig().Hash {
213 log.Error("conflict error - revision has been changed and is different")
214 } else {
215 newRev := mergeChildFunc(srcList[src.KeyMap2[key]])
216 newList[dst.KeyMap2[key]] = newRev
217 }
218 } else {
219 newRev := mergeChildFunc(srcList[src.KeyMap2[key]])
220 newList[dst.KeyMap2[key]] = newRev
221 }
222 }
223
224 // TODO: how do i sort this map in reverse order?
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500225 for key := range src.RemovedKeys {
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400226 if _, changed := dst.ChangedKeys[key]; changed {
227 log.Error("conflict error - revision has changed")
228 }
Stephane Barbarie88fbe7f2018-09-25 12:25:23 -0400229 if _, removed := dst.RemovedKeys[key]; !removed {
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400230 dstIdx := dst.KeyMap2[key]
231 oldRev := newList[dstIdx]
Stephane Barbarie88fbe7f2018-09-25 12:25:23 -0400232 revsToDiscard = append(revsToDiscard, oldRev)
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400233
234 copy(newList[dstIdx:], newList[dstIdx+1:])
235 newList[len(newList)-1] = nil
236 newList = newList[:len(newList)-1]
237
Stephane Barbarie8c48b5c2018-10-02 09:45:17 -0400238 changes = append(changes, ChangeTuple{POST_REMOVE, oldRev.GetData(), nil})
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400239 }
240 }
241
Stephane Barbarie88fbe7f2018-09-25 12:25:23 -0400242 if !dryRun {
243 newChildren[fieldName] = newList
244 }
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400245 }
246 }
247 }
248
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500249 if !dryRun && len(newChildren) > 0{
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400250 if configChanged {
251 rev = srcRev
252 } else {
253 rev = dstRev
254 }
255
Stephane Barbarie88fbe7f2018-09-25 12:25:23 -0400256 for _, discarded := range revsToDiscard {
257 discarded.Drop("", true)
258 }
259
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500260 dstRev.GetBranch().GetLatest().Drop("", configChanged)
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400261 rev = rev.UpdateAllChildren(newChildren, dstRev.GetBranch())
262
263 if configChanged {
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500264 // FIXME: what type of previous/latest data do we want to show? Specific node or Root
265 changes = append(changes, ChangeTuple{POST_UPDATE, dstRev.GetBranch().GetLatest().GetData(), rev.GetData()})
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400266 }
267 return rev, changes
Stephane Barbariee16186c2018-09-11 10:46:34 -0400268 }
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500269
270 return nil, nil
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400271}