ELAN: skip remote unicast MACs
[netvirt.git] / elanmanager / impl / src / main / java / org / opendaylight / netvirt / elan / l2gw / ha / listeners / HAOpNodeListener.java
1 /*
2  * Copyright (c) 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.listeners;
9
10 import static org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType.OPERATIONAL;
11
12 import com.google.common.base.Optional;
13 import com.google.common.base.Strings;
14
15 import java.util.ArrayList;
16 import java.util.List;
17 import java.util.Set;
18 import java.util.concurrent.ExecutionException;
19 import java.util.function.BiPredicate;
20 import java.util.function.Predicate;
21 import java.util.stream.Collectors;
22 import javax.inject.Inject;
23 import javax.inject.Singleton;
24
25 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
26 import org.opendaylight.controller.md.sal.binding.api.ReadWriteTransaction;
27 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
28 import org.opendaylight.controller.md.sal.common.api.data.ReadFailedException;
29 import org.opendaylight.netvirt.elan.l2gw.ha.HwvtepHAUtil;
30 import org.opendaylight.netvirt.elan.l2gw.ha.handlers.HAEventHandler;
31 import org.opendaylight.netvirt.elan.l2gw.ha.handlers.IHAEventHandler;
32 import org.opendaylight.netvirt.elan.l2gw.ha.handlers.NodeCopier;
33 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.hwvtep.rev150901.HwvtepGlobalAugmentation;
34 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.Node;
35 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
36
37 @Singleton
38 public class HAOpNodeListener extends HwvtepNodeBaseListener {
39
40     static BiPredicate<String, InstanceIdentifier<Node>> IS_PS_CHILD_TO_GLOBAL_NODE = (globalNodeId, iid) -> {
41         String psNodeId = iid.firstKeyOf(Node.class).getNodeId().getValue();
42         return psNodeId.startsWith(globalNodeId) && psNodeId.contains("physicalswitch");
43     };
44
45     static Predicate<InstanceIdentifier<Node>> IS_NOT_HA_CHILD = (iid) -> hwvtepHACache.getParent(iid) == null;
46
47     private final IHAEventHandler haEventHandler;
48     private final HAOpClusteredListener haOpClusteredListener;
49     private final NodeCopier nodeCopier;
50
51     @Inject
52     public HAOpNodeListener(DataBroker db, HAEventHandler haEventHandler,
53                             HAOpClusteredListener haOpClusteredListener,
54                             NodeCopier nodeCopier) throws Exception {
55         super(OPERATIONAL, db);
56         this.haEventHandler = haEventHandler;
57         this.haOpClusteredListener = haOpClusteredListener;
58         this.nodeCopier = nodeCopier;
59     }
60
61     String getNodeId(InstanceIdentifier<Node> iid) {
62         return iid.firstKeyOf(Node.class).getNodeId().getValue();
63     }
64
65     @Override
66     public void onGlobalNodeAdd(InstanceIdentifier<Node> childGlobalPath,
67                                 Node childNode,
68                                 ReadWriteTransaction tx) {
69         //copy child global node to ha global node
70         //create ha global config node if not present
71         //copy ha global config node to child global config node
72         LOG.trace("Node connected {} - Checking if Ha or Non-Ha enabled ", childNode.getNodeId().getValue());
73         haOpClusteredListener.onGlobalNodeAdd(childGlobalPath, childNode, tx);
74         if (IS_NOT_HA_CHILD.test(childGlobalPath)) {
75             return;
76         }
77         InstanceIdentifier<Node> haNodePath = hwvtepHACache.getParent(childGlobalPath);
78         LOG.trace("Ha enabled child node connected {}", childNode.getNodeId().getValue());
79         try {
80             nodeCopier.copyGlobalNode(Optional.fromNullable(childNode),
81                     childGlobalPath, haNodePath, LogicalDatastoreType.OPERATIONAL, tx);
82             nodeCopier.copyGlobalNode(Optional.fromNullable(null),
83                     haNodePath, childGlobalPath, LogicalDatastoreType.CONFIGURATION, tx);
84         } catch (ReadFailedException e) {
85             LOG.error("Failed to read nodes {} , {} ", childGlobalPath, haNodePath);
86         }
87         readAndCopyChildPsOpToParent(childGlobalPath, childNode, haNodePath, tx);
88     }
89
90     //Update on global node has been taken care by HAListeners as per perf improvement
91     @Override
92     void onGlobalNodeUpdate(InstanceIdentifier<Node> childGlobalPath,
93                             Node updatedChildNode,
94                             Node originalChildNode,
95                             ReadWriteTransaction tx) throws ReadFailedException {
96
97         String oldHAId = HwvtepHAUtil.getHAIdFromManagerOtherConfig(originalChildNode);
98         if (!Strings.isNullOrEmpty(oldHAId)) { //was already ha child
99             InstanceIdentifier<Node> haPath = hwvtepHACache.getParent(childGlobalPath);
100             LOG.debug("Copy oper update from child {} to parent {}", childGlobalPath, haPath);
101             haEventHandler.copyChildGlobalOpUpdateToHAParent(updatedChildNode, originalChildNode, haPath, tx);
102             return;//TODO handle unha case
103         }
104
105         HAOpClusteredListener.addToHACacheIfBecameHAChild(childGlobalPath, updatedChildNode, originalChildNode, tx);
106         if (IS_NOT_HA_CHILD.test(childGlobalPath)) {
107             return;
108         }
109         LOG.info("{} became ha child ", updatedChildNode.getNodeId().getValue());
110         onGlobalNodeAdd(childGlobalPath, updatedChildNode, tx);
111     }
112
113     @Override
114     void onGlobalNodeDelete(InstanceIdentifier<Node> childGlobalPath,
115                             Node childNode,
116                             ReadWriteTransaction tx) throws InterruptedException, ExecutionException,
117             ReadFailedException {
118         haOpClusteredListener.onGlobalNodeDelete(childGlobalPath, childNode, tx);
119         if (IS_NOT_HA_CHILD.test(childGlobalPath)) {
120             LOG.info("non ha child global delete {} ", getNodeId(childGlobalPath));
121             return;
122         }
123         LOG.info("ha child global delete {} ", getNodeId(childGlobalPath));
124         InstanceIdentifier<Node> haNodePath = hwvtepHACache.getParent(childGlobalPath);
125         Set<InstanceIdentifier<Node>> children = hwvtepHACache.getChildrenForHANode(haNodePath);
126         if (haOpClusteredListener.getConnected(children).isEmpty()) {
127             LOG.info("All child deleted for ha node {} ", HwvtepHAUtil.getNodeIdVal(haNodePath));
128             //ha ps delete is taken care by ps node delete
129             //HwvtepHAUtil.deleteSwitchesManagedBy-Node(haNodePath, tx);
130             HwvtepHAUtil.deleteNodeIfPresent(tx, OPERATIONAL, haNodePath);
131         } else {
132             LOG.info("not all child deleted {} connected {}", getNodeId(childGlobalPath),
133                     haOpClusteredListener.getConnected(children));
134         }
135     }
136
137     @Override
138     void onPsNodeAdd(InstanceIdentifier<Node> childPsPath,
139                      Node childPsNode,
140                      ReadWriteTransaction tx) throws ReadFailedException {
141         //copy child ps oper node to ha ps oper node
142         //copy ha ps config node to child ps config
143         haOpClusteredListener.onPsNodeAdd(childPsPath, childPsNode, tx);
144         InstanceIdentifier<Node> childGlobalPath = HwvtepHAUtil.getGlobalNodePathFromPSNode(childPsNode);
145         if (!haOpClusteredListener.getConnectedNodes().contains(childGlobalPath)) {
146             return;
147         }
148         if (IS_NOT_HA_CHILD.test(childGlobalPath)) {
149             return;
150         }
151         LOG.info("ha ps child connected {} ", getNodeId(childPsPath));
152         InstanceIdentifier<Node> haGlobalPath = hwvtepHACache.getParent(childGlobalPath);
153         InstanceIdentifier<Node> haPsPath = HwvtepHAUtil.convertPsPath(childPsNode, haGlobalPath);
154         try {
155             nodeCopier.copyPSNode(Optional.fromNullable(childPsNode), childPsPath, haPsPath, haGlobalPath,
156                     LogicalDatastoreType.OPERATIONAL, tx);
157             nodeCopier.copyPSNode(Optional.fromNullable(null), haPsPath, childPsPath, childGlobalPath,
158                     LogicalDatastoreType.CONFIGURATION, tx);
159         } catch (ReadFailedException e) {
160             LOG.error("Failed to read nodes {} , {} ", childPsPath, haGlobalPath);
161         }
162     }
163
164     @Override
165     void onPsNodeUpdate(InstanceIdentifier<Node> childPsPath,
166                         Node updatedChildPSNode,
167                         Node originalChildPSNode,
168                         ReadWriteTransaction tx) throws ReadFailedException {
169         InstanceIdentifier<Node> childGlobalPath = HwvtepHAUtil.getGlobalNodePathFromPSNode(updatedChildPSNode);
170         if (IS_NOT_HA_CHILD.test(childGlobalPath)) {
171             return;
172         }
173         InstanceIdentifier<Node> haGlobalPath = hwvtepHACache.getParent(childGlobalPath);
174         InstanceIdentifier<Node> haPsPath = HwvtepHAUtil.convertPsPath(updatedChildPSNode, haGlobalPath);
175         LOG.error("Copy oper ps update from child {} to parent {}", childPsPath, haPsPath);
176         haEventHandler.copyChildPsOpUpdateToHAParent(updatedChildPSNode, originalChildPSNode, haPsPath, tx);
177     }
178
179     @Override
180     void onPsNodeDelete(InstanceIdentifier<Node> childPsPath,
181                         Node childPsNode,
182                         ReadWriteTransaction tx) throws ReadFailedException {
183         //one child ps node disconnected
184         //find if all child ps nodes disconnected then delete parent ps node
185         haOpClusteredListener.onPsNodeDelete(childPsPath, childPsNode, tx);
186         InstanceIdentifier<Node> disconnectedChildGlobalPath = HwvtepHAUtil.getGlobalNodePathFromPSNode(childPsNode);
187         if (IS_NOT_HA_CHILD.test(disconnectedChildGlobalPath)) {
188             LOG.info("on non ha ps child delete {} ", getNodeId(childPsPath));
189             return;
190         }
191         InstanceIdentifier<Node> haGlobalPath = hwvtepHACache.getParent(disconnectedChildGlobalPath);
192         Set<InstanceIdentifier<Node>> childPsPaths = hwvtepHACache.getChildrenForHANode(haGlobalPath).stream()
193                 .map((childGlobalPath) -> HwvtepHAUtil.convertPsPath(childPsNode, childGlobalPath))
194                 .collect(Collectors.toSet());
195         //TODO validate what if this is null
196         if (haOpClusteredListener.getConnected(childPsPaths).isEmpty()) {
197             InstanceIdentifier<Node> haPsPath = HwvtepHAUtil.convertPsPath(childPsNode, haGlobalPath);
198             LOG.info("All child deleted for ha ps node {} ", HwvtepHAUtil.getNodeIdVal(haPsPath));
199             HwvtepHAUtil.deleteNodeIfPresent(tx, LogicalDatastoreType.OPERATIONAL, haPsPath);
200             //HwvtepHAUtil.deleteGlobalNodeSwitches(haGlobalPath, haPsPath, LogicalDatastoreType.OPERATIONAL, tx);
201         } else {
202             LOG.info("not all ha ps child deleted {} connected {}", getNodeId(childPsPath),
203                     haOpClusteredListener.getConnected(childPsPaths));
204         }
205     }
206
207     private void readAndCopyChildPsOpToParent(InstanceIdentifier<Node> childGlobalPath,
208                                               Node childNode,
209                                               InstanceIdentifier<Node> haNodePath,
210                                               ReadWriteTransaction tx) {
211         LOG.error("Inside readAndCopyChildPsOpToParent");
212         String childGlobalNodeId = childNode.getNodeId().getValue();
213         List<InstanceIdentifier> childPsIids = new ArrayList<>();
214         HwvtepGlobalAugmentation hwvtepGlobalAugmentation = childNode.getAugmentation(HwvtepGlobalAugmentation.class);
215         if (hwvtepGlobalAugmentation == null || HwvtepHAUtil.isEmpty(hwvtepGlobalAugmentation.getSwitches())) {
216             haOpClusteredListener.getConnectedNodes()
217                     .stream()
218                     .filter((connectedIid) -> IS_PS_CHILD_TO_GLOBAL_NODE.test(childGlobalNodeId, connectedIid))
219                     .forEach((connectedIid) -> childPsIids.add(connectedIid));
220         } else {
221             hwvtepGlobalAugmentation.getSwitches().forEach(
222                 (switches) -> childPsIids.add(switches.getSwitchRef().getValue()));
223         }
224         if (childPsIids.isEmpty()) {
225             LOG.info("No child ps found for global {}", childGlobalNodeId);
226         }
227         LOG.error("Got child PS node of size {}", childPsIids.size());
228         childPsIids.forEach((psIid) -> {
229             try {
230                 InstanceIdentifier<Node> childPsIid = psIid;
231                 Optional<Node> childPsNode = tx.read(LogicalDatastoreType.OPERATIONAL, childPsIid).checkedGet();
232                 if (childPsNode.isPresent()) {
233                     LOG.debug("Child oper PS node found");
234                     onPsNodeAdd(childPsIid, childPsNode.get(), tx);
235                 } else {
236                     LOG.debug("Child oper ps node not found {}", childPsIid);
237                 }
238             } catch (ReadFailedException e) {
239                 LOG.error("Failed to read child ps node {}", psIid);
240             }
241         });
242     }
243 }