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.Collections;
13 import java.util.HashSet;
14 import java.util.Iterator;
15 import java.util.List;
17 import java.util.concurrent.ExecutionException;
18 import java.util.concurrent.Future;
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.genius.datastoreutils.AsyncDataTreeChangeListenerBase;
25 import org.opendaylight.netvirt.neutronvpn.api.utils.NeutronConstants;
26 import org.opendaylight.yang.gen.v1.urn.huawei.params.xml.ns.yang.l3vpn.rev140815.vpn.instances.VpnInstance;
27 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.Uuid;
28 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.idmanager.rev160406.CreateIdPoolInput;
29 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.idmanager.rev160406.CreateIdPoolInputBuilder;
30 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.idmanager.rev160406.IdManagerService;
31 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn.rev150602.vpnmaps.VpnMap;
32 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.bgpvpns.rev150903.BgpvpnTypeBase;
33 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.bgpvpns.rev150903.BgpvpnTypeL3;
34 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.bgpvpns.rev150903.bgpvpns.attributes.Bgpvpns;
35 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.bgpvpns.rev150903.bgpvpns.attributes.bgpvpns.Bgpvpn;
36 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.rev150712.Neutron;
37 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
38 import org.opendaylight.yangtools.yang.common.RpcResult;
39 import org.osgi.framework.BundleContext;
40 import org.osgi.framework.FrameworkUtil;
41 import org.slf4j.Logger;
42 import org.slf4j.LoggerFactory;
45 public class NeutronBgpvpnChangeListener extends AsyncDataTreeChangeListenerBase<Bgpvpn, NeutronBgpvpnChangeListener> {
46 private static final Logger LOG = LoggerFactory.getLogger(NeutronBgpvpnChangeListener.class);
47 private final DataBroker dataBroker;
48 private final NeutronvpnManager nvpnManager;
49 private final IdManagerService idManager;
50 private final NeutronvpnUtils neutronvpnUtils;
51 private final String adminRDValue;
54 public NeutronBgpvpnChangeListener(final DataBroker dataBroker, final NeutronvpnManager neutronvpnManager,
55 final IdManagerService idManager, final NeutronvpnUtils neutronvpnUtils) {
56 super(Bgpvpn.class, NeutronBgpvpnChangeListener.class);
57 this.dataBroker = dataBroker;
58 nvpnManager = neutronvpnManager;
59 this.idManager = idManager;
60 this.neutronvpnUtils = neutronvpnUtils;
61 BundleContext bundleContext = FrameworkUtil.getBundle(NeutronBgpvpnChangeListener.class).getBundleContext();
62 adminRDValue = bundleContext.getProperty(NeutronConstants.RD_PROPERTY_KEY);
68 LOG.info("{} init", getClass().getSimpleName());
70 registerListener(LogicalDatastoreType.CONFIGURATION, dataBroker);
74 protected InstanceIdentifier<Bgpvpn> getWildCardPath() {
75 return InstanceIdentifier.create(Neutron.class).child(Bgpvpns.class).child(Bgpvpn.class);
79 protected NeutronBgpvpnChangeListener getDataTreeChangeListener() {
80 return NeutronBgpvpnChangeListener.this;
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 protected 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 VpnInstance.Type vpnInstanceType = VpnInstance.Type.L3;
100 // handle route-target(s)
101 List<String> inputRouteList = input.getRouteTargets();
102 List<String> inputImportRouteList = input.getImportTargets();
103 List<String> inputExportRouteList = input.getExportTargets();
104 Set<String> inputImportRouteSet = new HashSet<>();
105 Set<String> inputExportRouteSet = new HashSet<>();
107 if (inputRouteList != null && !inputRouteList.isEmpty()) {
108 inputImportRouteSet.addAll(inputRouteList);
109 inputExportRouteSet.addAll(inputRouteList);
111 if (inputImportRouteList != null && !inputImportRouteList.isEmpty()) {
112 inputImportRouteSet.addAll(inputImportRouteList);
114 if (inputExportRouteList != null && !inputExportRouteList.isEmpty()) {
115 inputExportRouteSet.addAll(inputExportRouteList);
117 List<String> importRouteTargets = new ArrayList<>();
118 List<String> exportRouteTargets = new ArrayList<>();
119 importRouteTargets.addAll(inputImportRouteSet);
120 exportRouteTargets.addAll(inputExportRouteSet);
122 List<String> rd = input.getRouteDistinguishers();
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,
142 if (input.getRouters() != null && !input.getRouters().isEmpty()) {
143 // currently only one router
144 router = input.getRouters().get(0);
148 nvpnManager.createVpn(input.getUuid(), input.getName(), input.getTenantId(), rd,
149 importRouteTargets, exportRouteTargets, router, input.getNetworks(),
150 vpnInstanceType, 0 /*l3vni*/);
151 } catch (Exception e) {
152 LOG.error("Creation of BGPVPN {} failed", vpnName, e);
155 LOG.error("Create BgpVPN with id {} failed due to missing RD value", vpnName);
159 LOG.warn("BGPVPN type for VPN {} is not L3", vpnName);
164 protected void remove(InstanceIdentifier<Bgpvpn> identifier, Bgpvpn input) {
165 LOG.trace("Removing Bgpvpn : key: {}, value={}", identifier, input);
166 if (isBgpvpnTypeL3(input.getType())) {
167 nvpnManager.removeVpn(input.getUuid());
168 // Release RD Id in pool
169 neutronvpnUtils.releaseRDId(NeutronConstants.RD_IDPOOL_NAME, input.getUuid().toString());
171 LOG.warn("BGPVPN type for VPN {} is not L3", input.getUuid().getValue());
176 protected void update(InstanceIdentifier<Bgpvpn> identifier, Bgpvpn original, Bgpvpn update) {
177 LOG.trace("Update Bgpvpn : key: {}, value={}", identifier, update);
178 Uuid vpnId = update.getUuid();
179 if (isBgpvpnTypeL3(update.getType())) {
181 handleVpnInstanceUpdate(original.getUuid().getValue(), original.getRouteDistinguishers(),
182 update.getRouteDistinguishers());
183 } catch (UnsupportedOperationException e) {
184 LOG.error("Error while processing Update Bgpvpn.", e);
187 List<Uuid> oldNetworks = original.getNetworks();
188 List<Uuid> newNetworks = update.getNetworks();
189 handleNetworksUpdate(vpnId, oldNetworks, newNetworks);
190 List<Uuid> oldRouters = original.getRouters();
191 List<Uuid> newRouters = update.getRouters();
192 handleRoutersUpdate(vpnId, oldRouters, newRouters);
194 LOG.warn("BGPVPN type for VPN {} is not L3", vpnId.getValue());
198 protected void handleVpnInstanceUpdate(String vpnInstanceName,final List<String> originalRds,
199 List<String> updateRDs) throws UnsupportedOperationException {
200 if (updateRDs == null || updateRDs.isEmpty()) {
203 int oldRdsCount = originalRds.size();
204 Iterator<String> originalRdsInter = originalRds.iterator();
206 while (originalRdsInter.hasNext()) {
207 String rd = originalRdsInter.next();
208 //If the existing rd is not present in the updateRds list, not allow to process the updateRDs.
209 if (!updateRDs.contains(rd)) {
210 LOG.error("The existing RD {} not present in the updatedRDsList:{}", rd, updateRDs);
211 throw new UnsupportedOperationException("The existing RD not present in the updatedRDsList");
214 if (updateRDs.size() == oldRdsCount) {
215 LOG.debug("There is no update in the List of Route Distinguisher for the VpnInstance:{}", vpnInstanceName);
218 LOG.debug("update the VpnInstance:{} with the List of RDs: {}", vpnInstanceName, updateRDs);
219 nvpnManager.updateVpnInstanceWithRDs(vpnInstanceName, updateRDs);
222 protected void handleNetworksUpdate(Uuid vpnId, List<Uuid> oldNetworks, List<Uuid> newNetworks) {
223 if (newNetworks != null && !newNetworks.isEmpty()) {
224 if (oldNetworks != null && !oldNetworks.isEmpty()) {
225 if (oldNetworks != newNetworks) {
226 Iterator<Uuid> iter = newNetworks.iterator();
227 while (iter.hasNext()) {
228 Uuid net = iter.next();
229 if (oldNetworks.contains(net)) {
230 oldNetworks.remove(net);
234 //clear removed networks
235 if (!oldNetworks.isEmpty()) {
236 LOG.trace("Removing old networks {} ", oldNetworks);
237 nvpnManager.dissociateNetworksFromVpn(vpnId, oldNetworks);
240 //add new (Delta) Networks
241 if (!newNetworks.isEmpty()) {
242 LOG.trace("Adding delta New networks {} ", newNetworks);
243 nvpnManager.associateNetworksToVpn(vpnId, newNetworks);
248 LOG.trace("Adding New networks {} ", newNetworks);
249 nvpnManager.associateNetworksToVpn(vpnId, newNetworks);
251 } else if (oldNetworks != null && !oldNetworks.isEmpty()) {
252 LOG.trace("Removing old networks {} ", oldNetworks);
253 nvpnManager.dissociateNetworksFromVpn(vpnId, oldNetworks);
258 protected void handleRoutersUpdate(Uuid vpnId, List<Uuid> oldRouters, List<Uuid> newRouters) {
259 if (newRouters != null && !newRouters.isEmpty()) {
260 if (oldRouters != null && !oldRouters.isEmpty()) {
261 if (oldRouters.size() > 1 || newRouters.size() > 1) {
262 VpnMap vpnMap = neutronvpnUtils.getVpnMap(vpnId);
263 if (vpnMap != null && vpnMap.getRouterId() != null) {
264 LOG.warn("Only single router association to a given bgpvpn is allowed. Kindly disassociate "
265 + "router {} from vpn {} before proceeding with associate",
266 vpnMap.getRouterId().getValue(), vpnId.getValue());
269 } else if (validateRouteInfo(newRouters.get(0))) {
270 nvpnManager.associateRouterToVpn(vpnId, newRouters.get(0));
272 } else if (oldRouters != null && !oldRouters.isEmpty()) {
273 /* dissociate old router */
274 Uuid oldRouter = oldRouters.get(0);
275 nvpnManager.dissociateRouterFromVpn(vpnId, oldRouter);
279 private void createIdPool() {
280 CreateIdPoolInput createPool = new CreateIdPoolInputBuilder().setPoolName(NeutronConstants.RD_IDPOOL_NAME)
281 .setLow(NeutronConstants.RD_IDPOOL_START)
282 .setHigh(new BigInteger(NeutronConstants.RD_IDPOOL_SIZE).longValue()).build();
284 Future<RpcResult<Void>> result = idManager.createIdPool(createPool);
285 if (result != null && result.get().isSuccessful()) {
286 LOG.info("Created IdPool for Bgpvpn RD");
288 LOG.error("Failed to create ID pool for BGPVPN RD, result future returned {}", result);
290 } catch (InterruptedException | ExecutionException e) {
291 LOG.error("Failed to create idPool for Bgpvpn RD", e);
295 private boolean validateRouteInfo(Uuid routerID) {
297 if ((assocVPNId = neutronvpnUtils.getVpnForRouter(routerID, true)) != null) {
298 LOG.warn("VPN router association failed due to router {} already associated to another VPN {}",
299 routerID.getValue(), assocVPNId.getValue());