Fixup Augmentable and Identifiable methods changing
[netvirt.git] / neutronvpn / impl / src / main / java / org / opendaylight / netvirt / neutronvpn / NeutronBgpvpnChangeListener.java
1 /*
2  * Copyright (c) 2016 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 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;
16 import java.util.Set;
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.CreateIdPoolOutput;
31 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.idmanager.rev160406.IdManagerService;
32 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn.rev150602.vpnmaps.VpnMap;
33 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.bgpvpns.rev150903.BgpvpnTypeBase;
34 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.bgpvpns.rev150903.BgpvpnTypeL3;
35 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.bgpvpns.rev150903.bgpvpns.attributes.Bgpvpns;
36 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.bgpvpns.rev150903.bgpvpns.attributes.bgpvpns.Bgpvpn;
37 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.rev150712.Neutron;
38 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
39 import org.opendaylight.yangtools.yang.common.RpcResult;
40 import org.osgi.framework.BundleContext;
41 import org.osgi.framework.FrameworkUtil;
42 import org.slf4j.Logger;
43 import org.slf4j.LoggerFactory;
44
45 @Singleton
46 public class NeutronBgpvpnChangeListener extends AsyncDataTreeChangeListenerBase<Bgpvpn, NeutronBgpvpnChangeListener> {
47     private static final Logger LOG = LoggerFactory.getLogger(NeutronBgpvpnChangeListener.class);
48     private final DataBroker dataBroker;
49     private final NeutronvpnManager nvpnManager;
50     private final IdManagerService idManager;
51     private final NeutronvpnUtils neutronvpnUtils;
52     private final String adminRDValue;
53
54     @Inject
55     public NeutronBgpvpnChangeListener(final DataBroker dataBroker, final NeutronvpnManager neutronvpnManager,
56                                        final IdManagerService idManager, final NeutronvpnUtils neutronvpnUtils) {
57         super(Bgpvpn.class, NeutronBgpvpnChangeListener.class);
58         this.dataBroker = dataBroker;
59         nvpnManager = neutronvpnManager;
60         this.idManager = idManager;
61         this.neutronvpnUtils = neutronvpnUtils;
62         BundleContext bundleContext = FrameworkUtil.getBundle(NeutronBgpvpnChangeListener.class).getBundleContext();
63         adminRDValue = bundleContext.getProperty(NeutronConstants.RD_PROPERTY_KEY);
64     }
65
66     @Override
67     @PostConstruct
68     public void init() {
69         LOG.info("{} init", getClass().getSimpleName());
70         createIdPool();
71         registerListener(LogicalDatastoreType.CONFIGURATION, dataBroker);
72     }
73
74     @Override
75     protected InstanceIdentifier<Bgpvpn> getWildCardPath() {
76         return InstanceIdentifier.create(Neutron.class).child(Bgpvpns.class).child(Bgpvpn.class);
77     }
78
79     @Override
80     protected NeutronBgpvpnChangeListener getDataTreeChangeListener() {
81         return NeutronBgpvpnChangeListener.this;
82     }
83
84     private boolean isBgpvpnTypeL3(Class<? extends BgpvpnTypeBase> bgpvpnType) {
85         if (BgpvpnTypeL3.class.equals(bgpvpnType)) {
86             return true;
87         } else {
88             LOG.warn("CRUD operations supported only for L3 type Bgpvpn");
89             return false;
90         }
91     }
92
93     @Override
94     // TODO Clean up the exception handling
95     @SuppressWarnings("checkstyle:IllegalCatch")
96     protected void add(InstanceIdentifier<Bgpvpn> identifier, Bgpvpn input) {
97         LOG.trace("Adding Bgpvpn : key: {}, value={}", identifier, input);
98         String vpnName = input.getUuid().getValue();
99         if (isBgpvpnTypeL3(input.getType())) {
100             VpnInstance.Type vpnInstanceType = VpnInstance.Type.L3;
101             // handle route-target(s)
102             List<String> inputRouteList = input.getRouteTargets();
103             List<String> inputImportRouteList = input.getImportTargets();
104             List<String> inputExportRouteList = input.getExportTargets();
105             Set<String> inputImportRouteSet = new HashSet<>();
106             Set<String> inputExportRouteSet = new HashSet<>();
107
108             if (inputRouteList != null && !inputRouteList.isEmpty()) {
109                 inputImportRouteSet.addAll(inputRouteList);
110                 inputExportRouteSet.addAll(inputRouteList);
111             }
112             if (inputImportRouteList != null && !inputImportRouteList.isEmpty()) {
113                 inputImportRouteSet.addAll(inputImportRouteList);
114             }
115             if (inputExportRouteList != null && !inputExportRouteList.isEmpty()) {
116                 inputExportRouteSet.addAll(inputExportRouteList);
117             }
118             List<String> importRouteTargets = new ArrayList<>();
119             List<String> exportRouteTargets = new ArrayList<>();
120             importRouteTargets.addAll(inputImportRouteSet);
121             exportRouteTargets.addAll(inputExportRouteSet);
122
123             List<String> rd = input.getRouteDistinguishers();
124
125             if (rd == null || rd.isEmpty()) {
126                 // generate new RD
127                 // TODO - commented out for now to avoid "Dead store to rd" violation.
128                 //rd = generateNewRD(input.getUuid());
129             } else {
130                 String[] rdParams = rd.get(0).split(":");
131                 if (rdParams[0].trim().equals(adminRDValue)) {
132                     LOG.error("AS specific part of RD should not be same as that defined by DC Admin. Error "
133                             + "encountered for BGPVPN {} with RD {}", vpnName, rd.get(0));
134                     return;
135                 }
136                 List<String> existingRDs = neutronvpnUtils.getExistingRDs();
137                 if (!Collections.disjoint(existingRDs, rd)) {
138                     LOG.error("Failed to create VPN {} as another VPN with the same RD {} already exists.", vpnName,
139                             rd);
140                     return;
141                 }
142                 Uuid router = null;
143                 if (input.getRouters() != null && !input.getRouters().isEmpty()) {
144                     // currently only one router
145                     router = input.getRouters().get(0);
146                 }
147                 if (!rd.isEmpty()) {
148                     try {
149                         nvpnManager.createVpn(input.getUuid(), input.getName(), input.getTenantId(), rd,
150                                 importRouteTargets, exportRouteTargets, router, input.getNetworks(),
151                                 vpnInstanceType, 0 /*l3vni*/);
152                     } catch (Exception e) {
153                         LOG.error("Creation of BGPVPN {} failed", vpnName, e);
154                     }
155                 } else {
156                     LOG.error("Create BgpVPN with id {} failed due to missing RD value", vpnName);
157                 }
158             }
159         } else {
160             LOG.warn("BGPVPN type for VPN {} is not L3", vpnName);
161         }
162     }
163
164     @Override
165     protected void remove(InstanceIdentifier<Bgpvpn> identifier, Bgpvpn input) {
166         LOG.trace("Removing Bgpvpn : key: {}, value={}", identifier, input);
167         if (isBgpvpnTypeL3(input.getType())) {
168             nvpnManager.removeVpn(input.getUuid());
169             // Release RD Id in pool
170             neutronvpnUtils.releaseRDId(NeutronConstants.RD_IDPOOL_NAME, input.getUuid().toString());
171         } else {
172             LOG.warn("BGPVPN type for VPN {} is not L3", input.getUuid().getValue());
173         }
174     }
175
176     @Override
177     protected void update(InstanceIdentifier<Bgpvpn> identifier, Bgpvpn original, Bgpvpn update) {
178         LOG.trace("Update Bgpvpn : key: {}, value={}", identifier, update);
179         Uuid vpnId = update.getUuid();
180         if (isBgpvpnTypeL3(update.getType())) {
181             try {
182                 handleVpnInstanceUpdate(original.getUuid().getValue(), original.getRouteDistinguishers(),
183                         update.getRouteDistinguishers());
184             } catch (UnsupportedOperationException e) {
185                 LOG.error("Error while processing Update Bgpvpn.", e);
186                 return;
187             }
188             List<Uuid> oldNetworks = original.getNetworks();
189             List<Uuid> newNetworks = update.getNetworks();
190             handleNetworksUpdate(vpnId, oldNetworks, newNetworks);
191             List<Uuid> oldRouters = original.getRouters();
192             List<Uuid> newRouters = update.getRouters();
193             handleRoutersUpdate(vpnId, oldRouters, newRouters);
194         } else {
195             LOG.warn("BGPVPN type for VPN {} is not L3", vpnId.getValue());
196         }
197     }
198
199     protected void handleVpnInstanceUpdate(String vpnInstanceName,final List<String> originalRds,
200                                            List<String> updateRDs) throws UnsupportedOperationException {
201         if (updateRDs == null || updateRDs.isEmpty()) {
202             return;
203         }
204         int oldRdsCount = originalRds.size();
205
206         for (String rd : originalRds) {
207             //If the existing rd is not present in the updateRds list, not allow to process the updateRDs.
208             if (!updateRDs.contains(rd)) {
209                 LOG.error("The existing RD {} not present in the updatedRDsList:{}", rd, updateRDs);
210                 throw new UnsupportedOperationException("The existing RD not present in the updatedRDsList");
211             }
212         }
213         if (updateRDs.size() == oldRdsCount) {
214             LOG.debug("There is no update in the List of Route Distinguisher for the VpnInstance:{}", vpnInstanceName);
215             return;
216         }
217         LOG.debug("update the VpnInstance:{} with the List of RDs: {}", vpnInstanceName, updateRDs);
218         nvpnManager.updateVpnInstanceWithRDs(vpnInstanceName, updateRDs);
219     }
220
221     protected void handleNetworksUpdate(Uuid vpnId, List<Uuid> oldNetworks, List<Uuid> newNetworks) {
222         if (newNetworks != null && !newNetworks.isEmpty()) {
223             if (oldNetworks != null && !oldNetworks.isEmpty()) {
224                 if (oldNetworks != newNetworks) {
225                     Iterator<Uuid> iter = newNetworks.iterator();
226                     while (iter.hasNext()) {
227                         Uuid net = iter.next();
228                         if (oldNetworks.contains(net)) {
229                             oldNetworks.remove(net);
230                             iter.remove();
231                         }
232                     }
233                     //clear removed networks
234                     if (!oldNetworks.isEmpty()) {
235                         LOG.trace("Removing old networks {} ", oldNetworks);
236                         nvpnManager.dissociateNetworksFromVpn(vpnId, oldNetworks);
237                     }
238
239                     //add new (Delta) Networks
240                     if (!newNetworks.isEmpty()) {
241                         LOG.trace("Adding delta New networks {} ", newNetworks);
242                         nvpnManager.associateNetworksToVpn(vpnId, newNetworks);
243                     }
244                 }
245             } else {
246                 //add new Networks
247                 LOG.trace("Adding New networks {} ", newNetworks);
248                 nvpnManager.associateNetworksToVpn(vpnId, newNetworks);
249             }
250         } else if (oldNetworks != null && !oldNetworks.isEmpty()) {
251             LOG.trace("Removing old networks {} ", oldNetworks);
252             nvpnManager.dissociateNetworksFromVpn(vpnId, oldNetworks);
253
254         }
255     }
256
257     protected void handleRoutersUpdate(Uuid vpnId, List<Uuid> oldRouters, List<Uuid> newRouters) {
258         if (newRouters != null && !newRouters.isEmpty()) {
259             if (oldRouters != null && !oldRouters.isEmpty()) {
260                 if (oldRouters.size() > 1 || newRouters.size() > 1) {
261                     VpnMap vpnMap = neutronvpnUtils.getVpnMap(vpnId);
262                     if (vpnMap != null && vpnMap.getRouterId() != null) {
263                         LOG.warn("Only single router association to a given bgpvpn is allowed. Kindly disassociate "
264                                 + "router {} from vpn {} before proceeding with associate",
265                                 vpnMap.getRouterId().getValue(), vpnId.getValue());
266                     }
267                 }
268             } else if (validateRouteInfo(newRouters.get(0))) {
269                 nvpnManager.associateRouterToVpn(vpnId, newRouters.get(0));
270             }
271         } else if (oldRouters != null && !oldRouters.isEmpty()) {
272                 /* dissociate old router */
273             Uuid oldRouter = oldRouters.get(0);
274             nvpnManager.dissociateRouterFromVpn(vpnId, oldRouter);
275         }
276     }
277
278     private void createIdPool() {
279         CreateIdPoolInput createPool = new CreateIdPoolInputBuilder().setPoolName(NeutronConstants.RD_IDPOOL_NAME)
280                 .setLow(NeutronConstants.RD_IDPOOL_START)
281                 .setHigh(new BigInteger(NeutronConstants.RD_IDPOOL_SIZE).longValue()).build();
282         try {
283             Future<RpcResult<CreateIdPoolOutput>> result = idManager.createIdPool(createPool);
284             if (result != null && result.get().isSuccessful()) {
285                 LOG.info("Created IdPool for Bgpvpn RD");
286             } else {
287                 LOG.error("Failed to create ID pool for BGPVPN RD, result future returned {}", result);
288             }
289         } catch (InterruptedException | ExecutionException e) {
290             LOG.error("Failed to create idPool for Bgpvpn RD", e);
291         }
292     }
293
294     private boolean validateRouteInfo(Uuid routerID) {
295         Uuid assocVPNId;
296         if ((assocVPNId = neutronvpnUtils.getVpnForRouter(routerID, true)) != null) {
297             LOG.warn("VPN router association failed due to router {} already associated to another VPN {}",
298                     routerID.getValue(), assocVPNId.getValue());
299             return false;
300         }
301         return true;
302     }
303
304 }