Stale neutronrouterdpn Oper DS cleanup
[netvirt.git] / neutronvpn / impl / src / main / java / org / opendaylight / netvirt / neutronvpn / NeutronRouterChangeListener.java
1 /*
2  * Copyright © 2016, 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.neutronvpn;
9
10 import com.google.common.base.Optional;
11 import java.util.ArrayList;
12 import java.util.Collections;
13 import java.util.HashMap;
14 import java.util.List;
15 import javax.annotation.PostConstruct;
16 import javax.inject.Inject;
17 import javax.inject.Singleton;
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.genius.datastoreutils.AsyncDataTreeChangeListenerBase;
21 import org.opendaylight.genius.mdsalutil.NwConstants;
22 import org.opendaylight.infrautils.jobcoordinator.JobCoordinator;
23 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.Uuid;
24 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.l3.rev150712.l3.attributes.Routes;
25 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.l3.rev150712.routers.attributes.Routers;
26 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.l3.rev150712.routers.attributes.routers.Router;
27 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.l3.rev150712.routers.attributes.routers.router.external_gateway_info.ExternalFixedIps;
28 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.rev150712.Neutron;
29 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.netvirt.inter.vpn.link.rev160311.inter.vpn.link.states.InterVpnLinkState;
30 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.netvirt.inter.vpn.link.rev160311.inter.vpn.links.InterVpnLink;
31 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
32 import org.slf4j.Logger;
33 import org.slf4j.LoggerFactory;
34
35 @Singleton
36 public class NeutronRouterChangeListener extends AsyncDataTreeChangeListenerBase<Router, NeutronRouterChangeListener> {
37     private static final Logger LOG = LoggerFactory.getLogger(NeutronRouterChangeListener.class);
38     private final DataBroker dataBroker;
39     private final NeutronvpnManager nvpnManager;
40     private final NeutronvpnNatManager nvpnNatManager;
41     private final NeutronSubnetGwMacResolver gwMacResolver;
42     private final NeutronvpnUtils neutronvpnUtils;
43     private final JobCoordinator jobCoordinator;
44
45     @Inject
46     public NeutronRouterChangeListener(final DataBroker dataBroker, final NeutronvpnManager neutronvpnManager,
47                                        final NeutronvpnNatManager neutronvpnNatManager,
48                                        final NeutronSubnetGwMacResolver gwMacResolver,
49                                        final NeutronvpnUtils neutronvpnUtils,
50                                        final JobCoordinator jobCoordinator) {
51         super(Router.class, NeutronRouterChangeListener.class);
52         this.dataBroker = dataBroker;
53         nvpnManager = neutronvpnManager;
54         nvpnNatManager = neutronvpnNatManager;
55         this.gwMacResolver = gwMacResolver;
56         this.neutronvpnUtils = neutronvpnUtils;
57         this.jobCoordinator = jobCoordinator;
58     }
59
60     @Override
61     @PostConstruct
62     public void init() {
63         LOG.info("{} init", getClass().getSimpleName());
64         registerListener(LogicalDatastoreType.CONFIGURATION, dataBroker);
65     }
66
67     @Override
68     protected InstanceIdentifier<Router> getWildCardPath() {
69         return InstanceIdentifier.create(Neutron.class).child(Routers.class).child(Router.class);
70     }
71
72     @Override
73     protected NeutronRouterChangeListener getDataTreeChangeListener() {
74         return NeutronRouterChangeListener.this;
75     }
76
77
78     @Override
79     protected void add(InstanceIdentifier<Router> identifier, Router input) {
80         LOG.trace("Adding Router : key: {}, value={}", identifier, input);
81         neutronvpnUtils.addToRouterCache(input);
82         // Create internal VPN
83         nvpnManager.createL3InternalVpn(input.getUuid(), null, null, null, null, null, input.getUuid(), null);
84         jobCoordinator.enqueueJob(input.getUuid().toString(), () -> {
85             nvpnNatManager.handleExternalNetworkForRouter(null, input);
86             return Collections.emptyList();
87         });
88         gwMacResolver.sendArpRequestsToExtGateways(input);
89     }
90
91     @Override
92     protected void remove(InstanceIdentifier<Router> identifier, Router input) {
93         LOG.trace("Removing router : key: {}, value={}", identifier, input);
94         Uuid routerId = input.getUuid();
95         // Handle router deletion for the NAT service
96         /*External Router and networks is handled before deleting the internal VPN, as there is dependency
97         on vpn operational data to release Lport tag in case of L3VPN over VxLAN*/
98         if (input.getExternalGatewayInfo() != null) {
99             Uuid extNetId = input.getExternalGatewayInfo().getExternalNetworkId();
100             List<ExternalFixedIps> externalFixedIps = input.getExternalGatewayInfo().getExternalFixedIps();
101             jobCoordinator.enqueueJob(input.getUuid().toString(), () -> {
102                 nvpnNatManager.removeExternalNetworkFromRouter(extNetId, input, externalFixedIps);
103                 return Collections.emptyList();
104             });
105         }
106         //NOTE: Pass an empty routerSubnetIds list, as router interfaces
107         //will be removed from VPN by invocations from NeutronPortChangeListener
108         List<Uuid> routerSubnetIds = new ArrayList<>();
109         nvpnManager.handleNeutronRouterDeleted(routerId, routerSubnetIds);
110
111         neutronvpnUtils.removeFromRouterCache(input);
112         nvpnNatManager.removeNeutronRouterDpns(input);
113     }
114
115     @Override
116     protected void update(InstanceIdentifier<Router> identifier, Router original, Router update) {
117         LOG.trace("Updating Router : key: {}, original value={}, update value={}", identifier, original, update);
118         neutronvpnUtils.addToRouterCache(update);
119         Uuid routerId = update.getUuid();
120         neutronvpnUtils.addToRouterCache(update);
121         Uuid vpnId = neutronvpnUtils.getVpnForRouter(routerId, true);
122         // internal vpn always present in case external vpn not found
123         if (vpnId == null) {
124             vpnId = routerId;
125         }
126         List<Routes> oldRoutes = original.getRoutes() != null ? original.getRoutes() : new ArrayList<>();
127         List<Routes> newRoutes = update.getRoutes() != null ? update.getRoutes() : new ArrayList<>();
128         if (!oldRoutes.equals(newRoutes)) {
129             newRoutes.removeIf(oldRoutes::remove);
130
131             if (!oldRoutes.isEmpty()) {
132                 handleChangedRoutes(vpnId, oldRoutes, NwConstants.DEL_FLOW);
133             }
134
135             //After initial extra-route configuration using cmd-"neutron router-update RouterA destination=IP-A,
136             // nexthop=prefix-A",if another update is done using command - "neutron router-update RouterA
137             // destination=IP-A,nexthop=prefix-B",neutron router listener calls update on prefix-A as well as prefix-B.
138             // On prefix-A , secondary adj (IP-A) is removed ,where as its added on prefix-B. This back-to-back update
139             // creates race-condition in Vrf Engine ,leading inconsistencies in l3nexthop, VpnExtraRoute,
140             // VpnInterfaceOp DS. Hence a temporary fix of 2sec delay is introduced in neutron.
141             // A better fix/design need to be thought to avoid race condition
142             try {
143                 Thread.sleep(2000); // sleep for 2sec
144             } catch (java.lang.InterruptedException e) {
145                 LOG.error("Exception while sleeping", e);
146             }
147
148             handleChangedRoutes(vpnId, newRoutes, NwConstants.ADD_FLOW);
149         }
150
151         jobCoordinator.enqueueJob(update.getUuid().toString(), () -> {
152             nvpnNatManager.handleExternalNetworkForRouter(original, update);
153             return Collections.emptyList();
154         });
155         gwMacResolver.sendArpRequestsToExtGateways(update);
156     }
157
158     private void handleChangedRoutes(Uuid vpnName, List<Routes> routes, int addedOrRemoved) {
159         // Some routes may point to an InterVpnLink's endpoint, lets treat them differently
160         List<Routes> interVpnLinkRoutes = new ArrayList<>();
161         List<Routes> otherRoutes = new ArrayList<>();
162         HashMap<String, InterVpnLink> nexthopsXinterVpnLinks = new HashMap<>();
163         for (Routes route : routes) {
164             String nextHop = route.getNexthop().stringValue();
165             // Nexthop is another VPN?
166             Optional<InterVpnLink> interVpnLink = neutronvpnUtils.getInterVpnLinkByEndpointIp(nextHop);
167             if (interVpnLink.isPresent()) {
168                 Optional<InterVpnLinkState> interVpnLinkState =
169                         neutronvpnUtils.getInterVpnLinkState(interVpnLink.get().getName());
170                 if (interVpnLinkState.isPresent() && interVpnLinkState.get().getState()
171                         == InterVpnLinkState.State.Active) {
172                     interVpnLinkRoutes.add(route);
173                     nexthopsXinterVpnLinks.put(nextHop, interVpnLink.get());
174                 } else {
175                     LOG.error("Failed installing route to {}. Reason: InterVPNLink {} is not Active",
176                             route.getDestination().stringValue(), interVpnLink.get().getName());
177                 }
178             } else {
179                 otherRoutes.add(route);
180             }
181         }
182
183         if (addedOrRemoved == NwConstants.ADD_FLOW) {
184             nvpnManager.addInterVpnRoutes(vpnName, interVpnLinkRoutes, nexthopsXinterVpnLinks);
185             nvpnManager.updateVpnInterfaceWithExtraRouteAdjacency(vpnName, otherRoutes);
186         } else {
187             nvpnManager.removeAdjacencyforExtraRoute(vpnName, otherRoutes);
188             nvpnManager.removeInterVpnRoutes(vpnName, interVpnLinkRoutes, nexthopsXinterVpnLinks);
189         }
190     }
191 }