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