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