blob: ef490d8ee222f7b01050fb4d5abb19e73be2d57d [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 */
16package model
17
18import (
19 "github.com/opencord/voltha-go/common/log"
20 "reflect"
21)
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 {
43 KeyMap1 map[reflect.Value]int
44 KeyMap2 map[reflect.Value]int
45 AddedKeys map[reflect.Value]struct{}
46 RemovedKeys map[reflect.Value]struct{}
47 ChangedKeys map[reflect.Value]struct{}
48}
49
50func newChangeAnalysis(lst1, lst2 []Revision, keyName string) *changeAnalysis {
51 changes := &changeAnalysis{}
52
53 changes.KeyMap1 = make(map[reflect.Value]int)
54 changes.KeyMap2 = make(map[reflect.Value]int)
55
56 changes.AddedKeys = make(map[reflect.Value]struct{})
57 changes.RemovedKeys = make(map[reflect.Value]struct{})
58 changes.ChangedKeys = make(map[reflect.Value]struct{})
59
60 for i, rev := range lst1 {
61 _, v := GetAttributeValue(rev.GetData(), keyName, 0)
62 changes.KeyMap1[v] = i
63 }
64 for i, rev := range lst2 {
65 _, v := GetAttributeValue(rev.GetData(), keyName, 0)
66 changes.KeyMap2[v] = i
67 }
68 for v, _ := range changes.KeyMap2 {
69 if _, ok := changes.KeyMap1[v]; !ok {
70 changes.AddedKeys[v] = struct{}{}
71 }
72 }
73 for v, _ := range changes.KeyMap1 {
74 if _, ok := changes.KeyMap2[v]; !ok {
75 changes.RemovedKeys[v] = struct{}{}
76 }
77 }
78 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
87func Merge3Way(
88 forkRev, srcRev, dstRev Revision,
89 mergeChildFunc func(Revision) Revision,
Stephane Barbarie694e2b92018-09-07 12:17:36 -040090 dryRun bool) (rev Revision, changes []ChangeTuple) {
Stephane Barbarieec0919b2018-09-05 14:14:29 -040091
92 var configChanged bool
93
94 if dstRev.GetConfig() == forkRev.GetConfig() {
95 configChanged = dstRev.GetConfig() != srcRev.GetConfig()
96 } else {
97 if dstRev.GetConfig().Hash != srcRev.GetConfig().Hash {
98 log.Error("config-collision")
99 }
100 configChanged = true
101 }
102
Stephane Barbariee16186c2018-09-11 10:46:34 -0400103 newChildren := reflect.ValueOf(dstRev.GetChildren()).Interface().(map[string][]Revision)
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400104 childrenFields := ChildrenFields(forkRev.GetData())
105
106 for fieldName, field := range childrenFields {
107 forkList := forkRev.GetChildren()[fieldName]
108 srcList := srcRev.GetChildren()[fieldName]
109 dstList := dstRev.GetChildren()[fieldName]
110
Stephane Barbariee16186c2018-09-11 10:46:34 -0400111 if revisionsAreEqual(dstList, srcList) {
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400112 for _, rev := range srcList {
113 mergeChildFunc(rev)
114 }
115 continue
116 }
117
118 if field.Key == "" {
119 if revisionsAreEqual(dstList, forkList) {
120 if !revisionsAreEqual(srcList, forkList) {
121 log.Error("we should not be here")
122 } else {
123 for _, rev := range srcList {
124 newChildren[fieldName] = append(newChildren[fieldName], mergeChildFunc(rev))
125 }
126 if field.IsContainer {
Stephane Barbarie694e2b92018-09-07 12:17:36 -0400127 changes = append(
128 changes, ChangeTuple{POST_LISTCHANGE,
Stephane Barbariee16186c2018-09-11 10:46:34 -0400129 NewOperationContext("", nil, fieldName, "")},
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400130 )
131 }
132 }
133 } else {
134 if !revisionsAreEqual(srcList, forkList) {
135 log.Error("cannot merge - single child node or un-keyed children list has changed")
136 }
137 }
138 } else {
139 if revisionsAreEqual(dstList, forkList) {
140 src := newChangeAnalysis(forkList, srcList, field.Key)
141
Stephane Barbariee16186c2018-09-11 10:46:34 -0400142 newList := reflect.ValueOf(srcList).Interface().([]Revision)
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400143
144 for key, _ := range src.AddedKeys {
Stephane Barbariee16186c2018-09-11 10:46:34 -0400145 idx := src.KeyMap2[key]
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400146 newRev := mergeChildFunc(newList[idx])
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400147
Stephane Barbariee16186c2018-09-11 10:46:34 -0400148 // FIXME: newRev may come back as nil... exclude those entries for now
149 if newRev != nil {
150 newList[idx] = newRev
151 changes = append(changes, ChangeTuple{POST_ADD, newRev.GetData()})
152 }
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400153 }
154 for key, _ := range src.RemovedKeys {
155 oldRev := forkList[src.KeyMap1[key]]
Stephane Barbariee16186c2018-09-11 10:46:34 -0400156 changes = append(changes, ChangeTuple{POST_REMOVE, oldRev.GetData()})
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400157 }
158 for key, _ := range src.ChangedKeys {
159 idx := src.KeyMap2[key]
160 newRev := mergeChildFunc(newList[idx])
Stephane Barbariee16186c2018-09-11 10:46:34 -0400161
162 // FIXME: newRev may come back as nil... exclude those entries for now
163 if newRev != nil {
164 newList[idx] = newRev
165 }
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400166 }
167
168 newChildren[fieldName] = newList
169 } else {
170 src := newChangeAnalysis(forkList, srcList, field.Key)
171 dst := newChangeAnalysis(forkList, dstList, field.Key)
172
Stephane Barbariee16186c2018-09-11 10:46:34 -0400173 newList := reflect.ValueOf(dstList).Interface().([]Revision)
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400174
175 for key, _ := range src.AddedKeys {
176 if _, exists := dst.AddedKeys[key]; exists {
177 childDstRev := dstList[dst.KeyMap2[key]]
178 childSrcRev := srcList[src.KeyMap2[key]]
179 if childDstRev.GetHash() == childSrcRev.GetHash() {
180 mergeChildFunc(childDstRev)
181 } else {
182 log.Error("conflict error - revision has been added is different")
183 }
184 } else {
185 newRev := mergeChildFunc(srcList[src.KeyMap2[key]])
186 newList = append(newList, newRev)
Stephane Barbariee16186c2018-09-11 10:46:34 -0400187 changes = append(changes, ChangeTuple{POST_ADD, newRev.GetData()})
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400188 }
189 }
190 for key, _ := range src.ChangedKeys {
191 if _, removed := dst.RemovedKeys[key]; removed {
192 log.Error("conflict error - revision has been removed")
193 } else if _, changed := dst.ChangedKeys[key]; changed {
194 childDstRev := dstList[dst.KeyMap2[key]]
195 childSrcRev := srcList[src.KeyMap2[key]]
196 if childDstRev.GetHash() == childSrcRev.GetHash() {
197 mergeChildFunc(childSrcRev)
198 } else if childDstRev.GetConfig().Hash != childSrcRev.GetConfig().Hash {
199 log.Error("conflict error - revision has been changed and is different")
200 } else {
201 newRev := mergeChildFunc(srcList[src.KeyMap2[key]])
202 newList[dst.KeyMap2[key]] = newRev
203 }
204 } else {
205 newRev := mergeChildFunc(srcList[src.KeyMap2[key]])
206 newList[dst.KeyMap2[key]] = newRev
207 }
208 }
209
210 // TODO: how do i sort this map in reverse order?
211 for key, _ := range src.RemovedKeys {
212 if _, changed := dst.ChangedKeys[key]; changed {
213 log.Error("conflict error - revision has changed")
214 }
215 if _, removed := dst.ChangedKeys[key]; !removed {
216 dstIdx := dst.KeyMap2[key]
217 oldRev := newList[dstIdx]
218
219 copy(newList[dstIdx:], newList[dstIdx+1:])
220 newList[len(newList)-1] = nil
221 newList = newList[:len(newList)-1]
222
Stephane Barbariee16186c2018-09-11 10:46:34 -0400223 changes = append(changes, ChangeTuple{POST_REMOVE, oldRev.GetData()})
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400224 }
225 }
226
227 newChildren[fieldName] = newList
228
229 }
230 }
231 }
232
233 if !dryRun {
234 if configChanged {
235 rev = srcRev
236 } else {
237 rev = dstRev
238 }
239
240 rev = rev.UpdateAllChildren(newChildren, dstRev.GetBranch())
241
242 if configChanged {
Stephane Barbariee16186c2018-09-11 10:46:34 -0400243 changes = append(changes, ChangeTuple{POST_UPDATE, rev.GetData()})
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400244 }
245 return rev, changes
Stephane Barbariee16186c2018-09-11 10:46:34 -0400246 } else {
247 return nil, nil
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400248
Stephane Barbariee16186c2018-09-11 10:46:34 -0400249 }
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400250}