2 * Copyright (c) 2016 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.math.BigInteger;
11 import java.util.ArrayList;
12 import java.util.Collection;
13 import java.util.Collections;
14 import java.util.HashSet;
15 import java.util.Iterator;
16 import java.util.List;
18 import java.util.concurrent.ExecutionException;
19 import java.util.concurrent.Future;
20 import javax.annotation.PreDestroy;
21 import javax.inject.Inject;
22 import javax.inject.Singleton;
23 import org.opendaylight.infrautils.utils.concurrent.Executors;
24 import org.opendaylight.mdsal.binding.api.DataBroker;
25 import org.opendaylight.mdsal.common.api.LogicalDatastoreType;
26 import org.opendaylight.netvirt.neutronvpn.api.utils.NeutronConstants;
27 import org.opendaylight.serviceutils.tools.listener.AbstractAsyncDataTreeChangeListener;
28 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.Uuid;
29 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.idmanager.rev160406.CreateIdPoolInput;
30 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.idmanager.rev160406.CreateIdPoolInputBuilder;
31 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.idmanager.rev160406.CreateIdPoolOutput;
32 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.idmanager.rev160406.IdManagerService;
33 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn.rev150602.vpnmaps.VpnMap;
34 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.bgpvpns.rev150903.BgpvpnTypeBase;
35 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.bgpvpns.rev150903.BgpvpnTypeL3;
36 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.bgpvpns.rev150903.bgpvpns.attributes.Bgpvpns;
37 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.bgpvpns.rev150903.bgpvpns.attributes.bgpvpns.Bgpvpn;
38 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.rev150712.Neutron;
39 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
40 import org.opendaylight.yangtools.yang.common.RpcError;
41 import org.opendaylight.yangtools.yang.common.RpcResult;
42 import org.osgi.framework.BundleContext;
43 import org.osgi.framework.FrameworkUtil;
44 import org.slf4j.Logger;
45 import org.slf4j.LoggerFactory;
48 public class NeutronBgpvpnChangeListener extends AbstractAsyncDataTreeChangeListener<Bgpvpn> {
49 private static final Logger LOG = LoggerFactory.getLogger(NeutronBgpvpnChangeListener.class);
50 private final DataBroker dataBroker;
51 private final NeutronvpnManager nvpnManager;
52 private final IdManagerService idManager;
53 private final NeutronvpnUtils neutronvpnUtils;
54 private final String adminRDValue;
57 public NeutronBgpvpnChangeListener(final DataBroker dataBroker, final NeutronvpnManager neutronvpnManager,
58 final IdManagerService idManager, final NeutronvpnUtils neutronvpnUtils) {
59 super(dataBroker, LogicalDatastoreType.CONFIGURATION, InstanceIdentifier.create(Neutron.class)
60 .child(Bgpvpns.class).child(Bgpvpn.class), Executors.newSingleThreadExecutor(
61 "NeutronBgpvpnChangeListener", LOG));
62 this.dataBroker = dataBroker;
63 nvpnManager = neutronvpnManager;
64 this.idManager = idManager;
65 this.neutronvpnUtils = neutronvpnUtils;
66 BundleContext bundleContext = FrameworkUtil.getBundle(NeutronBgpvpnChangeListener.class).getBundleContext();
67 adminRDValue = bundleContext.getProperty(NeutronConstants.RD_PROPERTY_KEY);
72 LOG.info("{} init", getClass().getSimpleName());
80 Executors.shutdownAndAwaitTermination(getExecutorService());
83 private boolean isBgpvpnTypeL3(Class<? extends BgpvpnTypeBase> bgpvpnType) {
84 if (BgpvpnTypeL3.class.equals(bgpvpnType)) {
87 LOG.warn("CRUD operations supported only for L3 type Bgpvpn");
93 // TODO Clean up the exception handling
94 @SuppressWarnings("checkstyle:IllegalCatch")
95 public void add(InstanceIdentifier<Bgpvpn> identifier, Bgpvpn input) {
96 LOG.trace("Adding Bgpvpn : key: {}, value={}", identifier, input);
97 String vpnName = input.getUuid().getValue();
98 if (isBgpvpnTypeL3(input.getType())) {
99 // handle route-target(s)
100 List<String> inputRouteList = input.getRouteTargets();
101 List<String> inputImportRouteList = input.getImportTargets();
102 List<String> inputExportRouteList = input.getExportTargets();
103 Set<String> inputImportRouteSet = new HashSet<>();
104 Set<String> inputExportRouteSet = new HashSet<>();
106 if (inputRouteList != null && !inputRouteList.isEmpty()) {
107 inputImportRouteSet.addAll(inputRouteList);
108 inputExportRouteSet.addAll(inputRouteList);
110 if (inputImportRouteList != null && !inputImportRouteList.isEmpty()) {
111 inputImportRouteSet.addAll(inputImportRouteList);
113 if (inputExportRouteList != null && !inputExportRouteList.isEmpty()) {
114 inputExportRouteSet.addAll(inputExportRouteList);
116 List<String> importRouteTargets = new ArrayList<>();
117 List<String> exportRouteTargets = new ArrayList<>();
118 importRouteTargets.addAll(inputImportRouteSet);
119 exportRouteTargets.addAll(inputExportRouteSet);
121 List<String> rd = input.getRouteDistinguishers() != null
122 ? input.getRouteDistinguishers() : new ArrayList<>();
124 if (rd == null || rd.isEmpty()) {
126 // TODO - commented out for now to avoid "Dead store to rd" violation.
127 //rd = generateNewRD(input.getUuid());
129 String[] rdParams = rd.get(0).split(":");
130 if (rdParams[0].trim().equals(adminRDValue)) {
131 LOG.error("AS specific part of RD should not be same as that defined by DC Admin. Error "
132 + "encountered for BGPVPN {} with RD {}", vpnName, rd.get(0));
135 List<String> existingRDs = neutronvpnUtils.getExistingRDs();
136 if (!Collections.disjoint(existingRDs, rd)) {
137 LOG.error("Failed to create VPN {} as another VPN with the same RD {} already exists.", vpnName,
141 List<Uuid> routersList = null;
142 if (input.getRouters() != null && !input.getRouters().isEmpty()) {
143 // try to take all routers
144 routersList = input.getRouters();
146 if (routersList != null && routersList.size() > NeutronConstants.MAX_ROUTERS_PER_BGPVPN) {
147 LOG.error("Creation of BGPVPN for rd {} failed: maximum allowed number of associated "
148 + "routers is {}.", rd, NeutronConstants.MAX_ROUTERS_PER_BGPVPN);
151 List<Uuid> networkList = null;
152 if (input.getNetworks() != null && !input.getNetworks().isEmpty()) {
153 networkList = input.getNetworks();
157 nvpnManager.createVpn(input.getUuid(), input.getName(), input.getTenantId(), rd,
158 importRouteTargets, exportRouteTargets, routersList, networkList,
159 false /*isL2Vpn*/, 0 /*l3vni*/);
160 } catch (Exception e) {
161 LOG.error("Creation of BGPVPN {} failed", vpnName, e);
164 LOG.error("Create BgpVPN with id {} failed due to missing RD value", vpnName);
168 LOG.warn("BGPVPN type for VPN {} is not L3", vpnName);
173 public void remove(InstanceIdentifier<Bgpvpn> identifier, Bgpvpn input) {
174 LOG.trace("Removing Bgpvpn : key: {}, value={}", identifier, input);
175 Uuid vpnId = input.getUuid();
176 if (isBgpvpnTypeL3(input.getType())) {
177 VpnMap vpnMap = neutronvpnUtils.getVpnMap(vpnId);
178 if (vpnMap == null) {
179 LOG.error("Failed to handle BGPVPN Remove for VPN {} as that VPN is not configured"
180 + " yet as a VPN Instance", vpnId.getValue());
183 nvpnManager.removeVpn(input.getUuid());
184 // Release RD Id in pool
185 List<String> rd = input.getRouteDistinguishers();
186 if (rd == null || rd.isEmpty()) {
187 int releasedId = neutronvpnUtils.releaseId(NeutronConstants.RD_IDPOOL_NAME, vpnId.getValue());
188 if (releasedId == NeutronConstants.INVALID_ID) {
189 LOG.error("NeutronBgpvpnChangeListener remove: Unable to release ID for key {}", vpnId.getValue());
193 LOG.warn("BGPVPN type for VPN {} is not L3", vpnId.getValue());
198 public void update(InstanceIdentifier<Bgpvpn> identifier, Bgpvpn original, Bgpvpn update) {
199 LOG.trace("Update Bgpvpn : key: {}, value={}", identifier, update);
200 Uuid vpnId = update.getUuid();
201 if (isBgpvpnTypeL3(update.getType())) {
203 handleVpnInstanceUpdate(original.getUuid().getValue(), original.getRouteDistinguishers(),
204 update.getRouteDistinguishers());
205 } catch (UnsupportedOperationException e) {
206 LOG.error("Error while processing Update Bgpvpn.", e);
209 List<Uuid> oldNetworks = original.getNetworks();
210 List<Uuid> newNetworks = update.getNetworks();
211 handleNetworksUpdate(vpnId, oldNetworks, newNetworks);
212 List<Uuid> oldRouters = original.getRouters();
213 List<Uuid> newRouters = update.getRouters();
214 handleRoutersUpdate(vpnId, oldRouters, newRouters);
216 LOG.warn("BGPVPN type for VPN {} is not L3", vpnId.getValue());
220 protected void handleVpnInstanceUpdate(String vpnInstanceName,final List<String> originalRds,
221 List<String> updateRDs) throws UnsupportedOperationException {
222 if (updateRDs == null || updateRDs.isEmpty()) {
225 int oldRdsCount = originalRds.size();
227 for (String rd : originalRds) {
228 //If the existing rd is not present in the updateRds list, not allow to process the updateRDs.
229 if (!updateRDs.contains(rd)) {
230 LOG.error("The existing RD {} not present in the updatedRDsList:{}", rd, updateRDs);
231 throw new UnsupportedOperationException("The existing RD not present in the updatedRDsList");
234 if (updateRDs.size() == oldRdsCount) {
235 LOG.debug("There is no update in the List of Route Distinguisher for the VpnInstance:{}", vpnInstanceName);
238 LOG.debug("update the VpnInstance:{} with the List of RDs: {}", vpnInstanceName, updateRDs);
239 nvpnManager.updateVpnInstanceWithRDs(vpnInstanceName, updateRDs);
242 protected void handleNetworksUpdate(Uuid vpnId, List<Uuid> oldNetworks, List<Uuid> newNetworks) {
243 if (newNetworks != null && !newNetworks.isEmpty()) {
244 if (oldNetworks != null && !oldNetworks.isEmpty()) {
245 if (oldNetworks != newNetworks) {
246 Iterator<Uuid> iter = newNetworks.iterator();
247 while (iter.hasNext()) {
248 Uuid net = iter.next();
249 if (oldNetworks.contains(net)) {
250 oldNetworks.remove(net);
254 //clear removed networks
255 if (!oldNetworks.isEmpty()) {
256 LOG.trace("Removing old networks {} ", oldNetworks);
257 List<String> errorMessages = nvpnManager.dissociateNetworksFromVpn(vpnId, oldNetworks);
258 if (!errorMessages.isEmpty()) {
259 LOG.error("handleNetworksUpdate: dissociate old Networks not part of bgpvpn update,"
260 + " from vpn {} failed due to {}", vpnId.getValue(), errorMessages);
264 //add new (Delta) Networks
265 if (!newNetworks.isEmpty()) {
266 LOG.trace("Adding delta New networks {} ", newNetworks);
267 List<String> errorMessages = nvpnManager.associateNetworksToVpn(vpnId, newNetworks);
268 if (!errorMessages.isEmpty()) {
269 LOG.error("handleNetworksUpdate: associate new Networks not part of original bgpvpn,"
270 + " to vpn {} failed due to {}", vpnId.getValue(), errorMessages);
276 LOG.trace("Adding New networks {} ", newNetworks);
277 List<String> errorMessages = nvpnManager.associateNetworksToVpn(vpnId, newNetworks);
278 if (!errorMessages.isEmpty()) {
279 LOG.error("handleNetworksUpdate: associate new Networks to vpn {} failed due to {}",
280 vpnId.getValue(), errorMessages);
283 } else if (oldNetworks != null && !oldNetworks.isEmpty()) {
284 LOG.trace("Removing old networks {} ", oldNetworks);
285 List<String> errorMessages = nvpnManager.dissociateNetworksFromVpn(vpnId, oldNetworks);
286 if (!errorMessages.isEmpty()) {
287 LOG.error("handleNetworksUpdate: dissociate old Networks from vpn {} failed due to {}",
288 vpnId.getValue(), errorMessages);
293 protected void handleRoutersUpdate(Uuid vpnId, List<Uuid> oldRouters, List<Uuid> newRouters) {
294 // for dualstack case we can associate with one VPN instance maximum 2 routers: one with
295 // only IPv4 ports and one with only IPv6 ports, or only one router with IPv4/IPv6 ports
296 // TODO: check router ports ethertype to follow this restriction
297 if (oldRouters != null && !oldRouters.isEmpty()) {
298 //remove to oldRouters the newRouters if existing
299 List<Uuid> oldRoutersCopy = new ArrayList<>();
300 oldRoutersCopy.addAll(oldRouters);
301 if (newRouters != null) {
302 newRouters.forEach(r -> oldRoutersCopy.remove(r));
304 /* dissociate old router */
305 oldRoutersCopy.forEach(r -> {
306 nvpnManager.dissociateRouterFromVpn(vpnId, r);
309 if (newRouters != null && !newRouters.isEmpty()) {
310 if (newRouters.size() > NeutronConstants.MAX_ROUTERS_PER_BGPVPN) {
311 LOG.debug("In handleRoutersUpdate: maximum allowed number of associated routers is 2. VPN: {} "
312 + "is already associated with router: {} and with router: {}",
313 vpnId, newRouters.get(0).getValue(), newRouters.get(1).getValue());
316 for (Uuid routerId : newRouters) {
317 if (oldRouters != null && oldRouters.contains(routerId)) {
320 /* If the first time BGP-VPN is getting associated with router, then no need
321 to validate if the router is already been associated with any other BGP-VPN.
322 This will avoid unnecessary MD-SAL data store read operations in VPN-MAPS.
324 if (oldRouters == null || oldRouters.isEmpty()) {
325 nvpnManager.associateRouterToVpn(vpnId, routerId);
326 } else if (validateRouteInfo(routerId)) {
327 nvpnManager.associateRouterToVpn(vpnId, routerId);
334 private void createIdPool() {
335 CreateIdPoolInput createPool = new CreateIdPoolInputBuilder().setPoolName(NeutronConstants.RD_IDPOOL_NAME)
336 .setLow(NeutronConstants.RD_IDPOOL_START)
337 .setHigh(new BigInteger(NeutronConstants.RD_IDPOOL_SIZE).longValue()).build();
339 Future<RpcResult<CreateIdPoolOutput>> result = idManager.createIdPool(createPool);
340 Collection<RpcError> rpcErrors = null;
341 if (result != null && result.get() != null) {
342 RpcResult<CreateIdPoolOutput> rpcResult = result.get();
343 LOG.info("Created IdPool for Bgpvpn RD");
344 if (rpcResult.isSuccessful()) {
345 LOG.info("Created IdPool for Bgpvpn RD");
348 rpcErrors = rpcResult.getErrors();
349 LOG.error("Failed to create ID pool for BGPVPN RD, result future returned {}", result);
351 LOG.error("createIdPool: Failed to create ID pool for BGPVPN RD, the call returned with RPC errors {}",
352 rpcErrors != null ? rpcErrors : "RpcResult is null");
353 } catch (InterruptedException | ExecutionException e) {
354 LOG.error("Failed to create idPool for Bgpvpn RD", e);
358 private boolean validateRouteInfo(Uuid routerID) {
360 if ((assocVPNId = neutronvpnUtils.getVpnForRouter(routerID, true)) != null) {
361 LOG.warn("VPN router association failed due to router {} already associated to another VPN {}",
362 routerID.getValue(), assocVPNId.getValue());