Handle nullable lists in elanmanager
[netvirt.git] / elanmanager / impl / src / main / java / org / opendaylight / netvirt / elan / l2gw / ha / commands / MergeCommand.java
1 /*
2  * Copyright (c) 2016, 2017 Ericsson India Global Services Pvt Ltd. and others.  All rights reserved.
3  *
4  * This program and the accompanying materials are made available under the
5  * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6  * and is available at http://www.eclipse.org/legal/epl-v10.html
7  */
8 package org.opendaylight.netvirt.elan.l2gw.ha.commands;
9
10 import static org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType.CONFIGURATION;
11 import static org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType.OPERATIONAL;
12 import static org.opendaylight.netvirt.elan.l2gw.ha.HwvtepHAUtil.isEmptyList;
13
14 import java.io.Serializable;
15 import java.util.ArrayList;
16 import java.util.Comparator;
17 import java.util.List;
18 import java.util.Objects;
19 import java.util.stream.Collectors;
20 import javax.annotation.Nonnull;
21 import javax.annotation.Nullable;
22 import org.opendaylight.controller.md.sal.binding.api.ReadWriteTransaction;
23 import org.opendaylight.controller.md.sal.binding.api.WriteTransaction;
24 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
25 import org.opendaylight.netvirt.elan.l2gw.ha.HwvtepHAUtil;
26 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.hwvtep.rev150901.hwvtep.physical.locator.set.attributes.LocatorSet;
27 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.TpId;
28 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.Node;
29 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.node.TerminationPoint;
30 import org.opendaylight.yangtools.concepts.Builder;
31 import org.opendaylight.yangtools.yang.binding.DataObject;
32 import org.opendaylight.yangtools.yang.binding.Identifier;
33 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
34 import org.slf4j.Logger;
35 import org.slf4j.LoggerFactory;
36
37
38
39
40 public abstract class MergeCommand<T extends DataObject, Y extends Builder, Z extends DataObject>
41         extends BaseCommand<T> implements IMergeCommand<T, Y, Z> {
42
43     private static final Logger LOG = LoggerFactory.getLogger(MergeCommand.class);
44
45     public List<T> transformOpData(List<T> existingData, List<T> src, InstanceIdentifier<Node> nodePath) {
46         if (isEmptyList(src)) {
47             return new ArrayList<>();
48         }
49         List<T> added = diffOf(src, existingData);//do not add existing data again
50         return transform(nodePath, added);
51     }
52
53     public List<T> transformConfigData(List<T> updatedSrc, InstanceIdentifier<Node> nodePath) {
54         if (isEmptyList(updatedSrc)) {
55             return new ArrayList<>();//what difference returning null makes ?
56         }
57         return transform(nodePath, updatedSrc);
58     }
59
60     @Nonnull
61     public List<T> diffByKey(List<T> updated, final List<T> original) {
62         if (updated == null) {
63             return new ArrayList<>();
64         }
65         if (original == null) {
66             return new ArrayList<>(updated);
67         }
68
69         List<T> result = new ArrayList<>();
70         for (T ele : updated) {
71             boolean present = false;
72             for (T orig : original) {
73                 if (Objects.equals(getKey(ele), getKey(orig))) {
74                     present = true;
75                     break;
76                 }
77             }
78             if (!present) {
79                 result.add(ele);
80             }
81         }
82         return result;
83     }
84
85     //TODO validate the perf of the following against direct setting of the data in dst node
86     public void transformUpdate(List<T> existing,
87                                 List<T> updated,
88                                 List<T> orig,
89                                 InstanceIdentifier<Node> nodePath,
90                                 LogicalDatastoreType datastoreType,
91                                 ReadWriteTransaction tx) {
92
93         if (updated == null) {
94             updated = new ArrayList<>();
95         }
96         if (orig == null) {
97             orig = new ArrayList<>();
98         }
99         List<T> added   = new ArrayList<>(updated);
100
101         added.removeAll(orig);
102         added = diffOf(added, existing);//do not add the existing data again
103         if (added.size() > 0) {
104             for (T addedItem : added) {
105                 InstanceIdentifier<T> transformedId = generateId(nodePath, addedItem);
106                 T transformedItem = transform(nodePath, addedItem);
107                 String nodeId = transformedId.firstKeyOf(Node.class).getNodeId().getValue();
108                 LOG.trace("adding {} {} {}", getDescription(), nodeId, getKey(transformedItem));
109                 tx.put(datastoreType, transformedId, transformedItem, WriteTransaction.CREATE_MISSING_PARENTS);
110             }
111         }
112         List<T> removed = new ArrayList<>(orig);
113         removed = diffByKey(removed, updated);
114
115         List<T> removedTransformed  = new ArrayList<>();
116         for (T ele : removed) {
117             removedTransformed.add(transform(nodePath, ele));
118         }
119
120         List<T> skip = diffByKey(removedTransformed, existing);//skip the ones which are not present in cfg ds
121         removedTransformed = diffByKey(removedTransformed, skip);
122         if (removedTransformed.size() > 0) {
123             for (T removedItem : removedTransformed) {
124                 InstanceIdentifier<T> transformedId = generateId(nodePath, removedItem);
125                 String nodeId = transformedId.firstKeyOf(Node.class).getNodeId().getValue();
126                 LOG.trace("removing {} {} {}",getDescription(), nodeId, getKey(removedItem));
127                 tx.delete(datastoreType, transformedId);
128             }
129         }
130     }
131
132     public List<T> transform(InstanceIdentifier<Node> nodePath, List<T> list) {
133         return list.stream().map(t -> transform(nodePath, t)).collect(Collectors.toList());
134     }
135
136     public abstract T transform(InstanceIdentifier<Node> nodePath, T objT);
137
138     @Override
139     public void mergeOperationalData(Y dst,
140                                      Z existingData,
141                                      Z src,
142                                      InstanceIdentifier<Node> nodePath) {
143         List<T> origDstData = getData(existingData);
144         List<T> srcData = getData(src);
145         List<T> data = transformOpData(origDstData, srcData, nodePath);
146         setData(dst, data);
147         if (!isEmptyList(data)) {
148             String nodeId = nodePath.firstKeyOf(Node.class).getNodeId().getValue();
149             LOG.trace("merging op {} to {} size {}",getDescription(), nodeId, data.size());
150         }
151     }
152
153     @Override
154     public void mergeConfigData(Y dst,
155                                 Z src,
156                                 InstanceIdentifier<Node> nodePath) {
157         List<T> data        = getData(src);
158         List<T> transformed = transformConfigData(data, nodePath);
159         setData(dst, transformed);
160         if (!isEmptyList(data)) {
161             String nodeId = nodePath.firstKeyOf(Node.class).getNodeId().getValue();
162             LOG.trace("copying config {} to {} size {}",getDescription(), nodeId, data.size());
163         }
164     }
165
166     @Override
167     public void mergeConfigUpdate(Z existing,
168                                   Z updated,
169                                   Z orig,
170                                   InstanceIdentifier<Node> nodePath,
171                                   ReadWriteTransaction tx) {
172         List<T> updatedData     = getData(updated);
173         List<T> origData        = getData(orig);
174         List<T> existingData    = getData(existing);
175         transformUpdate(existingData, updatedData, origData, nodePath, CONFIGURATION, tx);
176     }
177
178     @Override
179     public void mergeOpUpdate(Z origDst,
180                               Z updatedSrc,
181                               Z origSrc,
182                               InstanceIdentifier<Node> nodePath,
183                               ReadWriteTransaction tx) {
184         List<T> updatedData     = getData(updatedSrc);
185         List<T> origData        = getData(origSrc);
186         List<T> existingData    = getData(origDst);
187         transformUpdate(existingData, updatedData, origData, nodePath, OPERATIONAL, tx);
188     }
189
190     boolean areSameSize(@Nullable List objA, @Nullable List objB) {
191         if (HwvtepHAUtil.isEmptyList(objA) && HwvtepHAUtil.isEmptyList(objB)) {
192             return true;
193         }
194         if (!HwvtepHAUtil.isEmptyList(objA) && !HwvtepHAUtil.isEmptyList(objB)) {
195             return objA.size() == objB.size();
196         }
197         return false;
198     }
199
200
201     static LocatorSetComparator locatorSetComparator = new LocatorSetComparator();
202
203     static class LocatorSetComparator implements Comparator<LocatorSet>, Serializable {
204         private static final long serialVersionUID = 1L;
205
206         @Override
207         public int compare(final LocatorSet updatedLocatorSet, final LocatorSet origLocatorSet) {
208             InstanceIdentifier<?> updatedLocatorRefIndentifier = updatedLocatorSet.getLocatorRef().getValue();
209             TpId updatedLocatorSetTpId = updatedLocatorRefIndentifier.firstKeyOf(TerminationPoint.class).getTpId();
210
211             InstanceIdentifier<?> origLocatorRefIndentifier = origLocatorSet.getLocatorRef().getValue();
212             TpId origLocatorSetTpId = origLocatorRefIndentifier.firstKeyOf(TerminationPoint.class).getTpId();
213
214             if (updatedLocatorSetTpId.equals(origLocatorSetTpId)) {
215                 return 0;
216             }
217             return 1;
218         }
219     }
220
221     @Nullable
222     public abstract List<T> getData(Z node);
223
224     public abstract void setData(Y builder, List<T> data);
225
226     public abstract InstanceIdentifier<T> generateId(InstanceIdentifier<Node> id, T node);
227
228     public abstract Identifier getKey(T data);
229
230     public abstract String getDescription();
231
232     public abstract T withoutUuid(T data);
233 }