Convert itm-impl to use mdsal-binding-util
[genius.git] / itm / itm-impl / src / main / java / org / opendaylight / genius / itm / confighelpers / ItmTunnelAggregationHelper.java
1 /*
2  * Copyright (c) 2017 Hewlett Packard Enterprise, Co. 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.genius.itm.confighelpers;
9
10 import static org.opendaylight.mdsal.binding.util.Datastore.CONFIGURATION;
11 import static org.opendaylight.mdsal.binding.util.Datastore.OPERATIONAL;
12
13 import com.google.common.util.concurrent.ListenableFuture;
14 import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
15 import java.util.ArrayList;
16 import java.util.Collections;
17 import java.util.List;
18 import java.util.Map;
19 import java.util.Objects;
20 import java.util.Optional;
21 import java.util.concurrent.Callable;
22 import java.util.concurrent.ExecutionException;
23 import javax.inject.Inject;
24 import javax.inject.Singleton;
25 import org.eclipse.jdt.annotation.Nullable;
26 import org.opendaylight.genius.interfacemanager.globals.IfmConstants;
27 import org.opendaylight.genius.interfacemanager.globals.InterfaceInfo;
28 import org.opendaylight.genius.interfacemanager.globals.InterfaceInfo.InterfaceAdminState;
29 import org.opendaylight.genius.interfacemanager.interfaces.IInterfaceManager;
30 import org.opendaylight.genius.itm.impl.ItmUtils;
31 import org.opendaylight.genius.mdsalutil.ActionInfo;
32 import org.opendaylight.genius.mdsalutil.MDSALUtil;
33 import org.opendaylight.genius.mdsalutil.interfaces.IMdsalApiManager;
34 import org.opendaylight.infrautils.jobcoordinator.JobCoordinator;
35 import org.opendaylight.mdsal.binding.api.DataBroker;
36 import org.opendaylight.mdsal.binding.util.Datastore.Configuration;
37 import org.opendaylight.mdsal.binding.util.Datastore.Operational;
38 import org.opendaylight.mdsal.binding.util.ManagedNewTransactionRunner;
39 import org.opendaylight.mdsal.binding.util.ManagedNewTransactionRunnerImpl;
40 import org.opendaylight.mdsal.binding.util.TypedReadTransaction;
41 import org.opendaylight.mdsal.binding.util.TypedReadWriteTransaction;
42 import org.opendaylight.mdsal.binding.util.TypedWriteTransaction;
43 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.iana._if.type.rev170119.Tunnel;
44 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.state.Interface;
45 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.state.Interface.AdminStatus;
46 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.state.Interface.OperStatus;
47 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.state.InterfaceBuilder;
48 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.state.InterfaceKey;
49 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.meta.rev160406.InterfaceChildInfo;
50 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.meta.rev160406._interface.child.info.InterfaceParentEntry;
51 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.meta.rev160406._interface.child.info.InterfaceParentEntryKey;
52 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.meta.rev160406._interface.child.info._interface.parent.entry.InterfaceChildEntry;
53 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.meta.rev160406._interface.child.info._interface.parent.entry.InterfaceChildEntryBuilder;
54 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.meta.rev160406._interface.child.info._interface.parent.entry.InterfaceChildEntryKey;
55 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.rev160406.IfTunnel;
56 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.rev160406.ParentRefs;
57 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.rev160406.TunnelTypeBase;
58 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.rev160406.TunnelTypeLogicalGroup;
59 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.rev160406.TunnelTypeVxlan;
60 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.config.rev160406.ItmConfig;
61 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.config.rev160406.itm.config.TunnelAggregation;
62 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.config.rev160406.itm.config.TunnelAggregationKey;
63 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.op.rev160406.tunnel.list.InternalTunnel;
64 import org.opendaylight.yang.gen.v1.urn.opendaylight.group.types.rev131018.GroupTypes;
65 import org.opendaylight.yang.gen.v1.urn.opendaylight.group.types.rev131018.group.buckets.Bucket;
66 import org.opendaylight.yang.gen.v1.urn.opendaylight.group.types.rev131018.groups.Group;
67 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
68 import org.opendaylight.yangtools.yang.common.Uint64;
69 import org.slf4j.Logger;
70 import org.slf4j.LoggerFactory;
71
72 @Singleton
73 public class ItmTunnelAggregationHelper {
74
75     public static final int ADD_TUNNEL = 0;
76     public static final int DEL_TUNNEL = 1;
77     public static final int MOD_TUNNEL = 2;
78     public static final int MOD_GROUP_TUNNEL = 3;
79     public static final int DEFAULT_WEIGHT = 1;
80     public static final long INVALID_ID = 0;
81
82     private static final Logger LOG = LoggerFactory.getLogger(ItmTunnelAggregationHelper.class);
83     private static boolean tunnelAggregationEnabled;
84
85     private final IInterfaceManager interfaceManager;
86     private final IMdsalApiManager mdsalManager;
87     private final JobCoordinator jobCoordinator;
88
89     @Inject
90     public ItmTunnelAggregationHelper(final IInterfaceManager interfaceMngr,
91                                       final IMdsalApiManager mdsalMngr,
92                                       final ItmConfig itmConfig,
93                                       final JobCoordinator jobCoordinator) {
94         this.interfaceManager = interfaceMngr;
95         this.mdsalManager = mdsalMngr;
96         this.jobCoordinator = jobCoordinator;
97         initTunnelAggregationConfig(itmConfig);
98     }
99
100     public static boolean isTunnelAggregationEnabled() {
101         return tunnelAggregationEnabled;
102     }
103
104     public void createLogicalTunnelSelectGroup(TypedWriteTransaction<Configuration> tx,
105             Uint64 srcDpnId, String interfaceName, int lportTag) {
106         Group group = prepareLogicalTunnelSelectGroup(interfaceName, lportTag);
107         LOG.debug("MULTIPLE_VxLAN_TUNNELS: group id {} installed for {} srcDpnId {}",
108                 group.getGroupId().getValue(), interfaceName, srcDpnId);
109         mdsalManager.addGroup(tx, srcDpnId, group);
110     }
111
112     public void updateLogicalTunnelSelectGroup(InterfaceParentEntry entry, DataBroker broker) {
113         String logicTunnelName = entry.getParentInterface();
114         org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508
115                     .interfaces.Interface ifaceConfig = ItmUtils.getInterface(logicTunnelName, interfaceManager);
116         if (ifaceConfig == null || !ifaceConfig.getType().isAssignableFrom(Tunnel.class)) {
117             return;
118         }
119         IfTunnel ifTunnel = ifaceConfig.augmentation(IfTunnel.class);
120         if (!ifTunnel.getTunnelInterfaceType().isAssignableFrom(TunnelTypeLogicalGroup.class)) {
121             return;
122         }
123         LOG.debug("MULTIPLE_VxLAN_TUNNELS: updateLogicalTunnelSelectGroup name {}", logicTunnelName);
124         TunnelAggregationUpdateWorker worker =
125                 new TunnelAggregationUpdateWorker(null, null, ifaceConfig, entry, MOD_GROUP_TUNNEL, broker);
126         jobCoordinator.enqueueJob(logicTunnelName, worker);
127     }
128
129     public void updateLogicalTunnelState(Interface ifaceState, int tunnelAction, DataBroker broker) {
130         updateLogicalTunnelState(null, ifaceState, tunnelAction, broker);
131     }
132
133     public void updateLogicalTunnelState(Interface ifStateOrigin, Interface ifStateUpdated,
134                                                 int tunnelAction, DataBroker broker) {
135         if (!tunnelAggregationEnabled || ifStateUpdated == null) {
136             LOG.debug("MULTIPLE_VxLAN_TUNNELS: updateLogicalTunnelState - wrong configuration -"
137                     + " tunnelAggregationEnabled {} ifStateUpdated {}", tunnelAggregationEnabled, ifStateUpdated);
138             return;
139         }
140         String ifName = ifStateUpdated.getName();
141         org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.Interface iface =
142                 ItmUtils.getInterface(ifName, interfaceManager);
143         IfTunnel ifTunnel = iface != null ? iface.augmentation(IfTunnel.class) : null;
144         if (iface == null || ifTunnel == null) {
145             LOG.debug("MULTIPLE_VxLAN_TUNNELS: updateLogicalTunnelState - not tunnel interface {}", ifName);
146             return;
147         }
148         String logicTunnelName = null;
149         if (ifTunnel.getTunnelInterfaceType().isAssignableFrom(TunnelTypeLogicalGroup.class)) {
150             logicTunnelName = ifStateUpdated.getName();
151         } else {
152             ParentRefs parentRefs = iface.augmentation(ParentRefs.class);
153             if (ifTunnel.getTunnelInterfaceType().isAssignableFrom(TunnelTypeVxlan.class) && parentRefs != null) {
154                 logicTunnelName = parentRefs.getParentInterface();
155             }
156         }
157         if (logicTunnelName != null) {
158             TunnelAggregationUpdateWorker worker =
159                     new TunnelAggregationUpdateWorker(ifStateOrigin, ifStateUpdated, iface, null, tunnelAction, broker);
160             jobCoordinator.enqueueJob(logicTunnelName, worker);
161         }
162     }
163
164     private void initTunnelAggregationConfig(ItmConfig itmConfig) {
165         // Load balancing of VxLAN feature is guarded by a global configuration option in the ITM,
166         // only when the feature is enabled, the logical tunnel interfaces should be created.
167         boolean tunnelAggregationConfigEnabled = false;
168         Map<TunnelAggregationKey, TunnelAggregation> tunnelsConfig = itmConfig != null
169                 ? itmConfig.getTunnelAggregation() : null;
170         if (tunnelsConfig != null) {
171             for (TunnelAggregation tnlCfg : tunnelsConfig.values()) {
172                 Class<? extends TunnelTypeBase> tunType = ItmUtils.getTunnelType(tnlCfg.key().getTunnelType());
173                 if (tunType.isAssignableFrom(TunnelTypeVxlan.class)) {
174                     tunnelAggregationConfigEnabled = tnlCfg.isEnabled();
175                     LOG.info("MULTIPLE_VxLAN_TUNNELS: tunnelAggregationEnabled {}", tunnelAggregationConfigEnabled);
176                     break;
177                 }
178             }
179         }
180         tunnelAggregationEnabled = tunnelAggregationConfigEnabled;
181     }
182
183     private Group prepareLogicalTunnelSelectGroup(String interfaceName, int lportTag) {
184         long groupId = interfaceManager.getLogicalTunnelSelectGroupId(lportTag);
185         return MDSALUtil.buildGroup(groupId, interfaceName, GroupTypes.GroupSelect,
186                                     MDSALUtil.buildBucketLists(Collections.emptyList()));
187     }
188
189     private Bucket createBucket(String interfaceName, IfTunnel ifTunnel, int bucketId, int portNumber) {
190         List<ActionInfo> listActionInfo = interfaceManager.getInterfaceEgressActions(interfaceName);
191         if (listActionInfo == null) {
192             listActionInfo = Collections.emptyList();
193         }
194         if (listActionInfo.isEmpty()) {
195             LOG.warn("MULTIPLE_VxLAN_TUNNELS: could not build Egress bucket for {}", interfaceName);
196         }
197         Integer portWeight = ifTunnel.getWeight() != null ? ifTunnel.getWeight().toJava() : DEFAULT_WEIGHT;
198         return MDSALUtil.buildBucket(MDSALUtil.buildActions(listActionInfo), portWeight, bucketId,
199                                      portNumber, MDSALUtil.WATCH_GROUP);
200     }
201
202     @SuppressFBWarnings(value = "UPM_UNCALLED_PRIVATE_METHOD",
203             justification = "https://github.com/spotbugs/spotbugs/issues/811")
204     private void updateTunnelAggregationGroup(TypedWriteTransaction<Configuration> tx,
205             InterfaceParentEntry parentEntry) {
206         String logicTunnelName = parentEntry.getParentInterface();
207         InternalTunnel logicInternalTunnel = ItmUtils.ITM_CACHE.getInternalTunnel(logicTunnelName);
208         if (logicInternalTunnel == null) {
209             LOG.debug("MULTIPLE_VxLAN_TUNNELS: {} not found in internal tunnels list", logicTunnelName);
210             return;
211         }
212         InterfaceInfo ifLogicTunnel = interfaceManager.getInterfaceInfoFromOperationalDataStore(logicTunnelName);
213         long groupId = ifLogicTunnel != null
214                 ? interfaceManager.getLogicalTunnelSelectGroupId(ifLogicTunnel.getInterfaceTag()) : INVALID_ID;
215         Uint64 srcDpnId = logicInternalTunnel.getSourceDPN();
216         List<Bucket> listBuckets = new ArrayList<>();
217         @Nullable Map<InterfaceChildEntryKey, InterfaceChildEntry> interfaceChildEntries =
218                 parentEntry.getInterfaceChildEntry();
219         if (interfaceChildEntries == null || interfaceChildEntries.isEmpty()) {
220             LOG.debug("MULTIPLE_VxLAN_TUNNELS: empty child list in group {}", parentEntry.getParentInterface());
221             return;
222         }
223         for (InterfaceChildEntry interfaceChildEntry : interfaceChildEntries.values()) {
224             String curChildName = interfaceChildEntry.getChildInterface();
225             org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.Interface
226                         childIface = ItmUtils.getInterface(curChildName, interfaceManager);
227             IfTunnel ifTunnel = childIface != null ? childIface.augmentation(IfTunnel.class) : null;
228             if (ifTunnel == null || !ifTunnel.getTunnelInterfaceType().isAssignableFrom(TunnelTypeVxlan.class)) {
229                 LOG.debug("MULTIPLE_VxLAN_TUNNELS: not tunnel interface {} found in group {}",
230                         curChildName, logicTunnelName);
231                 continue;
232             }
233             ParentRefs parentRefs = childIface.augmentation(ParentRefs.class);
234             if (parentRefs == null) {
235                 LOG.debug("MULTIPLE_VxLAN_TUNNELS: parent refs not specified for interface {} in group {}",
236                         curChildName, logicTunnelName);
237                 continue;
238             }
239             InterfaceInfo ifInfo = interfaceManager.getInterfaceInfoFromOperationalDataStore(curChildName);
240             if (ifInfo == null) {
241                 LOG.debug("MULTIPLE_VxLAN_TUNNELS: interface state not found for {} in groupId {}",
242                         curChildName, groupId);
243                 continue;
244             }
245
246             List<InterfaceChildEntry> val = new ArrayList<>(interfaceChildEntries.values());
247             int bucketId = val.indexOf(interfaceChildEntry);
248
249             LOG.debug("MULTIPLE_VxLAN_TUNNELS: updateTunnelAggregationGroup - add bucketId {} to groupId {}",
250                     bucketId, groupId);
251             listBuckets.add(createBucket(curChildName, ifTunnel, bucketId, ifInfo.getPortNo()));
252         }
253         if (!listBuckets.isEmpty()) {
254             Group group = MDSALUtil.buildGroup(groupId, logicTunnelName, GroupTypes.GroupSelect,
255                                                MDSALUtil.buildBucketLists(listBuckets));
256             mdsalManager.addGroup(tx, srcDpnId, group);
257         }
258     }
259
260     @SuppressFBWarnings(value = "UPM_UNCALLED_PRIVATE_METHOD",
261             justification = "https://github.com/spotbugs/spotbugs/issues/811")
262     private void updateTunnelAggregationGroupBucket(Interface ifaceState, IfTunnel ifTunnel,
263                                                     ParentRefs parentRefs, InterfaceParentEntry groupParentEntry,
264                                                     int action, TypedReadWriteTransaction<Configuration> tx)
265             throws ExecutionException, InterruptedException {
266         String logicTunnelName = parentRefs.getParentInterface();
267         @Nullable Map<InterfaceChildEntryKey, InterfaceChildEntry> interfaceChildEntries =
268                 groupParentEntry.getInterfaceChildEntry();
269         if (interfaceChildEntries == null) {
270             LOG.debug("MULTIPLE_VxLAN_TUNNELS: empty child list in group {}", groupParentEntry.getParentInterface());
271             return;
272         }
273         String ifaceName = ifaceState.getName();
274         InterfaceChildEntry childEntry = new InterfaceChildEntryBuilder().setChildInterface(ifaceName)
275                 .withKey(new InterfaceChildEntryKey(ifaceName)).build();
276         List<InterfaceChildEntry> val = new ArrayList<>(interfaceChildEntries.values());
277         int bucketId = val.indexOf(childEntry);
278
279         if (bucketId == -1) {
280             LOG.debug("MULTIPLE_VxLAN_TUNNELS: wrong child id for {} in group {}", ifaceName,
281                     groupParentEntry.getParentInterface());
282             return;
283         }
284         InterfaceInfo ifLogicTunnel = interfaceManager.getInterfaceInfoFromOperationalDataStore(logicTunnelName);
285         long groupId = ifLogicTunnel != null
286                 ? interfaceManager.getLogicalTunnelSelectGroupId(ifLogicTunnel.getInterfaceTag()) : INVALID_ID;
287         if (groupId == INVALID_ID) {
288             LOG.warn("MULTIPLE_VxLAN_TUNNELS: unknown group id for logic tunnel {}", logicTunnelName);
289             return;
290         }
291         String lowerLayerIf = ifaceState.getLowerLayerIf().get(0); // openflow:dpnid:portnum
292         String[] split = lowerLayerIf.split(IfmConstants.OF_URI_SEPARATOR);
293         Uint64 srcDpnId = Uint64.valueOf(split[1]);
294         int portNumber = Integer.parseInt(split[2]);
295         if (action == ADD_TUNNEL) {
296             if (!mdsalManager.groupExists(srcDpnId, groupId)) {
297                 createLogicalTunnelSelectGroup(tx, srcDpnId, logicTunnelName, ifLogicTunnel.getInterfaceTag());
298             }
299             Bucket buckt = createBucket(ifaceName, ifTunnel, bucketId, portNumber);
300             LOG.debug("MULTIPLE_VxLAN_TUNNELS: add bucketId {} to groupId {}", bucketId, groupId);
301             mdsalManager.addBucket(tx, srcDpnId, groupId, buckt);
302         } else {
303             LOG.debug("MULTIPLE_VxLAN_TUNNELS: remove bucketId {} from groupId {}", bucketId, groupId);
304             mdsalManager.removeBucket(tx, srcDpnId, groupId, bucketId);
305         }
306     }
307
308     @SuppressFBWarnings(value = "UPM_UNCALLED_PRIVATE_METHOD",
309             justification = "https://github.com/spotbugs/spotbugs/issues/811")
310     private void updateLogicalTunnelGroupOperStatus(String logicalTunnelIfaceName, Interface ifaceState,
311             InterfaceParentEntry parentEntry, TypedReadWriteTransaction<Operational> tx)
312         throws ExecutionException, InterruptedException {
313         if (parentEntry == null) {
314             LOG.debug("MULTIPLE_VxLAN_TUNNELS: uninitialized parent entry {}", logicalTunnelIfaceName);
315             return;
316         }
317         OperStatus newOperStatus = getAggregatedOperStatus(ifaceState, parentEntry);
318         if (logicalTunnelIfaceName.equals(ifaceState.getName())) { //the current interface is logical tunnel itself
319             if (ifaceState.getOperStatus() != newOperStatus) {
320                 updateInterfaceOperStatus(logicalTunnelIfaceName, ifaceState, newOperStatus, tx);
321             }
322         } else {
323             InterfaceInfo ifLogicInfo =
324                     interfaceManager.getInterfaceInfoFromOperationalDataStore(logicalTunnelIfaceName);
325             if (isLogicalTunnelStateUpdateNeeded(newOperStatus, ifLogicInfo)) {
326                 InstanceIdentifier<Interface> id = ItmUtils.buildStateInterfaceId(logicalTunnelIfaceName);
327                 Optional<Interface> ifState = tx.read(id).get();
328                 if (ifState.isPresent()) {
329                     Interface ifStateLogicTunnel = ifState.get();
330                     updateInterfaceOperStatus(logicalTunnelIfaceName, ifStateLogicTunnel, newOperStatus, tx);
331                 }
332             }
333         }
334     }
335
336     private boolean isLogicalTunnelStateUpdateNeeded(OperStatus newOperStatus, InterfaceInfo ifLogicInfo) {
337         return ifLogicInfo != null && (ifLogicInfo.getOpState() == InterfaceInfo.InterfaceOpState.UP
338                 && newOperStatus == OperStatus.Down
339                 || ifLogicInfo.getOpState() == InterfaceInfo.InterfaceOpState.DOWN && newOperStatus == OperStatus.Up);
340     }
341
342     private OperStatus getAggregatedOperStatus(Interface ifaceState, InterfaceParentEntry parentEntry) {
343         String logicalTunnelName = parentEntry.getParentInterface();
344         if (!Objects.equals(logicalTunnelName, ifaceState.getName()) && ifaceState.getOperStatus() == OperStatus.Up) {
345             return OperStatus.Up;
346         }
347
348         @Nullable Map<InterfaceChildEntryKey, InterfaceChildEntry> interfaceChildEntries =
349                 parentEntry.getInterfaceChildEntry();
350         if (interfaceChildEntries == null || interfaceChildEntries.isEmpty()) {
351             LOG.debug("MULTIPLE_VxLAN_TUNNELS: OperStatus is Down, because of the empty child list in group {}",
352                     parentEntry.getParentInterface());
353             return OperStatus.Down;
354         }
355         for (InterfaceChildEntry interfaceChildEntry : interfaceChildEntries.values()) {
356             String curChildInterface = interfaceChildEntry.getChildInterface();
357             if (!Objects.equals(curChildInterface, ifaceState.getName())) {
358                 InterfaceInfo ifInfo = interfaceManager.getInterfaceInfoFromOperationalDataStore(curChildInterface);
359                 if (ifInfo != null && InterfaceInfo.InterfaceOpState.UP.equals(ifInfo.getOpState())) {
360                     return OperStatus.Up;
361                 }
362             }
363         }
364         return OperStatus.Down;
365     }
366
367     private void updateInterfaceOperStatus(String ifaceName, Interface ifaceState,
368                                            OperStatus st, TypedWriteTransaction<Operational> tx) {
369         LOG.debug("MULTIPLE_VxLAN_TUNNELS: update OperStatus to be {} for {}", st.toString(), ifaceName);
370         InstanceIdentifier<Interface> idLogicGroup = ItmUtils.buildStateInterfaceId(ifaceName);
371         InterfaceBuilder ifaceBuilderChild = new InterfaceBuilder(ifaceState);
372         ifaceBuilderChild.setOperStatus(st);
373         tx.mergeParentStructureMerge(idLogicGroup, ifaceBuilderChild.build());
374     }
375
376     @SuppressFBWarnings(value = "UPM_UNCALLED_PRIVATE_METHOD",
377             justification = "https://github.com/spotbugs/spotbugs/issues/811")
378     private void updateLogicalTunnelAdminStatus(String logicalTunnelName, Interface ifOrigin,
379             Interface ifUpdated, InterfaceParentEntry parentEntry, TypedWriteTransaction<Operational> tx) {
380
381         if (ifOrigin == null || ifUpdated == null || ifOrigin.getAdminStatus() == ifUpdated.getAdminStatus()) {
382             return;
383         }
384         @Nullable Map<InterfaceChildEntryKey, InterfaceChildEntry> interfaceChildEntries =
385                 parentEntry.getInterfaceChildEntry();
386         if (interfaceChildEntries == null || interfaceChildEntries.isEmpty()) {
387             LOG.debug("MULTIPLE_VxLAN_TUNNELS: empty child list in group {}", logicalTunnelName);
388             return;
389         }
390         for (InterfaceChildEntry interfaceChildEntry : interfaceChildEntries.values()) {
391             String curChildInterface = interfaceChildEntry.getChildInterface();
392             updateInterfaceAdminStatus(curChildInterface, ifUpdated.getAdminStatus(), tx);
393         }
394     }
395
396     @SuppressFBWarnings(value = "UPM_UNCALLED_PRIVATE_METHOD",
397             justification = "https://github.com/spotbugs/spotbugs/issues/811")
398     private void updateInterfaceAdminStatus(String logicalTunnelName, Interface ifState,
399         TypedWriteTransaction<Operational> tx) {
400         InterfaceInfo ifLogicTunnelInfo = interfaceManager.getInterfaceInfoFromOperationalDataStore(logicalTunnelName);
401         if (ifLogicTunnelInfo == null) {
402             return;
403         }
404         if (ifState.getAdminStatus() == AdminStatus.Up
405                 && ifLogicTunnelInfo.getAdminState() != InterfaceAdminState.ENABLED) {
406             updateInterfaceAdminStatus(ifState.getName(), AdminStatus.Down, tx);
407         }
408     }
409
410     private void updateInterfaceAdminStatus(String ifaceName, AdminStatus st, TypedWriteTransaction<Operational> tx) {
411         LOG.debug("MULTIPLE_VxLAN_TUNNELS: update AdminStatus to be {} for {}", st.toString(), ifaceName);
412         InstanceIdentifier<Interface> id = ItmUtils.buildStateInterfaceId(ifaceName);
413         InterfaceBuilder ifaceBuilderChild = new InterfaceBuilder();
414         ifaceBuilderChild.withKey(new InterfaceKey(ifaceName));
415         ifaceBuilderChild.setAdminStatus(st);
416         tx.mergeParentStructureMerge(id, ifaceBuilderChild.build());
417     }
418
419     private class TunnelAggregationUpdateWorker implements Callable<List<? extends ListenableFuture<?>>> {
420
421         private final Interface ifStateOrigin;
422         private final Interface ifStateUpdated;
423         private final ManagedNewTransactionRunner txRunner;
424         private final org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf
425                                                     .interfaces.rev140508.interfaces.Interface ifaceConfig;
426         private final int ifaceAction;
427         private final InterfaceParentEntry parentEntry;
428
429         TunnelAggregationUpdateWorker(Interface ifStateOrig, Interface ifStateUpdated,
430                 org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508
431                 .interfaces.Interface iface, InterfaceParentEntry entry, int action, DataBroker broker) {
432             this.ifStateOrigin = ifStateOrig;
433             this.ifStateUpdated = ifStateUpdated;
434             this.ifaceConfig = iface;
435             this.ifaceAction = action;
436             this.txRunner = new ManagedNewTransactionRunnerImpl(broker);
437             this.parentEntry = entry;
438         }
439
440         @Override
441         public List<? extends ListenableFuture<?>> call() {
442             List<ListenableFuture<?>> futures = new ArrayList<>();
443             futures.add(txRunner.callWithNewReadWriteTransactionAndSubmit(CONFIGURATION, confTx -> {
444                 if (ifaceAction == MOD_GROUP_TUNNEL) {
445                     updateTunnelAggregationGroup(confTx, parentEntry);
446                     return;
447                 }
448                 IfTunnel ifTunnel = ifaceConfig != null ? ifaceConfig.augmentation(IfTunnel.class) : null;
449                 if (ifTunnel == null) {
450                     LOG.debug("MULTIPLE_VxLAN_TUNNELS: not tunnel interface {}", ifaceConfig.getName());
451                     return;
452                 }
453                 if (ifTunnel.getTunnelInterfaceType().isAssignableFrom(TunnelTypeLogicalGroup.class)) {
454                     String logicTunnelIfaceName = ifStateUpdated.getName();
455                     futures.add(txRunner.callWithNewReadWriteTransactionAndSubmit(OPERATIONAL, operTx -> {
456                         final InterfaceParentEntry interfaceParentEntry =
457                             getInterfaceParentEntry(logicTunnelIfaceName, confTx);
458                         updateLogicalTunnelGroupOperStatus(logicTunnelIfaceName, ifStateUpdated,
459                             interfaceParentEntry,
460                             operTx);
461                         updateLogicalTunnelAdminStatus(logicTunnelIfaceName, ifStateOrigin, ifStateUpdated,
462                             interfaceParentEntry, operTx);
463                     }));
464                     return;
465                 }
466                 if (!ifTunnel.getTunnelInterfaceType().isAssignableFrom(TunnelTypeVxlan.class)) {
467                     LOG.debug("MULTIPLE_VxLAN_TUNNELS: wrong tunnel type {}", ifTunnel.getTunnelInterfaceType());
468                     return;
469                 }
470                 ParentRefs parentRefs = ifaceConfig.augmentation(ParentRefs.class);
471                 if (parentRefs == null) {
472                     LOG.debug("MULTIPLE_VxLAN_TUNNELS: not updated parent ref for {}", ifaceConfig.getName());
473                     return;
474                 }
475                 String logicTunnelIfaceName = parentRefs.getParentInterface();
476                 InterfaceParentEntry groupEntry = getInterfaceParentEntry(logicTunnelIfaceName, confTx);
477                 if (groupEntry == null) {
478                     LOG.debug("MULTIPLE_VxLAN_TUNNELS: not found InterfaceParentEntry for {}",
479                         logicTunnelIfaceName);
480                     return;
481                 }
482                 if (ifaceAction == ADD_TUNNEL) {
483                     futures.add(txRunner.callWithNewWriteOnlyTransactionAndSubmit(OPERATIONAL,
484                         operTx -> updateInterfaceAdminStatus(logicTunnelIfaceName, ifStateUpdated, operTx)));
485                     updateTunnelAggregationGroupBucket(ifStateUpdated, ifTunnel, parentRefs, groupEntry,
486                         ifaceAction,
487                         confTx);
488                 } else if (ifaceAction == DEL_TUNNEL) {
489                     updateTunnelAggregationGroupBucket(ifStateUpdated, ifTunnel, parentRefs, groupEntry,
490                         ifaceAction,
491                         confTx);
492                 }
493                 futures.add(txRunner.callWithNewReadWriteTransactionAndSubmit(OPERATIONAL,
494                     operTx -> updateLogicalTunnelGroupOperStatus(logicTunnelIfaceName, ifStateUpdated, groupEntry,
495                         operTx)));
496             }));
497             return futures;
498         }
499
500         private InterfaceParentEntry getInterfaceParentEntry(String logicalGroupName,
501             TypedReadTransaction<Configuration> tx) throws ExecutionException, InterruptedException {
502             InterfaceParentEntryKey interfaceParentEntryKey = new InterfaceParentEntryKey(logicalGroupName);
503             InstanceIdentifier.InstanceIdentifierBuilder<InterfaceParentEntry> intfIdBuilder =
504                     InstanceIdentifier.builder(InterfaceChildInfo.class)
505                             .child(InterfaceParentEntry.class, interfaceParentEntryKey);
506             InstanceIdentifier<InterfaceParentEntry> intfId = intfIdBuilder.build();
507             return tx.read(intfId).get().orElse(null);
508         }
509     }
510 }