8e9e9b409a3ca9cfc73c3627608db484fb8efa59
[netvirt.git] /
1 /*
2  * Copyright (c) 2016 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
9 package org.opendaylight.netvirt.elan.l2gw.listeners;
10
11 import com.google.common.base.Optional;
12 import com.google.common.util.concurrent.ListenableFuture;
13 import java.util.Collections;
14 import java.util.List;
15 import java.util.Set;
16 import java.util.concurrent.Callable;
17 import java.util.concurrent.ExecutionException;
18 import org.opendaylight.controller.md.sal.binding.api.ClusteredDataTreeChangeListener;
19 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
20 import org.opendaylight.controller.md.sal.binding.api.ReadWriteTransaction;
21 import org.opendaylight.controller.md.sal.common.api.clustering.EntityOwnershipService;
22 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
23 import org.opendaylight.controller.md.sal.common.api.data.ReadFailedException;
24 import org.opendaylight.genius.datastoreutils.hwvtep.HwvtepAbstractDataTreeChangeListener;
25 import org.opendaylight.genius.mdsalutil.MDSALUtil;
26 import org.opendaylight.genius.utils.hwvtep.HwvtepHACache;
27 import org.opendaylight.genius.utils.hwvtep.HwvtepSouthboundConstants;
28 import org.opendaylight.genius.utils.hwvtep.HwvtepSouthboundUtils;
29 import org.opendaylight.netvirt.elan.l2gw.ha.listeners.HAOpClusteredListener;
30 import org.opendaylight.netvirt.elan.l2gw.utils.ElanL2GatewayUtils;
31 import org.opendaylight.netvirt.elan.l2gw.utils.L2GatewayConnectionUtils;
32 import org.opendaylight.netvirt.elan.utils.ElanClusterUtils;
33 import org.opendaylight.netvirt.elan.utils.ElanUtils;
34 import org.opendaylight.netvirt.elanmanager.utils.ElanL2GwCacheUtils;
35 import org.opendaylight.netvirt.neutronvpn.api.l2gw.L2GatewayDevice;
36 import org.opendaylight.netvirt.neutronvpn.api.l2gw.utils.L2GatewayCacheUtils;
37 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.IpAddress;
38 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.rpcs.rev160406.ItmRpcService;
39 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.l2gateways.rev150712.l2gateway.connections.attributes.l2gatewayconnections.L2gatewayConnection;
40 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.hwvtep.rev150901.HwvtepGlobalAugmentation;
41 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.hwvtep.rev150901.HwvtepGlobalAugmentationBuilder;
42 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.hwvtep.rev150901.HwvtepGlobalRef;
43 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.hwvtep.rev150901.PhysicalSwitchAugmentation;
44 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.hwvtep.rev150901.hwvtep.physical._switch.attributes.TunnelIps;
45 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.NetworkTopology;
46 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.NodeId;
47 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.Topology;
48 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.TopologyKey;
49 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.Node;
50 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.NodeBuilder;
51 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
52 import org.slf4j.Logger;
53 import org.slf4j.LoggerFactory;
54
55 /**
56  * Listener to handle physical switch updates.
57  */
58 public class HwvtepPhysicalSwitchListener
59         extends HwvtepAbstractDataTreeChangeListener<PhysicalSwitchAugmentation, HwvtepPhysicalSwitchListener>
60         implements ClusteredDataTreeChangeListener<PhysicalSwitchAugmentation>, AutoCloseable {
61
62     /** The Constant LOG. */
63     private static final Logger LOG = LoggerFactory.getLogger(HwvtepPhysicalSwitchListener.class);
64
65     /** The data broker. */
66     private final DataBroker dataBroker;
67
68     /** The itm rpc service. */
69     private final ItmRpcService itmRpcService;
70
71     /** The entity ownership service. */
72     private final EntityOwnershipService entityOwnershipService;
73
74     private final L2GatewayConnectionUtils l2GatewayConnectionUtils;
75
76     private final HwvtepHACache hwvtepHACache = HwvtepHACache.getInstance();
77
78     /**
79      * Instantiates a new hwvtep physical switch listener.
80      *
81      * @param dataBroker
82      *            the data broker
83      * @param itmRpcService itm rpc
84      * @param entityOwnershipService entity ownership service
85      * @param elanUtils elan utils
86      */
87     public HwvtepPhysicalSwitchListener(final DataBroker dataBroker, ItmRpcService itmRpcService,
88                                         EntityOwnershipService entityOwnershipService,
89                                         ElanUtils elanUtils) {
90         super(PhysicalSwitchAugmentation.class, HwvtepPhysicalSwitchListener.class);
91         this.dataBroker = dataBroker;
92         this.itmRpcService = itmRpcService;
93         this.entityOwnershipService = entityOwnershipService;
94         this.l2GatewayConnectionUtils = elanUtils.getL2GatewayConnectionUtils();
95     }
96
97     public void init() {
98         registerListener(LogicalDatastoreType.OPERATIONAL, dataBroker);
99     }
100
101     @Override
102     protected InstanceIdentifier<PhysicalSwitchAugmentation> getWildCardPath() {
103         return InstanceIdentifier.create(NetworkTopology.class)
104                 .child(Topology.class, new TopologyKey(HwvtepSouthboundConstants.HWVTEP_TOPOLOGY_ID)).child(Node.class)
105                 .augmentation(PhysicalSwitchAugmentation.class);
106     }
107
108     @Override
109     protected HwvtepPhysicalSwitchListener getDataTreeChangeListener() {
110         return HwvtepPhysicalSwitchListener.this;
111     }
112
113     @Override
114     protected void removed(InstanceIdentifier<PhysicalSwitchAugmentation> identifier,
115             PhysicalSwitchAugmentation phySwitchDeleted) {
116         NodeId nodeId = getNodeId(identifier);
117         String psName = phySwitchDeleted.getHwvtepNodeName().getValue();
118         LOG.info("Received physical switch {} removed event for node {}", psName, nodeId.getValue());
119
120         L2GatewayDevice l2GwDevice = L2GatewayCacheUtils.getL2DeviceFromCache(psName);
121         if (l2GwDevice != null) {
122             if (!L2GatewayConnectionUtils.isGatewayAssociatedToL2Device(l2GwDevice)) {
123                 L2GatewayCacheUtils.removeL2DeviceFromCache(psName);
124                 LOG.debug("{} details removed from L2Gateway Cache", psName);
125                 InstanceIdentifier<Node> iid =
126                         HwvtepSouthboundUtils.createInstanceIdentifier(new NodeId(l2GwDevice.getHwvtepNodeId()));
127                 boolean deleteNode = true;
128                 if (hwvtepHACache.isHAParentNode(iid)) {
129                     Optional<Node> nodeOptional = null;
130                     try {
131                         nodeOptional = dataBroker.newReadWriteTransaction().read(
132                                 LogicalDatastoreType.CONFIGURATION, iid).checkedGet();
133                     } catch (ReadFailedException e) {
134                         nodeOptional = Optional.absent();
135                     }
136                     if (nodeOptional.isPresent()) {
137                         Node node = nodeOptional.get();
138                         NodeBuilder nodeBuilder = new NodeBuilder();
139                         HwvtepGlobalAugmentationBuilder builder = new HwvtepGlobalAugmentationBuilder();
140                         HwvtepGlobalAugmentation orig = node.getAugmentation(HwvtepGlobalAugmentation.class);
141                         if (orig != null) {
142                             deleteNode = false;
143                             builder.setSwitches(orig.getSwitches());
144                             builder.setManagers(orig.getManagers());
145                             nodeBuilder.setKey(node.getKey());
146                             nodeBuilder.setNodeId(node.getNodeId());
147                             nodeBuilder.addAugmentation(HwvtepGlobalAugmentation.class, builder.build());
148                             MDSALUtil.syncWrite(this.dataBroker, LogicalDatastoreType.CONFIGURATION,
149                                     iid, nodeBuilder.build());
150                         }
151                     }
152                 }
153                 if (deleteNode) {
154                     MDSALUtil.syncDelete(this.dataBroker, LogicalDatastoreType.CONFIGURATION,
155                             HwvtepSouthboundUtils.createInstanceIdentifier(new NodeId(l2GwDevice.getHwvtepNodeId())));
156                 }
157                 MDSALUtil.syncDelete(this.dataBroker, LogicalDatastoreType.CONFIGURATION,
158                         HwvtepSouthboundUtils.createInstanceIdentifier(nodeId));
159             } else {
160                 LOG.debug("{} details are not removed from L2Gateway Cache as it has L2Gateway reference", psName);
161             }
162
163             l2GwDevice.setConnected(false);
164             ElanL2GwCacheUtils.removeL2GatewayDeviceFromAllElanCache(psName);
165         } else {
166             LOG.error("Unable to find L2 Gateway details for {}", psName);
167         }
168     }
169
170     @Override
171     protected void updated(InstanceIdentifier<PhysicalSwitchAugmentation> identifier,
172             PhysicalSwitchAugmentation phySwitchBefore, PhysicalSwitchAugmentation phySwitchAfter) {
173         NodeId nodeId = getNodeId(identifier);
174         LOG.trace("Received PhysicalSwitch Update Event for node {}: PhysicalSwitch Before: {}, "
175                 + "PhysicalSwitch After: {}", nodeId.getValue(), phySwitchBefore, phySwitchAfter);
176         String psName = phySwitchBefore.getHwvtepNodeName().getValue();
177         LOG.info("Received physical switch {} update event for node {}", psName, nodeId.getValue());
178
179         if (isTunnelIpNewlyConfigured(phySwitchBefore, phySwitchAfter)) {
180             final L2GatewayDevice l2GwDevice =
181                     updateL2GatewayCache(psName, phySwitchAfter.getManagedBy(), phySwitchAfter.getTunnelIps());
182             handleAdd(l2GwDevice);
183         } else {
184             LOG.debug("Other updates in physical switch {} for node {}", psName, nodeId.getValue());
185             // TODO: handle tunnel ip change
186         }
187     }
188
189     @Override
190     protected void added(InstanceIdentifier<PhysicalSwitchAugmentation> identifier,
191             final PhysicalSwitchAugmentation phySwitchAdded) {
192         if (phySwitchAdded.getManagedBy() == null) {
193             LOG.info("managed by field is missing ");
194             return;
195         }
196         InstanceIdentifier<Node> globalNodeId = (InstanceIdentifier<Node>)phySwitchAdded.getManagedBy().getValue();
197         NodeId nodeId = getNodeId(identifier);
198         final String psName = phySwitchAdded.getHwvtepNodeName().getValue();
199         LOG.info("Received physical switch {} added event received for node {}", psName, nodeId.getValue());
200
201         try {
202             if (updateHACacheIfHANode(dataBroker, globalNodeId)) {
203                 updateL2GatewayCache(psName, new HwvtepGlobalRef(hwvtepHACache.getParent(globalNodeId)),
204                         phySwitchAdded.getTunnelIps());
205                 return;
206             }
207         } catch (ExecutionException e) {
208             LOG.error("Failed to read operational node {}", globalNodeId);
209             //TODO add retry mechanism
210         } catch (InterruptedException e) {
211             LOG.error("Failed to read operational node {}", globalNodeId);
212             //TODO add retry mechanism
213         }
214         L2GatewayDevice l2GwDevice =
215                 updateL2GatewayCache(psName, phySwitchAdded.getManagedBy(), phySwitchAdded.getTunnelIps());
216         handleAdd(l2GwDevice);
217     }
218
219     boolean updateHACacheIfHANode(DataBroker broker, InstanceIdentifier<Node> globalNodeId)
220             throws ExecutionException, InterruptedException {
221         ReadWriteTransaction transaction = broker.newReadWriteTransaction();
222         Node node = transaction.read(LogicalDatastoreType.OPERATIONAL, globalNodeId).get().get();
223         HAOpClusteredListener.addToCacheIfHAChildNode(globalNodeId, node);
224         return hwvtepHACache.isHAEnabledDevice(globalNodeId);
225     }
226
227     /**
228      * Handle add.
229      *
230      * @param l2GwDevice
231      *            the l2 gw device
232      */
233     private void handleAdd(L2GatewayDevice l2GwDevice) {
234         final String psName = l2GwDevice.getDeviceName();
235         final String hwvtepNodeId = l2GwDevice.getHwvtepNodeId();
236         Set<IpAddress> tunnelIps = l2GwDevice.getTunnelIps();
237         if (tunnelIps != null) {
238             for (final IpAddress tunnelIpAddr : tunnelIps) {
239                 if (L2GatewayConnectionUtils.isGatewayAssociatedToL2Device(l2GwDevice)) {
240                     LOG.debug("L2Gateway {} associated for {} physical switch; creating ITM tunnels for {}",
241                             l2GwDevice.getL2GatewayIds(), psName, tunnelIpAddr);
242
243                     // It's a pre-provision scenario
244                     // Initiate ITM tunnel creation
245                     ElanClusterUtils.runOnlyInLeaderNode(entityOwnershipService,
246                             "handling Physical Switch add create itm tunnels ",
247                             new Callable<List<ListenableFuture<Void>>>() {
248                                 @Override
249                                 public List<ListenableFuture<Void>> call() throws Exception {
250                                     ElanL2GatewayUtils.createItmTunnels(itmRpcService,
251                                             hwvtepNodeId, psName, tunnelIpAddr);
252                                     return Collections.emptyList();
253                                 }
254                             });
255
256                     // Initiate Logical switch creation for associated L2
257                     // Gateway Connections
258                     List<L2gatewayConnection> l2GwConns = L2GatewayConnectionUtils.getAssociatedL2GwConnections(
259                             dataBroker, l2GwDevice.getL2GatewayIds());
260                     if (l2GwConns != null) {
261                         LOG.debug("L2GatewayConnections associated for {} physical switch", psName);
262
263                         for (L2gatewayConnection l2GwConn : l2GwConns) {
264                             LOG.trace("L2GatewayConnection {} changes executed on physical switch {}",
265                                     l2GwConn.getL2gatewayId(), psName);
266
267                             l2GatewayConnectionUtils.addL2GatewayConnection(l2GwConn, psName);
268                         }
269                     }
270                     // TODO handle deleted l2gw connections while the device is
271                     // offline
272                 }
273             }
274         }
275     }
276
277
278     private L2GatewayDevice updateL2GatewayCache(String psName, HwvtepGlobalRef globalRef, List<TunnelIps> tunnelIps) {
279         L2GatewayDevice l2GwDevice = L2GatewayCacheUtils.getL2DeviceFromCache(psName);
280         if (l2GwDevice == null) {
281             LOG.debug("{} details are not present in L2Gateway Cache; added now!", psName);
282
283             l2GwDevice = new L2GatewayDevice();
284             l2GwDevice.setDeviceName(psName);
285             L2GatewayCacheUtils.addL2DeviceToCache(psName, l2GwDevice);
286         } else {
287             LOG.debug("{} details are present in L2Gateway Cache and same reference used for updates", psName);
288         }
289         l2GwDevice.setConnected(true);
290         String hwvtepNodeId = getManagedByNodeId(globalRef);
291         l2GwDevice.setHwvtepNodeId(hwvtepNodeId);
292
293         if (tunnelIps != null && !tunnelIps.isEmpty()) {
294             for (TunnelIps tunnelIp : tunnelIps) {
295                 IpAddress tunnelIpAddr = tunnelIp.getTunnelIpsKey();
296                 l2GwDevice.addTunnelIp(tunnelIpAddr);
297             }
298         }
299         LOG.trace("L2Gateway cache updated with below details: {}", l2GwDevice);
300         return l2GwDevice;
301     }
302
303     /**
304      * Gets the managed by node id.
305      *
306      * @param globalRef
307      *            the global ref
308      * @return the managed by node id
309      */
310     private String getManagedByNodeId(HwvtepGlobalRef globalRef) {
311         InstanceIdentifier<?> instId = globalRef.getValue();
312         return instId.firstKeyOf(Node.class).getNodeId().getValue();
313     }
314
315     /**
316      * Gets the node id.
317      *
318      * @param identifier
319      *            the identifier
320      * @return the node id
321      */
322     private NodeId getNodeId(InstanceIdentifier<PhysicalSwitchAugmentation> identifier) {
323         return identifier.firstKeyOf(Node.class).getNodeId();
324     }
325
326     /**
327      * Checks if is tunnel ip newly configured.
328      *
329      * @param phySwitchBefore
330      *            the phy switch before
331      * @param phySwitchAfter
332      *            the phy switch after
333      * @return true, if is tunnel ip newly configured
334      */
335     private boolean isTunnelIpNewlyConfigured(PhysicalSwitchAugmentation phySwitchBefore,
336             PhysicalSwitchAugmentation phySwitchAfter) {
337         return (phySwitchBefore.getTunnelIps() == null || phySwitchBefore.getTunnelIps().isEmpty())
338                 && phySwitchAfter.getTunnelIps() != null && !phySwitchAfter.getTunnelIps().isEmpty();
339     }
340
341 }