Merge "Clean up pom files"
[netvirt.git] / vpnservice / vpnmanager / vpnmanager-impl / src / main / java / org / opendaylight / netvirt / vpnmanager / VpnSubnetRouteHandler.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.vpnmanager;
9
10 import java.util.ArrayList;
11 import java.util.List;
12 import java.util.Map;
13 import java.util.HashMap;
14
15 import com.google.common.base.Preconditions;
16
17 import org.opendaylight.netvirt.bgpmanager.api.IBgpManager;
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.mdsalutil.MDSALUtil;
21 import org.opendaylight.netvirt.vpnmanager.utilities.InterfaceUtils;
22 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.state.Interface.OperStatus;
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.genius.idmanager.rev160406.IdManagerService;
25 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.*;
26 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.port.op.data.PortOpDataEntry;
27 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.port.op.data.PortOpDataEntryKey;
28 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.rd.to.elan.op.RdToElanOpEntry;
29 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.rd.to.elan.op.RdToElanOpEntryBuilder;
30 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.rd.to.elan.op.RdToElanOpEntryKey;
31 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.subnet.op.data.SubnetOpDataEntry;
32 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.subnet.op.data.SubnetOpDataEntryKey;
33 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.subnet.op.data.SubnetOpDataEntryBuilder;
34 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn.rev150602.*;
35 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn.rev150602.subnetmaps.Subnetmap;
36 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn.rev150602.subnetmaps.SubnetmapKey;
37 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.subnet.op.data.subnet.op.data.entry.SubnetToDpn;
38 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.subnet.op.data.subnet.op.data.entry.subnet.to.dpn.VpnInterfaces;
39 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.state.Interface;
40 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
41
42 import java.math.BigInteger;
43
44 import org.slf4j.Logger;
45 import org.slf4j.LoggerFactory;
46
47 import com.google.common.base.Optional;
48
49
50 public class VpnSubnetRouteHandler implements NeutronvpnListener {
51     private static final Logger logger = LoggerFactory.getLogger(VpnSubnetRouteHandler.class);
52
53     private final DataBroker broker;
54     private SubnetOpDpnManager subOpDpnManager;
55     private final IBgpManager bgpManager;
56     private IdManagerService idManager;
57     private VpnInterfaceManager vpnInterfaceManager;
58
59     public VpnSubnetRouteHandler(final DataBroker db, IBgpManager bgpManager, VpnInterfaceManager vpnIntfManager) {
60         broker = db;
61         subOpDpnManager = new SubnetOpDpnManager(broker);
62         this.bgpManager = bgpManager;
63         this.vpnInterfaceManager = vpnIntfManager;
64     }
65
66     public void setIdManager(IdManagerService idManager) {
67         this.idManager = idManager;
68     }
69
70     @Override
71     public void onSubnetAddedToVpn(SubnetAddedToVpn notification) {
72         if (!notification.isExternalVpn()) {
73             return;
74         }
75
76         Uuid subnetId = notification.getSubnetId();
77         String vpnName = notification.getVpnName();
78         String subnetIp = notification.getSubnetIp();
79         Long elanTag = notification.getElanTag();
80
81         Preconditions.checkNotNull(subnetId, "SubnetId cannot be null or empty!");
82         Preconditions.checkNotNull(subnetIp, "SubnetPrefix cannot be null or empty!");
83         Preconditions.checkNotNull(vpnName, "VpnName cannot be null or empty!");
84         Preconditions.checkNotNull(elanTag, "ElanTag cannot be null or empty!");
85
86         logger.info("onSubnetAddedToVpn: Subnet" + subnetId.getValue() + " being added to vpn");
87         //TODO(vivek): Change this to use more granularized lock at subnetId level
88         synchronized (this) {
89             try {
90                 //Create and add SubnetOpDataEntry object for this subnet to the SubnetOpData container
91                 InstanceIdentifier<SubnetOpDataEntry> subOpIdentifier = InstanceIdentifier.builder(SubnetOpData.class).
92                         child(SubnetOpDataEntry.class, new SubnetOpDataEntryKey(subnetId)).build();
93                 Optional<SubnetOpDataEntry> optionalSubs = VpnUtil.read(broker,
94                         LogicalDatastoreType.OPERATIONAL,
95                         subOpIdentifier);
96                 if (optionalSubs.isPresent()) {
97                     logger.error("onSubnetAddedToVpn: SubnetOpDataEntry for subnet " + subnetId.getValue() +
98                             " already detected to be present");
99                     return;
100                 }
101                 logger.debug("onSubnetAddedToVpn: Creating new SubnetOpDataEntry node for subnet: " +  subnetId.getValue());
102                 Map<BigInteger, SubnetToDpn> subDpnMap = new HashMap<>();
103                 SubnetOpDataEntry subOpEntry = null;
104                 BigInteger dpnId = null;
105                 BigInteger nhDpnId = null;
106                 SubnetToDpn subDpn = null;
107
108                 SubnetOpDataEntryBuilder subOpBuilder = new SubnetOpDataEntryBuilder().setKey(new SubnetOpDataEntryKey(subnetId));
109                 subOpBuilder.setSubnetId(subnetId);
110                 subOpBuilder.setSubnetCidr(subnetIp);
111                 String rd = VpnUtil.getVpnRdFromVpnInstanceConfig(broker, vpnName);
112                 if (rd == null) {
113                     logger.error("onSubnetAddedToVpn: The VPN Instance name " + notification.getVpnName() + " does not have RD ");
114                     return;
115                 }
116                 subOpBuilder.setVrfId(rd);
117                 subOpBuilder.setVpnName(vpnName);
118                 subOpBuilder.setSubnetToDpn(new ArrayList<>());
119                 subOpBuilder.setRouteAdvState(TaskState.Na);
120                 subOpBuilder.setElanTag(elanTag);
121
122                 // First recover set of ports available in this subnet
123                 InstanceIdentifier<Subnetmap> subMapid = InstanceIdentifier.builder(Subnetmaps.class).
124                         child(Subnetmap.class, new SubnetmapKey(subnetId)).build();
125                 Optional<Subnetmap> sm = VpnUtil.read(broker, LogicalDatastoreType.CONFIGURATION, subMapid);
126                 if (!sm.isPresent()) {
127                     logger.error("onSubnetAddedToVpn: Unable to retrieve subnetmap entry for subnet : " + subnetId);
128                     return;
129                 }
130                 Subnetmap subMap = sm.get();
131                 List<Uuid> portList = subMap.getPortList();
132                 if (portList != null) {
133                     for (Uuid port: portList) {
134                         Interface intfState = InterfaceUtils.getInterfaceStateFromOperDS(broker,port.getValue());
135                         if (intfState != null) {
136                             dpnId = InterfaceUtils.getDpIdFromInterface(intfState);
137                             if (dpnId == null) {
138                                 logger.info("onSubnetAddedToVpn: Port " + port.getValue() + " is not assigned DPN yet, ignoring ");
139                                 continue;
140                             }
141                             subOpDpnManager.addPortOpDataEntry(port.getValue(), subnetId, dpnId);
142                             if (intfState.getOperStatus() != OperStatus.Up) {
143                                 logger.info("onSubnetAddedToVpn: Port " + port.getValue() + " is not UP yet, ignoring ");
144                                 continue;
145                             }
146                             subDpn = subOpDpnManager.addInterfaceToDpn(subnetId, dpnId, port.getValue());
147                             if (intfState.getOperStatus() == OperStatus.Up) {
148                                 // port is UP
149                                 subDpnMap.put(dpnId, subDpn);
150                                 if (nhDpnId == null) {
151                                     nhDpnId = dpnId;
152                                 }
153                             }
154                         } else {
155                             subOpDpnManager.addPortOpDataEntry(port.getValue(), subnetId, null);
156                         }
157                     }
158                     if (subDpnMap.size() > 0) {
159                         subOpBuilder.setSubnetToDpn(new ArrayList<>(subDpnMap.values()));
160                     }
161                 }
162
163                 if (nhDpnId != null) {
164                     subOpBuilder.setNhDpnId(nhDpnId);
165                     try {
166                         /*
167                         Write the subnet route entry to the FIB.
168                         And also advertise the subnet route entry via BGP.
169                         */
170                         addSubnetRouteToFib(rd, subnetIp, nhDpnId, vpnName, elanTag);
171                         advertiseSubnetRouteToBgp(rd, subnetIp, nhDpnId, vpnName, elanTag);
172                         subOpBuilder.setRouteAdvState(TaskState.Done);
173                     } catch (Exception ex) {
174                         logger.error("onSubnetAddedToVpn: FIB rules and Advertising nhDpnId " + nhDpnId +
175                                 " information for subnet " + subnetId.getValue() + " to BGP failed {}", ex);
176                         subOpBuilder.setRouteAdvState(TaskState.Pending);
177                     }
178                 } else {
179                     try {
180                         /*
181                         Write the subnet route entry to the FIB.
182                         NOTE: Will not advertise to BGP as NextHopDPN is not available yet.
183                         */
184                         addSubnetRouteToFib(rd, subnetIp, null, vpnName, elanTag);
185                     } catch (Exception ex) {
186                         logger.error("onSubnetAddedToVpn: FIB rules writing for subnet {} with exception {} " +
187                                 subnetId.getValue(), ex);
188                         subOpBuilder.setRouteAdvState(TaskState.Pending);
189                     }
190                 }
191
192                 subOpEntry = subOpBuilder.build();
193                 MDSALUtil.syncWrite(broker, LogicalDatastoreType.OPERATIONAL, subOpIdentifier, subOpEntry);
194                 logger.info("onSubnetAddedToVpn: Added subnetopdataentry to OP Datastore for subnet " + subnetId.getValue());
195             } catch (Exception ex) {
196                 logger.error("Creation of SubnetOpDataEntry for subnet " +
197                         subnetId.getValue() + " failed {}", ex);
198             } finally {
199             }
200         }
201     }
202
203     @Override
204     public void onSubnetDeletedFromVpn(SubnetDeletedFromVpn notification) {
205         Uuid subnetId = notification.getSubnetId();
206
207         if (!notification.isExternalVpn()) {
208             return;
209         }
210         logger.info("onSubnetDeletedFromVpn: Subnet" + subnetId.getValue() + " being removed to vpn");
211         //TODO(vivek): Change this to use more granularized lock at subnetId level
212         synchronized (this) {
213             try {
214                 InstanceIdentifier<SubnetOpDataEntry> subOpIdentifier = InstanceIdentifier.builder(SubnetOpData.class).
215                         child(SubnetOpDataEntry.class, new SubnetOpDataEntryKey(subnetId)).build();
216                 logger.trace(" Removing the SubnetOpDataEntry node for subnet: " +  subnetId.getValue());
217                 Optional<SubnetOpDataEntry> optionalSubs = VpnUtil.read(broker,
218                         LogicalDatastoreType.OPERATIONAL,
219                         subOpIdentifier);
220                 if (!optionalSubs.isPresent()) {
221                     logger.error("onSubnetDeletedFromVpn: SubnetOpDataEntry for subnet " + subnetId.getValue() +
222                             " not available in datastore");
223                     return;
224                 }
225                 SubnetOpDataEntryBuilder subOpBuilder = new SubnetOpDataEntryBuilder(optionalSubs.get());
226                 List<SubnetToDpn> subDpnList = subOpBuilder.getSubnetToDpn();
227                 for (SubnetToDpn subDpn: subDpnList) {
228                     List<VpnInterfaces> vpnIntfList = subDpn.getVpnInterfaces();
229                     for (VpnInterfaces vpnIntf: vpnIntfList) {
230                         subOpDpnManager.removePortOpDataEntry(vpnIntf.getInterfaceName());
231                     }
232                 }
233                 //Removing Stale Ports in portOpData
234                 InstanceIdentifier<Subnetmap> subMapid = InstanceIdentifier.builder(Subnetmaps.class).
235                         child(Subnetmap.class, new SubnetmapKey(subnetId)).build();
236                 Optional<Subnetmap> sm = VpnUtil.read(broker, LogicalDatastoreType.CONFIGURATION, subMapid);
237                 if (!sm.isPresent()) {
238                     logger.error("Stale ports removal: Unable to retrieve subnetmap entry for subnet : " + subnetId);
239                 }
240                 Subnetmap subMap = sm.get();
241                 List<Uuid> portList = subMap.getPortList();
242                 if(portList!=null){
243                     InstanceIdentifier<PortOpData> portOpIdentifier = InstanceIdentifier.builder(PortOpData.class).build();
244                     Optional<PortOpData> optionalPortOp = VpnUtil.read(broker, LogicalDatastoreType.OPERATIONAL, portOpIdentifier);
245                     if(!optionalPortOp.isPresent()){
246                         logger.error("Stale ports removal: Cannot delete port. Not available in data store");
247                         return;
248                     } else{
249                         PortOpData portOpData = optionalPortOp.get();
250                         List<PortOpDataEntry> portOpDataList = portOpData.getPortOpDataEntry();
251                         if(portOpDataList!=null){
252                             for(PortOpDataEntry portOpDataListEntry :  portOpDataList){
253                                 if(portList.contains(new Uuid(portOpDataListEntry.getPortId()))){
254                                     logger.trace("Removing stale port: " + portOpDataListEntry + "for dissociated subnetId: " + subnetId);
255                                     MDSALUtil.syncDelete(broker, LogicalDatastoreType.OPERATIONAL, portOpIdentifier.
256                                             child(PortOpDataEntry.class, new PortOpDataEntryKey(portOpDataListEntry.getKey())));
257                                 }
258                             }
259                         }
260                     }
261                 }
262
263                 String rd = subOpBuilder.getVrfId();
264                 String subnetIp = subOpBuilder.getSubnetCidr();
265                 BigInteger nhDpnId = subOpBuilder.getNhDpnId();
266                 MDSALUtil.syncDelete(broker, LogicalDatastoreType.OPERATIONAL, subOpIdentifier);
267                 logger.trace("Removed subnetopdataentry successfully to CONFIG Datastore");
268                 try {
269                     //Withdraw the routes for all the interfaces on this subnet
270                     //Remove subnet route entry from FIB
271                     withdrawSubnetRoutefromBgp(rd, subnetIp);
272                     deleteSubnetRouteFromFib(rd, subnetIp);
273                 } catch (Exception ex) {
274                     logger.error("onSubnetAddedToVpn: Withdrawing routes from BGP for subnet " +
275                             subnetId.getValue() + " failed {}" + ex);
276                 }
277             } catch (Exception ex) {
278                 logger.error("Removal of SubnetOpDataEntry for subnet " +
279                         subnetId.getValue() + " failed {}" + ex);
280             } finally {
281             }
282         }
283     }
284
285     @Override
286     public void onSubnetUpdatedInVpn(SubnetUpdatedInVpn notification) {
287         Uuid subnetId = notification.getSubnetId();
288         String vpnName = notification.getVpnName();
289         String subnetIp = notification.getSubnetIp();
290         Long elanTag = notification.getElanTag();
291
292         Preconditions.checkNotNull(subnetId, "SubnetId cannot be null or empty!");
293         Preconditions.checkNotNull(subnetIp, "SubnetPrefix cannot be null or empty!");
294         Preconditions.checkNotNull(vpnName, "VpnName cannot be null or empty!");
295         Preconditions.checkNotNull(elanTag, "ElanTag cannot be null or empty!");
296
297         InstanceIdentifier<SubnetOpDataEntry> subOpIdentifier = InstanceIdentifier.builder(SubnetOpData.class).
298                 child(SubnetOpDataEntry.class, new SubnetOpDataEntryKey(subnetId)).build();
299         Optional<SubnetOpDataEntry> optionalSubs = VpnUtil.read(broker,
300                 LogicalDatastoreType.OPERATIONAL,
301                 subOpIdentifier);
302         if (optionalSubs.isPresent()) {
303             if (!notification.isExternalVpn()) {
304                 SubnetDeletedFromVpnBuilder bldr = new SubnetDeletedFromVpnBuilder().setVpnName(vpnName);
305                 bldr.setElanTag(elanTag).setExternalVpn(true).setSubnetIp(subnetIp).setSubnetId(subnetId);
306                 onSubnetDeletedFromVpn(bldr.build());
307             }
308             // TODO(vivek): Something got updated, but we donot know what ?
309         } else {
310             if (notification.isExternalVpn()) {
311                 SubnetAddedToVpnBuilder bldr = new SubnetAddedToVpnBuilder().setVpnName(vpnName).setElanTag(elanTag);
312                 bldr.setSubnetIp(subnetIp).setSubnetId(subnetId).setExternalVpn(true);
313                 onSubnetAddedToVpn(bldr.build());
314             }
315             // TODO(vivek): Something got updated, but we donot know what ?
316         }
317     }
318
319     @Override
320     public void onPortAddedToSubnet(PortAddedToSubnet notification) {
321         Uuid subnetId = notification.getSubnetId();
322         Uuid portId = notification.getPortId();
323
324         logger.info("onPortAddedToSubnet: Port " + portId.getValue() + " being added to subnet " + subnetId.getValue());
325         //TODO(vivek): Change this to use more granularized lock at subnetId level
326         synchronized (this) {
327             try {
328                 InstanceIdentifier<SubnetOpDataEntry> subOpIdentifier = InstanceIdentifier.builder(SubnetOpData.class).
329                         child(SubnetOpDataEntry.class, new SubnetOpDataEntryKey(subnetId)).build();
330
331                 Optional<SubnetOpDataEntry> optionalSubs = VpnUtil.read(broker, LogicalDatastoreType.OPERATIONAL,
332                         subOpIdentifier);
333                 if (!optionalSubs.isPresent()) {
334                     logger.info("onPortAddedToSubnet: Port " + portId.getValue() + " is part of a subnet " + subnetId.getValue() +
335                             " that is not in VPN, ignoring");
336                     return;
337                 }
338                 Interface intfState = InterfaceUtils.getInterfaceStateFromOperDS(broker,portId.getValue());
339                 if (intfState == null) {
340                     // Interface State not yet available
341                     subOpDpnManager.addPortOpDataEntry(portId.getValue(), subnetId, null);
342                     return;
343                 }
344                 BigInteger dpnId = InterfaceUtils.getDpIdFromInterface(intfState);
345                 if (dpnId == null) {
346                     logger.info("onPortAddedToSubnet: Port " + portId.getValue() + " is not assigned DPN yet, ignoring ");
347                     return;
348                 }
349                 subOpDpnManager.addPortOpDataEntry(portId.getValue(), subnetId, dpnId);
350                 if (intfState.getOperStatus() != OperStatus.Up) {
351                     logger.info("onPortAddedToSubnet: Port " + portId.getValue() + " is not UP yet, ignoring ");
352                     return;
353                 }
354                 logger.debug("onPortAddedToSubnet: Updating the SubnetOpDataEntry node for subnet: " + subnetId.getValue());
355                 SubnetToDpn subDpn = subOpDpnManager.addInterfaceToDpn(subnetId, dpnId, portId.getValue());
356                 if (subDpn == null) {
357                     return;
358                 }
359                 SubnetOpDataEntryBuilder subOpBuilder = new SubnetOpDataEntryBuilder(optionalSubs.get());
360                 List<SubnetToDpn> subDpnList = subOpBuilder.getSubnetToDpn();
361                 subDpnList.add(subDpn);
362                 subOpBuilder.setSubnetToDpn(subDpnList);
363                 if (subOpBuilder.getNhDpnId()  == null) {
364                     subOpBuilder.setNhDpnId(dpnId);
365                 }
366                 BigInteger nhDpnId = subOpBuilder.getNhDpnId();
367                 String rd = subOpBuilder.getVrfId();
368                 String subnetIp = subOpBuilder.getSubnetCidr();
369                 String vpnName = subOpBuilder.getVpnName();
370                 Long elanTag = subOpBuilder.getElanTag();
371                 if ((subOpBuilder.getRouteAdvState() == TaskState.Pending) ||
372                         (subOpBuilder.getRouteAdvState() == TaskState.Na)) {
373                     try {
374                         // Write the Subnet Route Entry to FIB
375                         // Advertise BGP Route here and set route_adv_state to DONE
376                         addSubnetRouteToFib(rd, subnetIp, nhDpnId, vpnName, elanTag);
377                         advertiseSubnetRouteToBgp(rd, subnetIp, nhDpnId, vpnName, elanTag);
378                         subOpBuilder.setRouteAdvState(TaskState.Done);
379                     } catch (Exception ex) {
380                         logger.error("onPortAddedToSubnet: Advertising NextHopDPN "+ nhDpnId +
381                                 " information for subnet " + subnetId.getValue() + " to BGP failed {}", ex);
382                     }
383                 }
384                 SubnetOpDataEntry subOpEntry = subOpBuilder.build();
385                 MDSALUtil.syncWrite(broker, LogicalDatastoreType.OPERATIONAL, subOpIdentifier, subOpEntry);
386                 logger.info("onPortAddedToSubnet: Updated subnetopdataentry to OP Datastore for port " + portId.getValue());
387
388             } catch (Exception ex) {
389                 logger.error("Creation of SubnetOpDataEntry for subnet " +
390                         subnetId.getValue() + " failed {}", ex);
391             } finally {
392             }
393         }
394     }
395
396     @Override
397     public void onPortRemovedFromSubnet(PortRemovedFromSubnet notification) {
398         Uuid subnetId = notification.getSubnetId();
399         Uuid portId = notification.getPortId();
400
401         logger.info("onPortRemovedFromSubnet: Port " + portId.getValue() + " being removed from subnet " + subnetId.getValue());
402         //TODO(vivek): Change this to use more granularized lock at subnetId level
403         synchronized (this) {
404             try {
405                 PortOpDataEntry portOpEntry = subOpDpnManager.removePortOpDataEntry(portId.getValue());
406                 if (portOpEntry == null) {
407                     return;
408                 }
409                 BigInteger dpnId = portOpEntry.getDpnId();
410                 if (dpnId == null) {
411                     logger.debug("onPortRemovedFromSubnet:  Port {} does not have a DPNId associated, ignoring", portId.getValue());
412                     return;
413                 }
414                 logger.debug("onPortRemovedFromSubnet: Updating the SubnetOpDataEntry node for subnet: " +  subnetId.getValue());
415                 boolean last = subOpDpnManager.removeInterfaceFromDpn(subnetId, dpnId, portId.getValue());
416                 InstanceIdentifier<SubnetOpDataEntry> subOpIdentifier = InstanceIdentifier.builder(SubnetOpData.class).
417                         child(SubnetOpDataEntry.class, new SubnetOpDataEntryKey(subnetId)).build();
418                 Optional<SubnetOpDataEntry> optionalSubs = VpnUtil.read(broker, LogicalDatastoreType.OPERATIONAL,
419                         subOpIdentifier);
420                 if (!optionalSubs.isPresent()) {
421                     logger.info("onPortRemovedFromSubnet: Port " + portId.getValue() + " is part of a subnet " + subnetId.getValue() +
422                             " that is not in VPN, ignoring");
423                     return;
424                 }
425                 SubnetOpDataEntry subOpEntry = null;
426                 List<SubnetToDpn> subDpnList = null;
427                 SubnetOpDataEntryBuilder subOpBuilder = new SubnetOpDataEntryBuilder(optionalSubs.get());
428                 String rd = subOpBuilder.getVrfId();
429                 String subnetIp = subOpBuilder.getSubnetCidr();
430                 String vpnName = subOpBuilder.getVpnName();
431                 Long elanTag = subOpBuilder.getElanTag();
432                 BigInteger nhDpnId = subOpBuilder.getNhDpnId();
433                 if ((nhDpnId != null) && (nhDpnId.equals(dpnId))) {
434                     // select another NhDpnId
435                     if (last) {
436                         logger.debug("onPortRemovedFromSubnet: Last port " + portId + " on the subnet: " +  subnetId.getValue());
437                         // last port on this DPN, so we need to swap the NHDpnId
438                         subDpnList = subOpBuilder.getSubnetToDpn();
439                         if (subDpnList.isEmpty()) {
440                             subOpBuilder.setNhDpnId(null);
441                             try {
442                                 // withdraw route from BGP
443                                 deleteSubnetRouteFromFib(rd, subnetIp);
444                                 withdrawSubnetRoutefromBgp(rd, subnetIp);
445                                 subOpBuilder.setRouteAdvState(TaskState.Na);
446                             } catch (Exception ex) {
447                                 logger.error("onPortRemovedFromSubnet: Withdrawing NextHopDPN " + dpnId + " information for subnet " +
448                                         subnetId.getValue() + " from BGP failed ", ex);
449                                 subOpBuilder.setRouteAdvState(TaskState.Pending);
450                             }
451                         } else {
452                             nhDpnId = subDpnList.get(0).getDpnId();
453                             subOpBuilder.setNhDpnId(nhDpnId);
454                             logger.debug("onInterfaceDown: Swapping the Designated DPN to " + nhDpnId + " for subnet " + subnetId.getValue());
455                             try {
456                                 // Best effort Withdrawal of route from BGP for this subnet
457                                 // Advertise the new NexthopIP to BGP for this subnet
458                                 withdrawSubnetRoutefromBgp(rd, subnetIp);
459                                 addSubnetRouteToFib(rd, subnetIp, nhDpnId, vpnName, elanTag);
460                                 advertiseSubnetRouteToBgp(rd, subnetIp, nhDpnId, vpnName, elanTag);
461                                 subOpBuilder.setRouteAdvState(TaskState.Done);
462                             } catch (Exception ex) {
463                                 logger.error("onPortRemovedFromSubnet: Swapping Withdrawing NextHopDPN " + dpnId +
464                                         " information for subnet " + subnetId.getValue() +
465                                         " to BGP failed {}" + ex);
466                                 subOpBuilder.setRouteAdvState(TaskState.Pending);
467                             }
468                         }
469                     }
470                 }
471                 subOpEntry = subOpBuilder.build();
472                 MDSALUtil.syncWrite(broker, LogicalDatastoreType.OPERATIONAL, subOpIdentifier, subOpEntry);
473                 logger.info("onPortRemovedFromSubnet: Updated subnetopdataentry to OP Datastore removing port " + portId.getValue());
474             } catch (Exception ex) {
475                 logger.error("Creation of SubnetOpDataEntry for subnet " +
476                         subnetId.getValue() + " failed {}" + ex);
477             } finally {
478             }
479         }
480     }
481
482     public void onInterfaceUp(Interface intfState) {
483
484         logger.info("onInterfaceUp: Port " + intfState.getName());
485         //TODO(vivek): Change this to use more granularized lock at subnetId level
486         synchronized (this) {
487             SubnetToDpn subDpn = null;
488             String intfName = intfState.getName();
489             PortOpDataEntry portOpEntry = subOpDpnManager.getPortOpDataEntry(intfName);
490             if (portOpEntry == null) {
491                 logger.info("onInterfaceUp: Port " + intfState.getName()  + "is part of a subnet not in VPN, ignoring");
492                 return;
493             }
494             BigInteger dpnId = portOpEntry.getDpnId();
495             if (dpnId  == null) {
496                 dpnId = InterfaceUtils.getDpIdFromInterface(intfState);
497                 if (dpnId == null) {
498                     logger.error("onInterfaceUp: Unable to determine the DPNID for port " + intfName);
499                     return;
500                 }
501             }
502             Uuid subnetId = portOpEntry.getSubnetId();
503             try {
504                 InstanceIdentifier<SubnetOpDataEntry> subOpIdentifier = InstanceIdentifier.builder(SubnetOpData.class).
505                         child(SubnetOpDataEntry.class, new SubnetOpDataEntryKey(subnetId)).build();
506                 Optional<SubnetOpDataEntry> optionalSubs = VpnUtil.read(broker, LogicalDatastoreType.OPERATIONAL,
507                         subOpIdentifier);
508                 if (!optionalSubs.isPresent()) {
509                     logger.error("onInterfaceUp: SubnetOpDataEntry for subnet " + subnetId.getValue() +
510                             " is not available");
511                     return;
512                 }
513
514                 SubnetOpDataEntryBuilder subOpBuilder = new SubnetOpDataEntryBuilder(optionalSubs.get());
515                 logger.debug("onInterfaceUp: Updating the SubnetOpDataEntry node for subnet: " +  subnetId.getValue());
516                 subOpDpnManager.addPortOpDataEntry(intfName, subnetId, dpnId);
517                 subDpn = subOpDpnManager.addInterfaceToDpn(subnetId, dpnId, intfName);
518                 if (subDpn == null) {
519                     return;
520                 }
521                 List<SubnetToDpn> subDpnList = subOpBuilder.getSubnetToDpn();
522                 subDpnList.add(subDpn);
523                 subOpBuilder.setSubnetToDpn(subDpnList);
524                 if (subOpBuilder.getNhDpnId()  == null) {
525                     subOpBuilder.setNhDpnId(dpnId);
526                 }
527                 BigInteger nhDpnId = subOpBuilder.getNhDpnId();
528                 String rd = subOpBuilder.getVrfId();
529                 String subnetIp = subOpBuilder.getSubnetCidr();
530                 String vpnName = subOpBuilder.getVpnName();
531                 Long elanTag = subOpBuilder.getElanTag();
532                 if ((subOpBuilder.getRouteAdvState() == TaskState.Pending) || (subOpBuilder.getRouteAdvState() == TaskState.Na)) {
533                     try {
534                         // Write the Subnet Route Entry to FIB
535                         // Advertise BGP Route here and set route_adv_state to DONE
536                         addSubnetRouteToFib(rd, subnetIp, nhDpnId, vpnName, elanTag);
537                         advertiseSubnetRouteToBgp(rd, subnetIp, nhDpnId, vpnName, elanTag);
538                         subOpBuilder.setRouteAdvState(TaskState.Done);
539                     } catch (Exception ex) {
540                         logger.error("onInterfaceUp: Advertising NextHopDPN " + nhDpnId + " information for subnet " +
541                                 subnetId.getValue() + " to BGP failed {}" + ex);
542                     }
543                 }
544                 SubnetOpDataEntry subOpEntry = subOpBuilder.build();
545                 MDSALUtil.syncWrite(broker, LogicalDatastoreType.OPERATIONAL, subOpIdentifier, subOpEntry);
546                 logger.info("onInterfaceUp: Updated subnetopdataentry to OP Datastore port up " + intfName);
547             } catch (Exception ex) {
548                 logger.error("Creation of SubnetOpDataEntry for subnet " +
549                         subnetId.getValue() + " failed {}" + ex);
550             } finally {
551             }
552         }
553     }
554
555     public void onInterfaceDown(Interface intfState) {
556         logger.info("onInterfaceDown: Port " + intfState.getName());
557         //TODO(vivek): Change this to use more granularized lock at subnetId level
558         synchronized (this) {
559             String intfName = intfState.getName();
560             PortOpDataEntry portOpEntry = subOpDpnManager.getPortOpDataEntry(intfName);
561             if (portOpEntry == null) {
562                 logger.info("onInterfaceDown: Port " + intfState.getName()  + "is part of a subnet not in VPN, ignoring");
563                 return;
564             }
565             BigInteger dpnId = portOpEntry.getDpnId();
566             if (dpnId  == null) {
567                 dpnId = InterfaceUtils.getDpIdFromInterface(intfState);
568                 if (dpnId == null) {
569                     logger.error("onInterfaceDown: Unable to determine the DPNID for port " + intfName);
570                     return;
571                 }
572             }
573             Uuid subnetId = portOpEntry.getSubnetId();
574             try {
575                 logger.debug("onInterfaceDown: Updating the SubnetOpDataEntry node for subnet: " +  subnetId.getValue());
576                 boolean last = subOpDpnManager.removeInterfaceFromDpn(subnetId, dpnId, intfName);
577                 InstanceIdentifier<SubnetOpDataEntry> subOpIdentifier = InstanceIdentifier.builder(SubnetOpData.class).
578                         child(SubnetOpDataEntry.class, new SubnetOpDataEntryKey(subnetId)).build();
579                 Optional<SubnetOpDataEntry> optionalSubs = VpnUtil.read(broker,
580                         LogicalDatastoreType.OPERATIONAL,
581                         subOpIdentifier);
582                 if (!optionalSubs.isPresent()) {
583                     logger.error("onInterfaceDown: SubnetOpDataEntry for subnet " + subnetId.getValue() +
584                             " is not available");
585                     return;
586                 }
587                 SubnetOpDataEntry subOpEntry = null;
588                 List<SubnetToDpn> subDpnList = null;
589                 SubnetOpDataEntryBuilder subOpBuilder = new SubnetOpDataEntryBuilder(optionalSubs.get());
590                 String rd = subOpBuilder.getVrfId();
591                 String subnetIp = subOpBuilder.getSubnetCidr();
592                 String vpnName = subOpBuilder.getVpnName();
593                 Long elanTag = subOpBuilder.getElanTag();
594                 BigInteger nhDpnId = subOpBuilder.getNhDpnId();
595                 if ((nhDpnId != null) && (nhDpnId.equals(dpnId))) {
596                     // select another NhDpnId
597                     if (last) {
598                         logger.debug("onInterfaceDown: Last active port " + intfState.getName() + " on the subnet: " +  subnetId.getValue());
599                         // last port on this DPN, so we need to swap the NHDpnId
600                         subDpnList = subOpBuilder.getSubnetToDpn();
601                         if (subDpnList.isEmpty()) {
602                             subOpBuilder.setNhDpnId(null);
603                             try {
604                                 // Withdraw route from BGP for this subnet
605                                 deleteSubnetRouteFromFib(rd, subnetIp);
606                                 withdrawSubnetRoutefromBgp(rd, subnetIp);
607                                 subOpBuilder.setRouteAdvState(TaskState.Na);
608                             } catch (Exception ex) {
609                                 logger.error("onInterfaceDown: Withdrawing NextHopDPN " + dpnId + " information for subnet " +
610                                         subnetId.getValue() + " from BGP failed {}" + ex);
611                                 subOpBuilder.setRouteAdvState(TaskState.Pending);
612                             }
613                         } else {
614                             nhDpnId = subDpnList.get(0).getDpnId();
615                             subOpBuilder.setNhDpnId(nhDpnId);
616                             logger.debug("onInterfaceDown: Swapping the Designated DPN to " + nhDpnId + " for subnet " + subnetId.getValue());
617                             try {
618                                 // Best effort Withdrawal of route from BGP for this subnet
619                                 withdrawSubnetRoutefromBgp(rd, subnetIp);
620                                 addSubnetRouteToFib(rd, subnetIp, nhDpnId, vpnName, elanTag);
621                                 advertiseSubnetRouteToBgp(rd, subnetIp, nhDpnId, vpnName, elanTag);
622                                 subOpBuilder.setRouteAdvState(TaskState.Done);
623                             } catch (Exception ex) {
624                                 logger.error("onInterfaceDown: Swapping Withdrawing NextHopDPN " + dpnId + " information for subnet " +
625                                         subnetId.getValue() + " to BGP failed {}" + ex);
626                                 subOpBuilder.setRouteAdvState(TaskState.Pending);
627                             }
628                         }
629                     }
630                 }
631                 subOpEntry = subOpBuilder.build();
632                 MDSALUtil.syncWrite(broker, LogicalDatastoreType.OPERATIONAL, subOpIdentifier, subOpEntry);
633                 logger.info("onInterfaceDown: Updated subnetopdataentry to OP Datastore port down " + intfName);
634             } catch (Exception ex) {
635                 logger.error("Creation of SubnetOpDataEntry for subnet " +
636                         subnetId.getValue() + " failed {}" + ex);
637             } finally {
638             }
639         }
640     }
641
642     private static void setRdToElanOpEntry(DataBroker broker,
643                                            String rd, String subnetIp, String nextHopIp, String vpnName,
644                                            Long elanTag) {
645         RdToElanOpEntryBuilder rdElanBuilder = null;
646         RdToElanOpEntry rdElan = null;
647
648         try {
649             InstanceIdentifier<RdToElanOpEntry> rdIdentifier = InstanceIdentifier.builder(RdToElanOp.class).
650                     child(RdToElanOpEntry.class, new RdToElanOpEntryKey(rd, subnetIp)).build();
651             Optional<RdToElanOpEntry> optionalRd = VpnUtil.read(broker, LogicalDatastoreType.OPERATIONAL, rdIdentifier);
652             if (!optionalRd.isPresent()) {
653                 // Create PortOpDataEntry only if not present
654                 rdElanBuilder = new RdToElanOpEntryBuilder().setKey(new RdToElanOpEntryKey(rd,subnetIp));
655                 rdElanBuilder.setRd(rd).setSubnetIp(subnetIp).setNextHopIp(nextHopIp);
656                 rdElanBuilder.setElanTag(elanTag);
657                 rdElanBuilder.setVpnName(vpnName);
658                 rdElan = rdElanBuilder.build();
659             } else {
660                 rdElanBuilder = new RdToElanOpEntryBuilder(optionalRd.get());
661                 rdElanBuilder.setRd(rd).setSubnetIp(subnetIp).setNextHopIp(nextHopIp);
662                 rdElanBuilder.setElanTag(elanTag);
663                 rdElanBuilder.setVpnName(vpnName);
664                 rdElan = rdElanBuilder.build();
665             }
666             MDSALUtil.syncWrite(broker, LogicalDatastoreType.OPERATIONAL, rdIdentifier, rdElan);
667             logger.info("Creating RdToElan entry Rd {} SubnetIp {} NextHopIp {} Elan {} " ,rd, subnetIp, nextHopIp, elanTag);
668         } catch (Exception ex) {
669             logger.error("Exception when creating RdToElan entry {}" + ex);
670         } finally {
671         }
672     }
673
674     private void addSubnetRouteToFib(String rd, String subnetIp, BigInteger nhDpnId, String vpnName,
675                                      Long elanTag) {
676         Preconditions.checkNotNull(rd, "RouteDistinguisher cannot be null or empty!");
677         Preconditions.checkNotNull(subnetIp, "SubnetRouteIp cannot be null or empty!");
678         Preconditions.checkNotNull(vpnName, "vpnName cannot be null or empty!");
679         Preconditions.checkNotNull(elanTag, "elanTag cannot be null or empty!");
680         String nexthopIp = null;
681         if (nhDpnId != null) {
682             nexthopIp = InterfaceUtils.getEndpointIpAddressForDPN(broker, nhDpnId);
683         }
684         int label = VpnUtil.getUniqueId(idManager, VpnConstants.VPN_IDPOOL_NAME,
685                 VpnUtil.getNextHopLabelKey(rd, subnetIp));
686         setRdToElanOpEntry(broker, rd, subnetIp, nexthopIp, vpnName, elanTag);
687         vpnInterfaceManager.addFibEntryToDS(rd, subnetIp, nexthopIp, label);
688     }
689
690     private void deleteSubnetRouteFromFib(String rd, String subnetIp) {
691         Preconditions.checkNotNull(rd, "RouteDistinguisher cannot be null or empty!");
692         Preconditions.checkNotNull(subnetIp, "SubnetRouteIp cannot be null or empty!");
693         vpnInterfaceManager.removeFibEntryFromDS(rd, subnetIp);
694     }
695
696     private void advertiseSubnetRouteToBgp(String rd, String subnetIp, BigInteger nhDpnId, String vpnName,
697                                            Long elanTag) throws Exception {
698         Preconditions.checkNotNull(rd, "RouteDistinguisher cannot be null or empty!");
699         Preconditions.checkNotNull(subnetIp, "SubnetRouteIp cannot be null or empty!");
700         Preconditions.checkNotNull(elanTag, "elanTag cannot be null or empty!");
701         Preconditions.checkNotNull(nhDpnId, "nhDpnId cannot be null or empty!");
702         Preconditions.checkNotNull(vpnName, "vpnName cannot be null or empty!");
703         String nexthopIp = null;
704         nexthopIp = InterfaceUtils.getEndpointIpAddressForDPN(broker, nhDpnId);
705         if (nexthopIp == null) {
706             logger.error("createSubnetRouteInVpn: Unable to obtain endpointIp address for DPNId " + nhDpnId);
707             throw new Exception("Unable to obtain endpointIp address for DPNId " + nhDpnId);
708         }
709         int label = VpnUtil.getUniqueId(idManager, VpnConstants.VPN_IDPOOL_NAME,
710                 VpnUtil.getNextHopLabelKey(rd, subnetIp));
711         setRdToElanOpEntry(broker, rd, subnetIp, nexthopIp, vpnName, elanTag);
712         try {
713             bgpManager.advertisePrefix(rd, subnetIp, nexthopIp, label);
714         } catch (Exception e) {
715             logger.error("Subnet route not advertised for rd " + rd + " failed ", e);
716             throw e;
717         }
718     }
719
720     private void withdrawSubnetRoutefromBgp(String rd, String subnetIp) throws Exception {
721         Preconditions.checkNotNull(rd, "RouteDistinguisher cannot be null or empty!");
722         Preconditions.checkNotNull(subnetIp, "SubnetIp cannot be null or empty!");
723         try {
724             bgpManager.withdrawPrefix(rd, subnetIp);
725         } catch (Exception e) {
726             logger.error("Subnet route not advertised for rd " + rd + " failed ", e);
727             throw e;
728         }
729     }
730
731     @Override
732     public void onRouterAssociatedToVpn(RouterAssociatedToVpn notification) {
733     }
734
735     @Override
736     public void onRouterDisassociatedFromVpn(RouterDisassociatedFromVpn notification) {
737
738     }
739 }
740