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