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