Upgrade to the Neon base platform
[netvirt.git] / vpnmanager / impl / src / main / java / org / opendaylight / netvirt / vpnmanager / InterfaceStateChangeListener.java
1 /*
2  * Copyright (c) 2015, 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.vpnmanager;
9
10 import static org.opendaylight.genius.infra.Datastore.CONFIGURATION;
11 import static org.opendaylight.genius.infra.Datastore.OPERATIONAL;
12 import static org.opendaylight.netvirt.vpnmanager.VpnUtil.requireNonNullElse;
13
14 import com.google.common.base.Optional;
15 import com.google.common.collect.HashBasedTable;
16 import com.google.common.collect.Table;
17 import com.google.common.util.concurrent.FutureCallback;
18 import com.google.common.util.concurrent.Futures;
19 import com.google.common.util.concurrent.ListenableFuture;
20 import java.math.BigInteger;
21 import java.util.ArrayList;
22 import java.util.Collections;
23 import java.util.List;
24 import javax.annotation.PostConstruct;
25 import javax.inject.Inject;
26 import javax.inject.Singleton;
27 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
28 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
29 import org.opendaylight.genius.datastoreutils.AsyncDataTreeChangeListenerBase;
30 import org.opendaylight.genius.infra.ManagedNewTransactionRunner;
31 import org.opendaylight.genius.infra.ManagedNewTransactionRunnerImpl;
32 import org.opendaylight.infrautils.jobcoordinator.JobCoordinator;
33 import org.opendaylight.netvirt.vpnmanager.api.InterfaceUtils;
34 import org.opendaylight.yang.gen.v1.urn.huawei.params.xml.ns.yang.l3vpn.rev140815.vpn.interfaces.VpnInterface;
35 import org.opendaylight.yang.gen.v1.urn.huawei.params.xml.ns.yang.l3vpn.rev140815.vpn.interfaces.vpn._interface.VpnInstanceNames;
36 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.iana._if.type.rev170119.L2vlan;
37 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.InterfacesState;
38 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.state.Interface;
39 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.state.Interface.OperStatus;
40 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.vpn._interface.op.data.VpnInterfaceOpDataEntry;
41 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
42 import org.slf4j.Logger;
43 import org.slf4j.LoggerFactory;
44
45 @Singleton
46 public class InterfaceStateChangeListener
47     extends AsyncDataTreeChangeListenerBase<Interface, InterfaceStateChangeListener> {
48
49     private static final Logger LOG = LoggerFactory.getLogger(InterfaceStateChangeListener.class);
50     private static final short DJC_MAX_RETRIES = 3;
51     private final DataBroker dataBroker;
52     private final ManagedNewTransactionRunner txRunner;
53     private final VpnInterfaceManager vpnInterfaceManager;
54     private final VpnUtil vpnUtil;
55     private final JobCoordinator jobCoordinator;
56
57     Table<OperStatus, OperStatus, IntfTransitionState> stateTable = HashBasedTable.create();
58
59     enum IntfTransitionState {
60         STATE_UP,
61         STATE_DOWN,
62         STATE_IGNORE
63     }
64
65     private void initialize() {
66         //  Interface State Transition Table
67         //               Up                Down            Unknown
68         // ---------------------------------------------------------------
69         /* Up       { STATE_IGNORE,   STATE_DOWN,     STATE_IGNORE }, */
70         /* Down     { STATE_UP,       STATE_IGNORE,   STATE_IGNORE }, */
71         /* Unknown  { STATE_UP,       STATE_DOWN,     STATE_IGNORE }, */
72
73         stateTable.put(Interface.OperStatus.Up, Interface.OperStatus.Down, IntfTransitionState.STATE_DOWN);
74         stateTable.put(Interface.OperStatus.Down, Interface.OperStatus.Up, IntfTransitionState.STATE_UP);
75         stateTable.put(Interface.OperStatus.Unknown, Interface.OperStatus.Up, IntfTransitionState.STATE_UP);
76         stateTable.put(Interface.OperStatus.Unknown, Interface.OperStatus.Down, IntfTransitionState.STATE_DOWN);
77     }
78
79     @Inject
80     public InterfaceStateChangeListener(final DataBroker dataBroker, final VpnInterfaceManager vpnInterfaceManager,
81             final VpnUtil vpnUtil, final JobCoordinator jobCoordinator) {
82         super(Interface.class, InterfaceStateChangeListener.class);
83         this.dataBroker = dataBroker;
84         this.txRunner = new ManagedNewTransactionRunnerImpl(dataBroker);
85         this.vpnInterfaceManager = vpnInterfaceManager;
86         this.vpnUtil = vpnUtil;
87         this.jobCoordinator = jobCoordinator;
88         initialize();
89     }
90
91     @PostConstruct
92     public void start() {
93         LOG.info("{} start", getClass().getSimpleName());
94         registerListener(LogicalDatastoreType.OPERATIONAL, dataBroker);
95     }
96
97
98     @Override
99     protected InstanceIdentifier<Interface> getWildCardPath() {
100         return InstanceIdentifier.create(InterfacesState.class).child(Interface.class);
101     }
102
103     @Override
104     protected InterfaceStateChangeListener getDataTreeChangeListener() {
105         return InterfaceStateChangeListener.this;
106     }
107
108
109     @Override
110     // TODO Clean up the exception handling
111     @SuppressWarnings("checkstyle:IllegalCatch")
112     protected void add(InstanceIdentifier<Interface> identifier, Interface intrf) {
113         try {
114             if (L2vlan.class.equals(intrf.getType())) {
115                 LOG.info("VPN Interface add event - intfName {} from InterfaceStateChangeListener",
116                                 intrf.getName());
117                 jobCoordinator.enqueueJob("VPNINTERFACE-" + intrf.getName(), () -> {
118                     List<ListenableFuture<Void>> futures = new ArrayList<>(3);
119                     futures.add(txRunner.callWithNewReadWriteTransactionAndSubmit(CONFIGURATION, writeInvTxn -> {
120                         ListenableFuture<Void> configFuture
121                             = txRunner.callWithNewWriteOnlyTransactionAndSubmit(CONFIGURATION, writeConfigTxn -> {
122                                 ListenableFuture<Void> operFuture
123                                     = txRunner.callWithNewWriteOnlyTransactionAndSubmit(OPERATIONAL, writeOperTxn -> {
124                                         final String interfaceName = intrf.getName();
125                                         LOG.info("Detected interface add event for interface {}", interfaceName);
126                                         final VpnInterface vpnIf = vpnUtil.getConfiguredVpnInterface(interfaceName);
127                                         if (vpnIf != null) {
128                                             for (VpnInstanceNames vpnInterfaceVpnInstance :
129                                                     requireNonNullElse(vpnIf.getVpnInstanceNames(),
130                                                         Collections.<VpnInstanceNames>emptyList())) {
131                                                 String vpnName = vpnInterfaceVpnInstance.getVpnName();
132                                                 String primaryRd = vpnUtil.getPrimaryRd(vpnName);
133                                                 if (!vpnInterfaceManager.isVpnInstanceReady(vpnName)) {
134                                                     LOG.info("VPN Interface add event - intfName {} onto vpnName {} "
135                                                             + "running oper-driven, VpnInstance not ready, holding"
136                                                             + " on", vpnIf.getName(), vpnName);
137                                                 } else if (vpnUtil.isVpnPendingDelete(primaryRd)) {
138                                                     LOG.error("add: Ignoring addition of vpnInterface {}, as"
139                                                             + " vpnInstance {} with primaryRd {} is already marked for"
140                                                             + " deletion", interfaceName, vpnName, primaryRd);
141                                                 } else {
142                                                     BigInteger intfDpnId = BigInteger.ZERO;
143                                                     try {
144                                                         intfDpnId = InterfaceUtils.getDpIdFromInterface(intrf);
145                                                     } catch (Exception e) {
146                                                         LOG.error("Unable to retrieve dpnId for interface {}. "
147                                                                 + "Process vpn interface add failed",intrf.getName(),
148                                                                 e);
149                                                         return;
150                                                     }
151                                                     final BigInteger dpnId = intfDpnId;
152                                                     final int ifIndex = intrf.getIfIndex();
153                                                     LOG.info("VPN Interface add event - intfName {} onto vpnName {}"
154                                                             + " running oper-driven", vpnIf.getName(), vpnName);
155                                                     vpnInterfaceManager.processVpnInterfaceUp(dpnId, vpnIf, primaryRd,
156                                                             ifIndex, false, writeConfigTxn, writeOperTxn, writeInvTxn,
157                                                             intrf, vpnName);
158
159                                                 }
160                                             }
161
162                                         }
163                                     });
164                                 futures.add(operFuture);
165                                 operFuture.get(); //Synchronous submit of operTxn
166                             });
167                         futures.add(configFuture);
168                         //TODO: Allow immediateFailedFuture from writeCfgTxn to cancel writeInvTxn as well.
169                         Futures.addCallback(configFuture, new PostVpnInterfaceThreadWorker(intrf.getName(), true,
170                                 "Operational"));
171                     }));
172                     return futures;
173                 });
174             }
175         } catch (Exception e) {
176             LOG.error("Exception caught in Interface {} Operational State Up event", intrf.getName(), e);
177         }
178     }
179
180     @Override
181     // TODO Clean up the exception handling
182     @SuppressWarnings("checkstyle:IllegalCatch")
183     protected void remove(InstanceIdentifier<Interface> identifier, Interface intrf) {
184         final String ifName = intrf.getName();
185         BigInteger dpId = BigInteger.ZERO;
186         try {
187             if (L2vlan.class.equals(intrf.getType())) {
188                 LOG.info("VPN Interface remove event - intfName {} from InterfaceStateChangeListener",
189                                 intrf.getName());
190                 try {
191                     dpId = InterfaceUtils.getDpIdFromInterface(intrf);
192                 } catch (Exception e) {
193                     LOG.error("Unable to retrieve dpnId from interface operational data store for interface"
194                             + " {}. Fetching from vpn interface op data store. ", ifName, e);
195                 }
196                 final BigInteger inputDpId = dpId;
197                 jobCoordinator.enqueueJob("VPNINTERFACE-" + ifName, () -> {
198                     List<ListenableFuture<Void>> futures = new ArrayList<>(3);
199                     ListenableFuture<Void> configFuture =
200                         txRunner.callWithNewWriteOnlyTransactionAndSubmit(CONFIGURATION,
201                             writeConfigTxn -> futures.add(txRunner.callWithNewWriteOnlyTransactionAndSubmit(OPERATIONAL,
202                                 writeOperTxn -> futures.add(
203                                     txRunner.callWithNewReadWriteTransactionAndSubmit(CONFIGURATION, writeInvTxn -> {
204                                         VpnInterface cfgVpnInterface =
205                                             vpnUtil.getConfiguredVpnInterface(ifName);
206                                         if (cfgVpnInterface == null) {
207                                             LOG.debug("Interface {} is not a vpninterface, ignoring.", ifName);
208                                             return;
209                                         }
210                                         for (VpnInstanceNames vpnInterfaceVpnInstance :
211                                                 requireNonNullElse(cfgVpnInterface.getVpnInstanceNames(),
212                                                     Collections.<VpnInstanceNames>emptyList())) {
213                                             String vpnName = vpnInterfaceVpnInstance.getVpnName();
214                                             Optional<VpnInterfaceOpDataEntry> optVpnInterface =
215                                                 vpnUtil.getVpnInterfaceOpDataEntry(ifName, vpnName);
216                                             if (!optVpnInterface.isPresent()) {
217                                                 LOG.debug("Interface {} vpn {} is not a vpninterface, or deletion"
218                                                     + " triggered by northbound agent. ignoring.", ifName, vpnName);
219                                                 continue;
220                                             }
221                                             final VpnInterfaceOpDataEntry vpnInterface = optVpnInterface.get();
222                                             String gwMac = intrf.getPhysAddress() != null ? intrf.getPhysAddress()
223                                                 .getValue() : vpnInterface.getGatewayMacAddress();
224                                             BigInteger dpnId = inputDpId;
225                                             if (dpnId == null || dpnId.equals(BigInteger.ZERO)) {
226                                                 dpnId = vpnInterface.getDpnId();
227                                             }
228                                             final int ifIndex = intrf.getIfIndex();
229                                             LOG.info("VPN Interface remove event - intfName {} onto vpnName {}"
230                                                 + " running oper-driver", vpnInterface.getName(), vpnName);
231                                             vpnInterfaceManager.processVpnInterfaceDown(dpnId, ifName, ifIndex, gwMac,
232                                                 vpnInterface, false, writeConfigTxn, writeOperTxn, writeInvTxn);
233                                         }
234                                     })))));
235                     futures.add(configFuture);
236                     Futures.addCallback(configFuture, new PostVpnInterfaceThreadWorker(intrf.getName(), false,
237                             "Operational"));
238                     return futures;
239                 }, DJC_MAX_RETRIES);
240             }
241         } catch (Exception e) {
242             LOG.error("Exception observed in handling deletion of VPN Interface {}. ", ifName, e);
243         }
244     }
245
246     // TODO Clean up the exception handling
247     @SuppressWarnings("checkstyle:IllegalCatch")
248     @Override
249     protected void update(InstanceIdentifier<Interface> identifier,
250                     Interface original, Interface update) {
251         final String ifName = update.getName();
252         try {
253             if (update.getIfIndex() == null) {
254                 return;
255             }
256             if (L2vlan.class.equals(update.getType())) {
257                 LOG.info("VPN Interface update event - intfName {} from InterfaceStateChangeListener",
258                         update.getName());
259                 jobCoordinator.enqueueJob("VPNINTERFACE-" + ifName, () -> {
260                     List<ListenableFuture<Void>> futures = new ArrayList<>(3);
261                     futures.add(
262                         txRunner.callWithNewWriteOnlyTransactionAndSubmit(OPERATIONAL, writeOperTxn -> futures.add(
263                             txRunner.callWithNewWriteOnlyTransactionAndSubmit(CONFIGURATION,
264                                 writeConfigTxn -> futures.add(
265                                     txRunner.callWithNewReadWriteTransactionAndSubmit(CONFIGURATION, writeInvTxn -> {
266                                         final VpnInterface vpnIf =
267                                             vpnUtil.getConfiguredVpnInterface(ifName);
268                                         if (vpnIf != null) {
269                                             final int ifIndex = update.getIfIndex();
270                                             BigInteger dpnId;
271                                             try {
272                                                 dpnId = InterfaceUtils.getDpIdFromInterface(update);
273                                             } catch (Exception e) {
274                                                 LOG.error("remove: Unable to retrieve dpnId for interface {}", ifName,
275                                                     e);
276                                                 return;
277                                             }
278                                             IntfTransitionState state = getTransitionState(original.getOperStatus(),
279                                                 update.getOperStatus());
280                                             if (state.equals(IntfTransitionState.STATE_IGNORE)) {
281                                                 LOG.info("InterfaceStateChangeListener: Interface {} state original {}"
282                                                         + "updated {} not handled", ifName, original.getOperStatus(),
283                                                     update.getOperStatus());
284                                                 return;
285                                             }
286                                             if (state.equals(IntfTransitionState.STATE_UP)) {
287                                                 for (VpnInstanceNames vpnInterfaceVpnInstance :
288                                                         requireNonNullElse(vpnIf.getVpnInstanceNames(),
289                                                             Collections.<VpnInstanceNames>emptyList())) {
290                                                     String vpnName = vpnInterfaceVpnInstance.getVpnName();
291                                                     String primaryRd = vpnUtil.getPrimaryRd(vpnName);
292                                                     if (!vpnInterfaceManager.isVpnInstanceReady(vpnName)) {
293                                                         LOG.error(
294                                                             "VPN Interface update event - intfName {} onto vpnName {} "
295                                                                 + "running oper-driven UP, VpnInstance not ready,"
296                                                                 + " holding on", vpnIf.getName(), vpnName);
297                                                     } else if (vpnUtil.isVpnPendingDelete(primaryRd)) {
298                                                         LOG.error("update: Ignoring UP event for vpnInterface {}, as "
299                                                             + "vpnInstance {} with primaryRd {} is already marked for"
300                                                             + " deletion", vpnIf.getName(), vpnName, primaryRd);
301                                                     } else {
302                                                         vpnInterfaceManager.processVpnInterfaceUp(dpnId, vpnIf,
303                                                             primaryRd,
304                                                             ifIndex, true, writeConfigTxn, writeOperTxn, writeInvTxn,
305                                                             update, vpnName);
306                                                     }
307                                                 }
308                                             } else if (state.equals(IntfTransitionState.STATE_DOWN)) {
309                                                 for (VpnInstanceNames vpnInterfaceVpnInstance :
310                                                         requireNonNullElse(vpnIf.getVpnInstanceNames(),
311                                                             Collections.<VpnInstanceNames>emptyList())) {
312                                                     String vpnName = vpnInterfaceVpnInstance.getVpnName();
313                                                     LOG.info("VPN Interface update event - intfName {} onto vpnName {}"
314                                                         + " running oper-driven DOWN", vpnIf.getName(), vpnName);
315                                                     Optional<VpnInterfaceOpDataEntry> optVpnInterface =
316                                                         vpnUtil.getVpnInterfaceOpDataEntry(vpnIf.getName(), vpnName);
317                                                     if (optVpnInterface.isPresent()) {
318                                                         VpnInterfaceOpDataEntry vpnOpInterface = optVpnInterface.get();
319                                                         vpnInterfaceManager.processVpnInterfaceDown(dpnId,
320                                                             vpnIf.getName(),
321                                                             ifIndex, update.getPhysAddress().getValue(), vpnOpInterface,
322                                                             true, writeConfigTxn, writeOperTxn, writeInvTxn);
323                                                     } else {
324                                                         LOG.error(
325                                                             "InterfaceStateChangeListener Update DOWN - vpnInterface {}"
326                                                                 + " not available, ignoring event", vpnIf.getName());
327                                                         continue;
328                                                     }
329                                                 }
330                                             }
331                                         } else {
332                                             LOG.debug("Interface {} is not a vpninterface, ignoring.", ifName);
333                                         }
334                                     }))))));
335                     return futures;
336                 });
337             }
338         } catch (Exception e) {
339             LOG.error("Exception observed in handling updation of VPN Interface {}. ", update.getName(), e);
340         }
341     }
342
343     private class PostVpnInterfaceThreadWorker implements FutureCallback<Void> {
344         private final String interfaceName;
345         private final boolean add;
346         private final String txnDestination;
347
348         PostVpnInterfaceThreadWorker(String interfaceName, boolean add, String transactionDest) {
349             this.interfaceName = interfaceName;
350             this.add = add;
351             this.txnDestination = transactionDest;
352         }
353
354         @Override
355         public void onSuccess(Void voidObj) {
356             if (add) {
357                 LOG.debug("InterfaceStateChangeListener: VrfEntries for {} stored into destination {} successfully",
358                         interfaceName, txnDestination);
359             } else {
360                 LOG.debug("InterfaceStateChangeListener:  VrfEntries for {} removed successfully", interfaceName);
361             }
362         }
363
364         @Override
365         public void onFailure(Throwable throwable) {
366             if (add) {
367                 LOG.error("InterfaceStateChangeListener: VrfEntries for {} failed to store into destination {}",
368                         interfaceName, txnDestination, throwable);
369             } else {
370                 LOG.error("InterfaceStateChangeListener: VrfEntries for {} removal failed", interfaceName, throwable);
371                 vpnUtil.unsetScheduledToRemoveForVpnInterface(interfaceName);
372             }
373         }
374     }
375
376     private IntfTransitionState getTransitionState(Interface.OperStatus original , Interface.OperStatus updated) {
377         IntfTransitionState transitionState = stateTable.get(original, updated);
378
379         if (transitionState == null) {
380             return IntfTransitionState.STATE_IGNORE;
381         }
382         return transitionState;
383     }
384 }