2 * Copyright © 2016, 2017 Ericsson India Global Services Pvt Ltd. and others. All rights reserved.
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
8 package org.opendaylight.netvirt.neutronvpn;
10 import java.util.ArrayList;
11 import java.util.Collections;
12 import java.util.HashMap;
13 import java.util.Iterator;
14 import java.util.List;
15 import java.util.Optional;
16 import javax.annotation.PreDestroy;
17 import javax.inject.Inject;
18 import javax.inject.Singleton;
19 import org.opendaylight.genius.mdsalutil.NwConstants;
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.serviceutils.tools.listener.AbstractAsyncDataTreeChangeListener;
25 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.Uuid;
26 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.l3.rev150712.l3.attributes.Routes;
27 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.l3.rev150712.routers.attributes.Routers;
28 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.l3.rev150712.routers.attributes.routers.Router;
29 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.l3.rev150712.routers.attributes.routers.router.external_gateway_info.ExternalFixedIps;
30 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.rev150712.Neutron;
31 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.netvirt.inter.vpn.link.rev160311.inter.vpn.link.states.InterVpnLinkState;
32 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.netvirt.inter.vpn.link.rev160311.inter.vpn.links.InterVpnLink;
33 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
34 import org.slf4j.Logger;
35 import org.slf4j.LoggerFactory;
38 public class NeutronRouterChangeListener extends AbstractAsyncDataTreeChangeListener<Router> {
39 private static final Logger LOG = LoggerFactory.getLogger(NeutronRouterChangeListener.class);
40 private final DataBroker dataBroker;
41 private final NeutronvpnManager nvpnManager;
42 private final NeutronvpnNatManager nvpnNatManager;
43 private final NeutronSubnetGwMacResolver gwMacResolver;
44 private final NeutronvpnUtils neutronvpnUtils;
45 private final JobCoordinator jobCoordinator;
48 public NeutronRouterChangeListener(final DataBroker dataBroker, final NeutronvpnManager neutronvpnManager,
49 final NeutronvpnNatManager neutronvpnNatManager,
50 final NeutronSubnetGwMacResolver gwMacResolver,
51 final NeutronvpnUtils neutronvpnUtils,
52 final JobCoordinator jobCoordinator) {
53 super(dataBroker, LogicalDatastoreType.CONFIGURATION, InstanceIdentifier.create(Neutron.class)
54 .child(Routers.class).child(Router.class),
55 Executors.newSingleThreadExecutor("NeutronRouterChangeListener", LOG));
56 this.dataBroker = dataBroker;
57 nvpnManager = neutronvpnManager;
58 nvpnNatManager = neutronvpnNatManager;
59 this.gwMacResolver = gwMacResolver;
60 this.neutronvpnUtils = neutronvpnUtils;
61 this.jobCoordinator = jobCoordinator;
65 LOG.info("{} init", getClass().getSimpleName());
72 Executors.shutdownAndAwaitTermination(getExecutorService());
76 public void add(InstanceIdentifier<Router> identifier, Router input) {
77 LOG.trace("Adding Router : key: {}, value={}", identifier, input);
78 neutronvpnUtils.addToRouterCache(input);
79 // Create internal VPN
80 nvpnManager.createL3InternalVpn(input.getUuid(), null, null, null, null, null, input.getUuid(), null);
81 jobCoordinator.enqueueJob(input.getUuid().toString(), () -> {
82 nvpnNatManager.handleExternalNetworkForRouter(null, input);
83 return Collections.emptyList();
85 gwMacResolver.sendArpRequestsToExtGateways(input);
89 public void remove(InstanceIdentifier<Router> identifier, Router input) {
90 LOG.trace("Removing router : key: {}, value={}", identifier, input);
91 Uuid routerId = input.getUuid();
92 // Handle router deletion for the NAT service
93 /*External Router and networks is handled before deleting the internal VPN, as there is dependency
94 on vpn operational data to release Lport tag in case of L3VPN over VxLAN*/
95 if (input.getExternalGatewayInfo() != null) {
96 Uuid extNetId = input.getExternalGatewayInfo().getExternalNetworkId();
97 List<ExternalFixedIps> externalFixedIps = input.getExternalGatewayInfo().getExternalFixedIps();
98 jobCoordinator.enqueueJob(input.getUuid().toString(), () -> {
99 nvpnNatManager.removeExternalNetworkFromRouter(extNetId, input, externalFixedIps);
100 return Collections.emptyList();
103 //NOTE: Pass an empty routerSubnetIds list, as router interfaces
104 //will be removed from VPN by invocations from NeutronPortChangeListener
105 List<Uuid> routerSubnetIds = new ArrayList<>();
106 nvpnManager.handleNeutronRouterDeleted(routerId, routerSubnetIds);
108 neutronvpnUtils.removeFromRouterCache(input);
109 nvpnNatManager.removeNeutronRouterDpns(input);
113 public void update(InstanceIdentifier<Router> identifier, Router original, Router update) {
114 LOG.trace("Updating Router : key: {}, original value={}, update value={}", identifier, original, update);
115 neutronvpnUtils.addToRouterCache(update);
116 Uuid routerId = update.getUuid();
117 neutronvpnUtils.addToRouterCache(update);
118 Uuid vpnId = neutronvpnUtils.getVpnForRouter(routerId, true);
119 // internal vpn always present in case external vpn not found
123 List<Routes> oldRoutes = new ArrayList<>(original.nonnullRoutes());
124 List<Routes> newRoutes = new ArrayList<>(update.nonnullRoutes());
125 if (!oldRoutes.equals(newRoutes)) {
126 Iterator<Routes> iterator = newRoutes.iterator();
127 while (iterator.hasNext()) {
128 Routes route = iterator.next();
129 if (oldRoutes.remove(route)) {
134 LOG.debug("Updating Router : AddRoutes {}, DeleteRoutes {}", newRoutes, oldRoutes);
135 if (!oldRoutes.isEmpty()) {
136 handleChangedRoutes(vpnId, oldRoutes, NwConstants.DEL_FLOW);
139 //After initial extra-route configuration using cmd-"neutron router-update RouterA destination=IP-A,
140 // nexthop=prefix-A",if another update is done using command - "neutron router-update RouterA
141 // destination=IP-A,nexthop=prefix-B",neutron router listener calls update on prefix-A as well as prefix-B.
142 // On prefix-A , secondary adj (IP-A) is removed ,where as its added on prefix-B. This back-to-back update
143 // creates race-condition in Vrf Engine ,leading inconsistencies in l3nexthop, VpnExtraRoute,
144 // VpnInterfaceOp DS. Hence a temporary fix of 2sec delay is introduced in neutron.
145 // A better fix/design need to be thought to avoid race condition
147 Thread.sleep(2000); // sleep for 2sec
148 } catch (java.lang.InterruptedException e) {
149 LOG.error("Exception while sleeping", e);
152 if (!newRoutes.isEmpty()) {
153 handleChangedRoutes(vpnId, newRoutes, NwConstants.ADD_FLOW);
157 jobCoordinator.enqueueJob(update.getUuid().toString(), () -> {
158 nvpnNatManager.handleExternalNetworkForRouter(original, update);
159 return Collections.emptyList();
161 gwMacResolver.sendArpRequestsToExtGateways(update);
164 private void handleChangedRoutes(Uuid vpnName, List<Routes> routes, int addedOrRemoved) {
165 // Some routes may point to an InterVpnLink's endpoint, lets treat them differently
166 List<Routes> interVpnLinkRoutes = new ArrayList<>();
167 List<Routes> otherRoutes = new ArrayList<>();
168 HashMap<String, InterVpnLink> nexthopsXinterVpnLinks = new HashMap<>();
169 for (Routes route : routes) {
170 String nextHop = route.getNexthop().stringValue();
171 // Nexthop is another VPN?
172 Optional<InterVpnLink> interVpnLink = neutronvpnUtils.getInterVpnLinkByEndpointIp(nextHop);
173 if (interVpnLink.isPresent()) {
174 Optional<InterVpnLinkState> interVpnLinkState =
175 neutronvpnUtils.getInterVpnLinkState(interVpnLink.get().getName());
176 if (interVpnLinkState.isPresent() && interVpnLinkState.get().getState()
177 == InterVpnLinkState.State.Active) {
178 interVpnLinkRoutes.add(route);
179 nexthopsXinterVpnLinks.put(nextHop, interVpnLink.get());
181 LOG.error("Failed installing route to {}. Reason: InterVPNLink {} is not Active",
182 route.getDestination().stringValue(), interVpnLink.get().getName());
185 otherRoutes.add(route);
189 if (addedOrRemoved == NwConstants.ADD_FLOW) {
190 nvpnManager.addInterVpnRoutes(vpnName, interVpnLinkRoutes, nexthopsXinterVpnLinks);
191 nvpnManager.updateVpnInterfaceWithExtraRouteAdjacency(vpnName, otherRoutes);
193 nvpnManager.removeAdjacencyforExtraRoute(vpnName, otherRoutes);
194 nvpnManager.removeInterVpnRoutes(vpnName, interVpnLinkRoutes, nexthopsXinterVpnLinks);