NETVIRT-1630 migrate to md-sal APIs
[netvirt.git] / vpnmanager / impl / src / main / java / org / opendaylight / netvirt / vpnmanager / SubnetRouteInterfaceStateChangeListener.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 com.google.common.util.concurrent.ListenableFuture;
11 import java.util.ArrayList;
12 import java.util.List;
13 import java.util.Optional;
14 import java.util.concurrent.ExecutionException;
15 import javax.annotation.PreDestroy;
16 import javax.inject.Inject;
17 import javax.inject.Singleton;
18 import org.eclipse.jdt.annotation.NonNull;
19 import org.opendaylight.genius.datastoreutils.SingleTransactionDataBroker;
20 import org.opendaylight.infrautils.jobcoordinator.JobCoordinator;
21 import org.opendaylight.infrautils.utils.concurrent.Executors;
22 import org.opendaylight.mdsal.binding.api.DataBroker;
23 import org.opendaylight.mdsal.common.api.LogicalDatastoreType;
24 import org.opendaylight.netvirt.neutronvpn.api.utils.NeutronUtils;
25 import org.opendaylight.netvirt.neutronvpn.interfaces.INeutronVpnManager;
26 import org.opendaylight.netvirt.vpnmanager.api.InterfaceUtils;
27 import org.opendaylight.serviceutils.tools.listener.AbstractAsyncDataTreeChangeListener;
28 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.iana._if.type.rev170119.L2vlan;
29 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.InterfacesState;
30 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.state.Interface;
31 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.Uuid;
32 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.port.op.data.PortOpDataEntry;
33 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.vpn._interface.op.data.VpnInterfaceOpDataEntry;
34 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn.l3vpn.rev200204.vpn.interfaces.VpnInterface;
35 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn.l3vpn.rev200204.vpn.interfaces.vpn._interface.VpnInstanceNames;
36 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.ports.rev150712.port.attributes.FixedIps;
37 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.ports.rev150712.ports.attributes.ports.Port;
38 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
39 import org.opendaylight.yangtools.yang.common.Uint64;
40 import org.slf4j.Logger;
41 import org.slf4j.LoggerFactory;
42
43 @Singleton
44 public class SubnetRouteInterfaceStateChangeListener extends AbstractAsyncDataTreeChangeListener<Interface> {
45     private static final Logger LOG = LoggerFactory.getLogger(SubnetRouteInterfaceStateChangeListener.class);
46     private static final String LOGGING_PREFIX = "SUBNETROUTE:";
47     private final DataBroker dataBroker;
48     private final VpnSubnetRouteHandler vpnSubnetRouteHandler;
49     private final SubnetOpDpnManager subOpDpnManager;
50     private final INeutronVpnManager neutronVpnManager;
51     private final JobCoordinator jobCoordinator;
52
53     @Inject
54     public SubnetRouteInterfaceStateChangeListener(final DataBroker dataBroker,
55             final VpnSubnetRouteHandler vpnSubnetRouteHandler, final SubnetOpDpnManager subnetOpDpnManager,
56             final INeutronVpnManager neutronVpnService, final JobCoordinator jobCoordinator) {
57         super(dataBroker, LogicalDatastoreType.OPERATIONAL,
58                 InstanceIdentifier.create(InterfacesState.class).child(Interface.class),
59                 Executors.newListeningSingleThreadExecutor("SubnetRouteInterfaceStateChangeListener", LOG));
60         this.dataBroker = dataBroker;
61         this.vpnSubnetRouteHandler = vpnSubnetRouteHandler;
62         this.subOpDpnManager = subnetOpDpnManager;
63         this.neutronVpnManager = neutronVpnService;
64         this.jobCoordinator = jobCoordinator;
65         start();
66     }
67
68     public void start() {
69         LOG.info("{} start", getClass().getSimpleName());
70     }
71
72     @Override
73     @PreDestroy
74     public void close() {
75         super.close();
76         Executors.shutdownAndAwaitTermination(getExecutorService());
77     }
78
79
80     @Override
81     public void add(InstanceIdentifier<Interface> identifier, Interface intrf) {
82         LOG.trace("{} add: Received interface {} up event", LOGGING_PREFIX, intrf);
83         if (L2vlan.class.equals(intrf.getType())) {
84             LOG.trace("SubnetRouteInterfaceListener add: Received interface {} up event", intrf);
85             if (Interface.OperStatus.Up.equals(intrf.getOperStatus())) {
86                 List<Uuid> subnetIdList = getSubnetId(intrf);
87                 if (subnetIdList.isEmpty()) {
88                     LOG.trace("SubnetRouteInterfaceListener add: Port {} doesn't exist in configDS",
89                             intrf.getName());
90                     return;
91                 }
92                 for (Uuid subnetId : subnetIdList) {
93                     jobCoordinator.enqueueJob("SUBNETROUTE-" + subnetId,
94                         () -> {
95                             String interfaceName = intrf.getName();
96                             Uint64 dpnId = Uint64.ZERO;
97                             LOG.info("{} add: Received port UP event for interface {} subnetId {}",
98                                     LOGGING_PREFIX, interfaceName, subnetId);
99                             try {
100                                 dpnId = InterfaceUtils.getDpIdFromInterface(intrf);
101                             } catch (NullPointerException e) {
102                                 LOG.error("{} add: Unable to obtain dpnId for interface {} in subnet {},"
103                                                 + " subnetroute inclusion for this interface failed", LOGGING_PREFIX,
104                                         interfaceName, subnetId, e);
105                             }
106                             List<ListenableFuture<Void>> futures = new ArrayList<>();
107                             try {
108                                 InstanceIdentifier<VpnInterface> id = VpnUtil.getVpnInterfaceIdentifier(interfaceName);
109                                 Optional<VpnInterface> cfgVpnInterface = SingleTransactionDataBroker.syncReadOptional(
110                                         dataBroker, LogicalDatastoreType.CONFIGURATION, id);
111                                 if (!cfgVpnInterface.isPresent()) {
112                                     return futures;
113                                 }
114                                 vpnSubnetRouteHandler.onInterfaceUp(dpnId, intrf.getName(), subnetId);
115                                 LOG.info("{} add: Processed interface {} up event", LOGGING_PREFIX, intrf.getName());
116                             } catch (InterruptedException | ExecutionException e) {
117                                 LOG.error("add: Failed to read data store for interface {} dpn {}", interfaceName,
118                                         dpnId);
119                             }
120                             return futures;
121                         });
122                 }
123             }
124         }
125     }
126
127     // TODO Clean up the exception handling
128     @SuppressWarnings("checkstyle:IllegalCatch")
129     @Override
130     public void remove(InstanceIdentifier<Interface> identifier, Interface intrf) {
131         if (L2vlan.class.equals(intrf.getType())) {
132             LOG.trace("SubnetRouteInterfaceListener remove: Received interface {} down event", intrf);
133             List<Uuid> subnetIdList = getSubnetId(intrf);
134             if (subnetIdList.isEmpty()) {
135                 LOG.trace("SubnetRouteInterfaceListener remove: Port {} doesn't exist in configDS",
136                         intrf.getName());
137                 return;
138             }
139             LOG.trace("{} remove: Processing interface {} down event in ", LOGGING_PREFIX, intrf.getName());
140             for (Uuid subnetId : subnetIdList) {
141                 jobCoordinator.enqueueJob("SUBNETROUTE-" + subnetId,
142                     () -> {
143                         List<ListenableFuture<Void>> futures = new ArrayList<>();
144                         try {
145                             String interfaceName = intrf.getName();
146                             Uint64 dpnId = Uint64.ZERO;
147                             LOG.info("{} remove: Received port DOWN event for interface {} in subnet {} ",
148                                     LOGGING_PREFIX, interfaceName, subnetId);
149                             try {
150                                 dpnId = InterfaceUtils.getDpIdFromInterface(intrf);
151                             } catch (Exception e) {
152                                 LOG.error("{} remove: Unable to retrieve dpnId for interface {} in subnet {}. "
153                                                 + "Fetching from vpn interface itself",
154                                         LOGGING_PREFIX, intrf.getName(), subnetId, e);
155                             }
156                             InstanceIdentifier<VpnInterface> id = VpnUtil
157                                     .getVpnInterfaceIdentifier(interfaceName);
158                             Optional<VpnInterface> cfgVpnInterface = SingleTransactionDataBroker.syncReadOptional(
159                                     dataBroker, LogicalDatastoreType.CONFIGURATION, id);
160                             if (!cfgVpnInterface.isPresent()) {
161                                 return futures;
162                             }
163                             boolean interfaceDownEligible = false;
164                             for (VpnInstanceNames vpnInterfaceVpnInstance :
165                                     cfgVpnInterface.get().nonnullVpnInstanceNames()) {
166                                 String vpnName = vpnInterfaceVpnInstance.getVpnName();
167                                 InstanceIdentifier<VpnInterfaceOpDataEntry> idOper = VpnUtil
168                                         .getVpnInterfaceOpDataEntryIdentifier(interfaceName, vpnName);
169                                 Optional<VpnInterfaceOpDataEntry> optVpnInterface = SingleTransactionDataBroker
170                                         .syncReadOptional(dataBroker, LogicalDatastoreType.OPERATIONAL, idOper);
171                                 if (optVpnInterface.isPresent()) {
172                                     Uint64 dpnIdLocal = dpnId;
173                                     if (Uint64.ZERO.equals(dpnIdLocal)) {
174                                         dpnIdLocal = optVpnInterface.get().getDpnId();
175                                     }
176                                     if (!Uint64.ZERO.equals(dpnIdLocal)) {
177                                         interfaceDownEligible = true;
178                                         break;
179                                     }
180                                 }
181                             }
182                             if (interfaceDownEligible) {
183                                 vpnSubnetRouteHandler.onInterfaceDown(dpnId, intrf.getName(), subnetId);
184                             }
185                             LOG.info("{} remove: Processed interface {} down event in ", LOGGING_PREFIX,
186                                     intrf.getName());
187                         } catch (InterruptedException | ExecutionException e) {
188                             LOG.error("{} remove: Failed to read data store for {}", LOGGING_PREFIX, intrf.getName());
189                         }
190                         return futures;
191                     });
192             }
193         }
194     }
195
196     @Override
197     public void update(InstanceIdentifier<Interface> identifier,
198         Interface original, Interface update) {
199         String interfaceName = update.getName();
200         if (L2vlan.class.equals(update.getType())) {
201             LOG.trace("{} update: Operation Interface update event - Old: {}, New: {}", LOGGING_PREFIX,
202                     original, update);
203             List<Uuid> subnetIdList = getSubnetId(update);
204             if (subnetIdList.isEmpty()) {
205                 LOG.error("SubnetRouteInterfaceListener update: Port {} doesn't exist in configDS",
206                         update.getName());
207                 return;
208             }
209             for (Uuid subnetId : subnetIdList) {
210                 jobCoordinator.enqueueJob("SUBNETROUTE-" + subnetId,
211                     () -> {
212                         Uint64 dpnId = Uint64.ZERO;
213                         try {
214                             dpnId = InterfaceUtils.getDpIdFromInterface(update);
215                         } catch (NullPointerException e) {
216                             LOG.error("{} remove: Unable to retrieve dpnId for interface {} in subnet  {}. "
217                                             + "Fetching from vpn interface itself", LOGGING_PREFIX, update.getName(),
218                                     subnetId, e);
219                         }
220                         List<ListenableFuture<Void>> futures = new ArrayList<>();
221                         try {
222                             InstanceIdentifier<VpnInterface> id = VpnUtil
223                                     .getVpnInterfaceIdentifier(interfaceName);
224                             Optional<VpnInterface> cfgVpnInterface = SingleTransactionDataBroker.syncReadOptional(
225                                     dataBroker, LogicalDatastoreType.CONFIGURATION, id);
226                             if (!cfgVpnInterface.isPresent()) {
227                                 return futures;
228                             }
229                             boolean interfaceChangeEligible = false;
230                             for (VpnInstanceNames vpnInterfaceVpnInstance :
231                                     cfgVpnInterface.get().nonnullVpnInstanceNames()) {
232                                 String vpnName = vpnInterfaceVpnInstance.getVpnName();
233                                 InstanceIdentifier<VpnInterfaceOpDataEntry> idOper = VpnUtil
234                                         .getVpnInterfaceOpDataEntryIdentifier(interfaceName, vpnName);
235                                 Optional<VpnInterfaceOpDataEntry> optVpnInterface = SingleTransactionDataBroker
236                                         .syncReadOptional(dataBroker, LogicalDatastoreType.OPERATIONAL, idOper);
237                                 if (optVpnInterface.isPresent()) {
238                                     Uint64 dpnIdLocal = dpnId;
239                                     if (Uint64.ZERO.equals(dpnIdLocal)) {
240                                         dpnIdLocal = optVpnInterface.get().getDpnId();
241                                     }
242                                     if (!Uint64.ZERO.equals(dpnIdLocal)) {
243                                         interfaceChangeEligible = true;
244                                         break;
245                                     }
246                                 }
247                             }
248                             if (interfaceChangeEligible) {
249                                 if (Interface.OperStatus.Up.equals(update.getOperStatus())) {
250                                     LOG.info("{} update: Received port UP event for interface {} in subnet {}",
251                                             LOGGING_PREFIX, update.getName(), subnetId);
252                                     vpnSubnetRouteHandler.onInterfaceUp(dpnId, update.getName(), subnetId);
253                                 } else if (Interface.OperStatus.Down.equals(update.getOperStatus())
254                                         || Interface.OperStatus.Unknown.equals(update.getOperStatus())) {
255                                 /*
256                                  * If the interface went down voluntarily (or) if the interface is not
257                                  * reachable from control-path involuntarily, trigger subnetRoute election
258                                  */
259                                     LOG.info("{} update: Received port {} event for interface {} in subnet {} ",
260                                             LOGGING_PREFIX, Interface.OperStatus.Unknown.equals(update.getOperStatus())
261                                                     ? "UNKNOWN" : "DOWN", update.getName(), subnetId);
262                                     vpnSubnetRouteHandler.onInterfaceDown(dpnId, update.getName(), subnetId);
263                                 }
264                             }
265                         } catch (InterruptedException | ExecutionException e) {
266                             LOG.error("update: Failed to read data store for interface {} dpn {}", interfaceName,
267                                     dpnId);
268                         }
269                         return futures;
270                     });
271             }
272         }
273         LOG.info("{} update: Processed Interface {} update event", LOGGING_PREFIX, update.getName());
274     }
275
276     @NonNull
277     protected List<Uuid> getSubnetId(Interface intrf) {
278         List<Uuid> listSubnetIds = new ArrayList<>();
279         if (!NeutronUtils.isUuid(intrf.getName())) {
280             LOG.debug("SubnetRouteInterfaceListener: Interface {} doesn't have valid uuid pattern", intrf.getName());
281             return listSubnetIds;
282         }
283
284         PortOpDataEntry portOpEntry = subOpDpnManager.getPortOpDataEntry(intrf.getName());
285         if (portOpEntry != null) {
286             List<Uuid> subnet = portOpEntry.getSubnetIds();
287             if (subnet != null) {
288                 return subnet;
289             }
290             return listSubnetIds;
291         }
292         LOG.trace("SubnetRouteInterfaceListener : Received Port {} event for {} that is not part of subnetRoute",
293                 intrf.getOperStatus(), intrf.getName());
294         Port port = neutronVpnManager.getNeutronPort(intrf.getName());
295         if (port == null) {
296             return listSubnetIds;
297         }
298         List<FixedIps> portIps = port.getFixedIps();
299         if (portIps != null) {
300             for (FixedIps portIp : portIps) {
301                 listSubnetIds.add(portIp.getSubnetId());
302             }
303         }
304         return listSubnetIds;
305     }
306 }