2 * Copyright (c) 2016 Ericsson India Global Services Pvt Ltd. and others. All rights reserved.
4 * This program and the accompanying materials are made available under the
5 * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6 * and is available at http://www.eclipse.org/legal/epl-v10.html
8 package org.opendaylight.vpnservice;
10 import java.util.ArrayList;
11 import java.util.List;
13 import java.util.HashMap;
15 import com.google.common.base.Preconditions;
16 import org.opendaylight.bgpmanager.api.IBgpManager;
17 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
18 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
19 import org.opendaylight.vpnservice.mdsalutil.MDSALUtil;
20 import org.opendaylight.vpnservice.utilities.InterfaceUtils;
21 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.state.Interface.OperStatus;
22 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.Uuid;
23 import org.opendaylight.yang.gen.v1.urn.opendaylight.l3vpn.rev130911.*;
24 import org.opendaylight.yang.gen.v1.urn.opendaylight.l3vpn.rev130911.port.op.data.PortOpDataEntry;
25 import org.opendaylight.yang.gen.v1.urn.opendaylight.l3vpn.rev130911.port.op.data.PortOpDataEntryKey;
26 import org.opendaylight.yang.gen.v1.urn.opendaylight.l3vpn.rev130911.rd.to.elan.op.RdToElanOpEntry;
27 import org.opendaylight.yang.gen.v1.urn.opendaylight.l3vpn.rev130911.rd.to.elan.op.RdToElanOpEntryBuilder;
28 import org.opendaylight.yang.gen.v1.urn.opendaylight.l3vpn.rev130911.rd.to.elan.op.RdToElanOpEntryKey;
29 import org.opendaylight.yang.gen.v1.urn.opendaylight.l3vpn.rev130911.subnet.op.data.SubnetOpDataEntry;
30 import org.opendaylight.yang.gen.v1.urn.opendaylight.l3vpn.rev130911.subnet.op.data.SubnetOpDataEntryKey;
31 import org.opendaylight.yang.gen.v1.urn.opendaylight.l3vpn.rev130911.subnet.op.data.SubnetOpDataEntryBuilder;
32 import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.idmanager.rev150403.IdManagerService;
33 import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.neutronvpn.rev150602.*;
34 import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.neutronvpn.rev150602.subnetmaps.Subnetmap;
35 import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.neutronvpn.rev150602.subnetmaps.SubnetmapKey;
36 import org.opendaylight.yang.gen.v1.urn.opendaylight.l3vpn.rev130911.subnet.op.data.subnet.op.data.entry.SubnetToDpn;
37 import org.opendaylight.yang.gen.v1.urn.opendaylight.l3vpn.rev130911.subnet.op.data.subnet.op.data.entry.subnet.to.dpn.VpnInterfaces;
38 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.state.Interface;
39 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
41 import java.math.BigInteger;
43 import org.slf4j.Logger;
44 import org.slf4j.LoggerFactory;
46 import com.google.common.base.Optional;
49 public class VpnSubnetRouteHandler implements NeutronvpnListener {
50 private static final Logger logger = LoggerFactory.getLogger(VpnSubnetRouteHandler.class);
52 private final DataBroker broker;
53 private SubnetOpDpnManager subOpDpnManager;
54 private final IBgpManager bgpManager;
55 private IdManagerService idManager;
56 private VpnInterfaceManager vpnInterfaceManager;
58 public VpnSubnetRouteHandler(final DataBroker db, IBgpManager bgpManager, VpnInterfaceManager vpnIntfManager) {
60 subOpDpnManager = new SubnetOpDpnManager(broker);
61 this.bgpManager = bgpManager;
62 this.vpnInterfaceManager = vpnIntfManager;
65 public void setIdManager(IdManagerService idManager) {
66 this.idManager = idManager;
70 public void onSubnetAddedToVpn(SubnetAddedToVpn notification) {
71 if (!notification.isExternalVpn()) {
75 Uuid subnetId = notification.getSubnetId();
76 String vpnName = notification.getVpnName();
77 String subnetIp = notification.getSubnetIp();
78 Long elanTag = notification.getElanTag();
80 Preconditions.checkNotNull(subnetId, "SubnetId cannot be null or empty!");
81 Preconditions.checkNotNull(subnetIp, "SubnetPrefix cannot be null or empty!");
82 Preconditions.checkNotNull(vpnName, "VpnName cannot be null or empty!");
83 Preconditions.checkNotNull(elanTag, "ElanTag cannot be null or empty!");
85 logger.info("onSubnetAddedToVpn: Subnet" + subnetId.getValue() + " being added to vpn");
86 //TODO(vivek): Change this to use more granularized lock at subnetId level
89 //Create and add SubnetOpDataEntry object for this subnet to the SubnetOpData container
90 InstanceIdentifier<SubnetOpDataEntry> subOpIdentifier = InstanceIdentifier.builder(SubnetOpData.class).
91 child(SubnetOpDataEntry.class, new SubnetOpDataEntryKey(subnetId)).build();
92 Optional<SubnetOpDataEntry> optionalSubs = VpnUtil.read(broker,
93 LogicalDatastoreType.OPERATIONAL,
95 if (optionalSubs.isPresent()) {
96 logger.error("onSubnetAddedToVpn: SubnetOpDataEntry for subnet " + subnetId.getValue() +
97 " already detected to be present");
100 logger.debug("onSubnetAddedToVpn: Creating new SubnetOpDataEntry node for subnet: " + subnetId.getValue());
101 Map<BigInteger, SubnetToDpn> subDpnMap = new HashMap<BigInteger, SubnetToDpn>();
102 SubnetOpDataEntry subOpEntry = null;
103 BigInteger dpnId = null;
104 BigInteger nhDpnId = null;
105 SubnetToDpn subDpn = null;
107 SubnetOpDataEntryBuilder subOpBuilder = new SubnetOpDataEntryBuilder().setKey(new SubnetOpDataEntryKey(subnetId));
108 subOpBuilder.setSubnetId(subnetId);
109 subOpBuilder.setSubnetCidr(subnetIp);
110 String rd = VpnUtil.getVpnRdFromVpnInstanceConfig(broker, vpnName);
112 logger.error("onSubnetAddedToVpn: The VPN Instance name " + notification.getVpnName() + " does not have RD ");
115 subOpBuilder.setVrfId(rd);
116 subOpBuilder.setVpnName(vpnName);
117 subOpBuilder.setSubnetToDpn(new ArrayList<SubnetToDpn>());
118 subOpBuilder.setRouteAdvState(TaskState.Na);
119 subOpBuilder.setElanTag(elanTag);
121 // First recover set of ports available in this subnet
122 InstanceIdentifier<Subnetmap> subMapid = InstanceIdentifier.builder(Subnetmaps.class).
123 child(Subnetmap.class, new SubnetmapKey(subnetId)).build();
124 Optional<Subnetmap> sm = VpnUtil.read(broker, LogicalDatastoreType.CONFIGURATION, subMapid);
125 if (!sm.isPresent()) {
126 logger.error("onSubnetAddedToVpn: Unable to retrieve subnetmap entry for subnet : " + subnetId);
129 Subnetmap subMap = sm.get();
130 List<Uuid> portList = subMap.getPortList();
131 if (portList != null) {
132 for (Uuid port: portList) {
133 Interface intfState = InterfaceUtils.getInterfaceStateFromOperDS(broker,port.getValue());
134 if (intfState != null) {
135 dpnId = InterfaceUtils.getDpIdFromInterface(intfState);
137 logger.info("onSubnetAddedToVpn: Port " + port.getValue() + " is not assigned DPN yet, ignoring ");
140 subOpDpnManager.addPortOpDataEntry(port.getValue(), subnetId, dpnId);
141 if (intfState.getOperStatus() != OperStatus.Up) {
142 logger.info("onSubnetAddedToVpn: Port " + port.getValue() + " is not UP yet, ignoring ");
145 subDpn = subOpDpnManager.addInterfaceToDpn(subnetId, dpnId, port.getValue());
146 if (intfState.getOperStatus() == OperStatus.Up) {
148 subDpnMap.put(dpnId, subDpn);
149 if (nhDpnId == null) {
154 subOpDpnManager.addPortOpDataEntry(port.getValue(), subnetId, null);
157 if (subDpnMap.size() > 0) {
158 subOpBuilder.setSubnetToDpn(new ArrayList<SubnetToDpn>(subDpnMap.values()));
162 if (nhDpnId != null) {
163 subOpBuilder.setNhDpnId(nhDpnId);
166 Write the subnet route entry to the FIB.
167 And also advertise the subnet route entry via BGP.
169 addSubnetRouteToFib(rd, subnetIp, nhDpnId, vpnName, elanTag);
170 advertiseSubnetRouteToBgp(rd, subnetIp, nhDpnId, vpnName, elanTag);
171 subOpBuilder.setRouteAdvState(TaskState.Done);
172 } catch (Exception ex) {
173 logger.error("onSubnetAddedToVpn: FIB rules and Advertising nhDpnId " + nhDpnId +
174 " information for subnet " + subnetId.getValue() + " to BGP failed {}", ex);
175 subOpBuilder.setRouteAdvState(TaskState.Pending);
180 Write the subnet route entry to the FIB.
181 NOTE: Will not advertise to BGP as NextHopDPN is not available yet.
183 addSubnetRouteToFib(rd, subnetIp, null, vpnName, elanTag);
184 } catch (Exception ex) {
185 logger.error("onSubnetAddedToVpn: FIB rules writing for subnet {} with exception {} " +
186 subnetId.getValue(), ex);
187 subOpBuilder.setRouteAdvState(TaskState.Pending);
191 subOpEntry = subOpBuilder.build();
192 MDSALUtil.syncWrite(broker, LogicalDatastoreType.OPERATIONAL, subOpIdentifier, subOpEntry);
193 logger.info("onSubnetAddedToVpn: Added subnetopdataentry to OP Datastore for subnet " + subnetId.getValue());
194 } catch (Exception ex) {
195 logger.error("Creation of SubnetOpDataEntry for subnet " +
196 subnetId.getValue() + " failed {}", ex);
203 public void onSubnetDeletedFromVpn(SubnetDeletedFromVpn notification) {
204 Uuid subnetId = notification.getSubnetId();
206 if (!notification.isExternalVpn()) {
209 logger.info("onSubnetDeletedFromVpn: Subnet" + subnetId.getValue() + " being removed to vpn");
210 //TODO(vivek): Change this to use more granularized lock at subnetId level
211 synchronized (this) {
213 InstanceIdentifier<SubnetOpDataEntry> subOpIdentifier = InstanceIdentifier.builder(SubnetOpData.class).
214 child(SubnetOpDataEntry.class, new SubnetOpDataEntryKey(subnetId)).build();
215 logger.trace(" Removing the SubnetOpDataEntry node for subnet: " + subnetId.getValue());
216 Optional<SubnetOpDataEntry> optionalSubs = VpnUtil.read(broker,
217 LogicalDatastoreType.OPERATIONAL,
219 if (!optionalSubs.isPresent()) {
220 logger.error("onSubnetDeletedFromVpn: SubnetOpDataEntry for subnet " + subnetId.getValue() +
221 " not available in datastore");
224 SubnetOpDataEntryBuilder subOpBuilder = new SubnetOpDataEntryBuilder(optionalSubs.get());
225 List<SubnetToDpn> subDpnList = subOpBuilder.getSubnetToDpn();
226 for (SubnetToDpn subDpn: subDpnList) {
227 List<VpnInterfaces> vpnIntfList = subDpn.getVpnInterfaces();
228 for (VpnInterfaces vpnIntf: vpnIntfList) {
229 subOpDpnManager.removePortOpDataEntry(vpnIntf.getInterfaceName());
232 //Removing Stale Ports in portOpData
233 InstanceIdentifier<Subnetmap> subMapid = InstanceIdentifier.builder(Subnetmaps.class).
234 child(Subnetmap.class, new SubnetmapKey(subnetId)).build();
235 Optional<Subnetmap> sm = VpnUtil.read(broker, LogicalDatastoreType.CONFIGURATION, subMapid);
236 if (!sm.isPresent()) {
237 logger.error("Stale ports removal: Unable to retrieve subnetmap entry for subnet : " + subnetId);
239 Subnetmap subMap = sm.get();
240 List<Uuid> portList = subMap.getPortList();
242 InstanceIdentifier<PortOpData> portOpIdentifier = InstanceIdentifier.builder(PortOpData.class).build();
243 Optional<PortOpData> optionalPortOp = VpnUtil.read(broker, LogicalDatastoreType.OPERATIONAL, portOpIdentifier);
244 if(!optionalPortOp.isPresent()){
245 logger.error("Stale ports removal: Cannot delete port. Not available in data store");
248 PortOpData portOpData = optionalPortOp.get();
249 List<PortOpDataEntry> portOpDataList = portOpData.getPortOpDataEntry();
250 if(portOpDataList!=null){
251 for(PortOpDataEntry portOpDataListEntry : portOpDataList){
252 if(portList.contains(new Uuid(portOpDataListEntry.getPortId()))){
253 logger.trace("Removing stale port: " + portOpDataListEntry + "for dissociated subnetId: " + subnetId);
254 MDSALUtil.syncDelete(broker, LogicalDatastoreType.OPERATIONAL, portOpIdentifier.
255 child(PortOpDataEntry.class, new PortOpDataEntryKey(portOpDataListEntry.getKey())));
262 String rd = subOpBuilder.getVrfId();
263 String subnetIp = subOpBuilder.getSubnetCidr();
264 BigInteger nhDpnId = subOpBuilder.getNhDpnId();
265 MDSALUtil.syncDelete(broker, LogicalDatastoreType.OPERATIONAL, subOpIdentifier);
266 logger.trace("Removed subnetopdataentry successfully to CONFIG Datastore");
268 //Withdraw the routes for all the interfaces on this subnet
269 //Remove subnet route entry from FIB
270 withdrawSubnetRoutefromBgp(rd, subnetIp);
271 deleteSubnetRouteFromFib(rd, subnetIp);
272 } catch (Exception ex) {
273 logger.error("onSubnetAddedToVpn: Withdrawing routes from BGP for subnet " +
274 subnetId.getValue() + " failed {}" + ex);
276 } catch (Exception ex) {
277 logger.error("Removal of SubnetOpDataEntry for subnet " +
278 subnetId.getValue() + " failed {}" + ex);
285 public void onSubnetUpdatedInVpn(SubnetUpdatedInVpn notification) {
286 Uuid subnetId = notification.getSubnetId();
287 String vpnName = notification.getVpnName();
288 String subnetIp = notification.getSubnetIp();
289 Long elanTag = notification.getElanTag();
291 Preconditions.checkNotNull(subnetId, "SubnetId cannot be null or empty!");
292 Preconditions.checkNotNull(subnetIp, "SubnetPrefix cannot be null or empty!");
293 Preconditions.checkNotNull(vpnName, "VpnName cannot be null or empty!");
294 Preconditions.checkNotNull(elanTag, "ElanTag cannot be null or empty!");
296 InstanceIdentifier<SubnetOpDataEntry> subOpIdentifier = InstanceIdentifier.builder(SubnetOpData.class).
297 child(SubnetOpDataEntry.class, new SubnetOpDataEntryKey(subnetId)).build();
298 Optional<SubnetOpDataEntry> optionalSubs = VpnUtil.read(broker,
299 LogicalDatastoreType.OPERATIONAL,
301 if (optionalSubs.isPresent()) {
302 if (!notification.isExternalVpn()) {
303 SubnetDeletedFromVpnBuilder bldr = new SubnetDeletedFromVpnBuilder().setVpnName(vpnName);
304 bldr.setElanTag(elanTag).setExternalVpn(true).setSubnetIp(subnetIp).setSubnetId(subnetId);
305 onSubnetDeletedFromVpn(bldr.build());
307 // TODO(vivek): Something got updated, but we donot know what ?
309 if (notification.isExternalVpn()) {
310 SubnetAddedToVpnBuilder bldr = new SubnetAddedToVpnBuilder().setVpnName(vpnName).setElanTag(elanTag);
311 bldr.setSubnetIp(subnetIp).setSubnetId(subnetId).setExternalVpn(true);;
312 onSubnetAddedToVpn(bldr.build());
314 // TODO(vivek): Something got updated, but we donot know what ?
319 public void onPortAddedToSubnet(PortAddedToSubnet notification) {
320 Uuid subnetId = notification.getSubnetId();
321 Uuid portId = notification.getPortId();
323 logger.info("onPortAddedToSubnet: Port " + portId.getValue() + " being added to subnet " + subnetId.getValue());
324 //TODO(vivek): Change this to use more granularized lock at subnetId level
325 synchronized (this) {
327 InstanceIdentifier<SubnetOpDataEntry> subOpIdentifier = InstanceIdentifier.builder(SubnetOpData.class).
328 child(SubnetOpDataEntry.class, new SubnetOpDataEntryKey(subnetId)).build();
330 Optional<SubnetOpDataEntry> optionalSubs = VpnUtil.read(broker, LogicalDatastoreType.OPERATIONAL,
332 if (!optionalSubs.isPresent()) {
333 logger.info("onPortAddedToSubnet: Port " + portId.getValue() + " is part of a subnet " + subnetId.getValue() +
334 " that is not in VPN, ignoring");
337 Interface intfState = InterfaceUtils.getInterfaceStateFromOperDS(broker,portId.getValue());
338 if (intfState == null) {
339 // Interface State not yet available
340 subOpDpnManager.addPortOpDataEntry(portId.getValue(), subnetId, null);
343 BigInteger dpnId = InterfaceUtils.getDpIdFromInterface(intfState);
345 logger.info("onPortAddedToSubnet: Port " + portId.getValue() + " is not assigned DPN yet, ignoring ");
348 subOpDpnManager.addPortOpDataEntry(portId.getValue(), subnetId, dpnId);
349 if (intfState.getOperStatus() != OperStatus.Up) {
350 logger.info("onPortAddedToSubnet: Port " + portId.getValue() + " is not UP yet, ignoring ");
353 logger.debug("onPortAddedToSubnet: Updating the SubnetOpDataEntry node for subnet: " + subnetId.getValue());
354 SubnetToDpn subDpn = subOpDpnManager.addInterfaceToDpn(subnetId, dpnId, portId.getValue());
355 if (subDpn == null) {
358 SubnetOpDataEntryBuilder subOpBuilder = new SubnetOpDataEntryBuilder(optionalSubs.get());
359 List<SubnetToDpn> subDpnList = subOpBuilder.getSubnetToDpn();
360 subDpnList.add(subDpn);
361 subOpBuilder.setSubnetToDpn(subDpnList);
362 if (subOpBuilder.getNhDpnId() == null) {
363 subOpBuilder.setNhDpnId(dpnId);
365 BigInteger nhDpnId = subOpBuilder.getNhDpnId();
366 String rd = subOpBuilder.getVrfId();
367 String subnetIp = subOpBuilder.getSubnetCidr();
368 String vpnName = subOpBuilder.getVpnName();
369 Long elanTag = subOpBuilder.getElanTag();
370 if ((subOpBuilder.getRouteAdvState() == TaskState.Pending) ||
371 (subOpBuilder.getRouteAdvState() == TaskState.Na)) {
373 // Write the Subnet Route Entry to FIB
374 // Advertise BGP Route here and set route_adv_state to DONE
375 addSubnetRouteToFib(rd, subnetIp, nhDpnId, vpnName, elanTag);
376 advertiseSubnetRouteToBgp(rd, subnetIp, nhDpnId, vpnName, elanTag);
377 subOpBuilder.setRouteAdvState(TaskState.Done);
378 } catch (Exception ex) {
379 logger.error("onPortAddedToSubnet: Advertising NextHopDPN "+ nhDpnId +
380 " information for subnet " + subnetId.getValue() + " to BGP failed {}", ex);
383 SubnetOpDataEntry subOpEntry = subOpBuilder.build();
384 MDSALUtil.syncWrite(broker, LogicalDatastoreType.OPERATIONAL, subOpIdentifier, subOpEntry);
385 logger.info("onPortAddedToSubnet: Updated subnetopdataentry to OP Datastore for port " + portId.getValue());
387 } catch (Exception ex) {
388 logger.error("Creation of SubnetOpDataEntry for subnet " +
389 subnetId.getValue() + " failed {}", ex);
396 public void onPortRemovedFromSubnet(PortRemovedFromSubnet notification) {
397 Uuid subnetId = notification.getSubnetId();
398 Uuid portId = notification.getPortId();
400 logger.info("onPortRemovedFromSubnet: Port " + portId.getValue() + " being removed from subnet " + subnetId.getValue());
401 //TODO(vivek): Change this to use more granularized lock at subnetId level
402 synchronized (this) {
404 PortOpDataEntry portOpEntry = subOpDpnManager.removePortOpDataEntry(portId.getValue());
405 if (portOpEntry == null) {
408 BigInteger dpnId = portOpEntry.getDpnId();
410 logger.debug("onPortRemovedFromSubnet: Port {} does not have a DPNId associated, ignoring", portId.getValue());
413 logger.debug("onPortRemovedFromSubnet: Updating the SubnetOpDataEntry node for subnet: " + subnetId.getValue());
414 boolean last = subOpDpnManager.removeInterfaceFromDpn(subnetId, dpnId, portId.getValue());
415 InstanceIdentifier<SubnetOpDataEntry> subOpIdentifier = InstanceIdentifier.builder(SubnetOpData.class).
416 child(SubnetOpDataEntry.class, new SubnetOpDataEntryKey(subnetId)).build();
417 Optional<SubnetOpDataEntry> optionalSubs = VpnUtil.read(broker, LogicalDatastoreType.OPERATIONAL,
419 if (!optionalSubs.isPresent()) {
420 logger.info("onPortRemovedFromSubnet: Port " + portId.getValue() + " is part of a subnet " + subnetId.getValue() +
421 " that is not in VPN, ignoring");
424 SubnetOpDataEntry subOpEntry = null;
425 List<SubnetToDpn> subDpnList = null;
426 SubnetOpDataEntryBuilder subOpBuilder = new SubnetOpDataEntryBuilder(optionalSubs.get());
427 String rd = subOpBuilder.getVrfId();
428 String subnetIp = subOpBuilder.getSubnetCidr();
429 String vpnName = subOpBuilder.getVpnName();
430 Long elanTag = subOpBuilder.getElanTag();
431 BigInteger nhDpnId = subOpBuilder.getNhDpnId();
432 if ((nhDpnId != null) && (nhDpnId.equals(dpnId))) {
433 // select another NhDpnId
435 logger.debug("onPortRemovedFromSubnet: Last port " + portId + " on the subnet: " + subnetId.getValue());
436 // last port on this DPN, so we need to swap the NHDpnId
437 subDpnList = subOpBuilder.getSubnetToDpn();
438 if (subDpnList.isEmpty()) {
439 subOpBuilder.setNhDpnId(null);
441 // withdraw route from BGP
442 deleteSubnetRouteFromFib(rd, subnetIp);
443 withdrawSubnetRoutefromBgp(rd, subnetIp);
444 subOpBuilder.setRouteAdvState(TaskState.Na);
445 } catch (Exception ex) {
446 logger.error("onPortRemovedFromSubnet: Withdrawing NextHopDPN " + dpnId + " information for subnet " +
447 subnetId.getValue() + " from BGP failed ", ex);
448 subOpBuilder.setRouteAdvState(TaskState.Pending);
451 nhDpnId = subDpnList.get(0).getDpnId();
452 subOpBuilder.setNhDpnId(nhDpnId);
453 logger.debug("onInterfaceDown: Swapping the Designated DPN to " + nhDpnId + " for subnet " + subnetId.getValue());
455 // Best effort Withdrawal of route from BGP for this subnet
456 // Advertise the new NexthopIP to BGP for this subnet
457 withdrawSubnetRoutefromBgp(rd, subnetIp);
458 addSubnetRouteToFib(rd, subnetIp, nhDpnId, vpnName, elanTag);
459 advertiseSubnetRouteToBgp(rd, subnetIp, nhDpnId, vpnName, elanTag);
460 subOpBuilder.setRouteAdvState(TaskState.Done);
461 } catch (Exception ex) {
462 logger.error("onPortRemovedFromSubnet: Swapping Withdrawing NextHopDPN " + dpnId +
463 " information for subnet " + subnetId.getValue() +
464 " to BGP failed {}" + ex);
465 subOpBuilder.setRouteAdvState(TaskState.Pending);
470 subOpEntry = subOpBuilder.build();
471 MDSALUtil.syncWrite(broker, LogicalDatastoreType.OPERATIONAL, subOpIdentifier, subOpEntry);
472 logger.info("onPortRemovedFromSubnet: Updated subnetopdataentry to OP Datastore removing port " + portId.getValue());
473 } catch (Exception ex) {
474 logger.error("Creation of SubnetOpDataEntry for subnet " +
475 subnetId.getValue() + " failed {}" + ex);
481 public void onInterfaceUp(Interface intfState) {
483 logger.info("onInterfaceUp: Port " + intfState.getName());
484 //TODO(vivek): Change this to use more granularized lock at subnetId level
485 synchronized (this) {
486 SubnetToDpn subDpn = null;
487 String intfName = intfState.getName();
488 PortOpDataEntry portOpEntry = subOpDpnManager.getPortOpDataEntry(intfName);
489 if (portOpEntry == null) {
490 logger.info("onInterfaceUp: Port " + intfState.getName() + "is part of a subnet not in VPN, ignoring");
493 BigInteger dpnId = portOpEntry.getDpnId();
495 dpnId = InterfaceUtils.getDpIdFromInterface(intfState);
497 logger.error("onInterfaceUp: Unable to determine the DPNID for port " + intfName);
501 Uuid subnetId = portOpEntry.getSubnetId();
503 InstanceIdentifier<SubnetOpDataEntry> subOpIdentifier = InstanceIdentifier.builder(SubnetOpData.class).
504 child(SubnetOpDataEntry.class, new SubnetOpDataEntryKey(subnetId)).build();
505 Optional<SubnetOpDataEntry> optionalSubs = VpnUtil.read(broker, LogicalDatastoreType.OPERATIONAL,
507 if (!optionalSubs.isPresent()) {
508 logger.error("onInterfaceUp: SubnetOpDataEntry for subnet " + subnetId.getValue() +
509 " is not available");
513 SubnetOpDataEntryBuilder subOpBuilder = new SubnetOpDataEntryBuilder(optionalSubs.get());
514 logger.debug("onInterfaceUp: Updating the SubnetOpDataEntry node for subnet: " + subnetId.getValue());
515 subOpDpnManager.addPortOpDataEntry(intfName, subnetId, dpnId);
516 subDpn = subOpDpnManager.addInterfaceToDpn(subnetId, dpnId, intfName);
517 if (subDpn == null) {
520 List<SubnetToDpn> subDpnList = subOpBuilder.getSubnetToDpn();
521 subDpnList.add(subDpn);
522 subOpBuilder.setSubnetToDpn(subDpnList);
523 if (subOpBuilder.getNhDpnId() == null) {
524 subOpBuilder.setNhDpnId(dpnId);
526 BigInteger nhDpnId = subOpBuilder.getNhDpnId();
527 String rd = subOpBuilder.getVrfId();
528 String subnetIp = subOpBuilder.getSubnetCidr();
529 String vpnName = subOpBuilder.getVpnName();
530 Long elanTag = subOpBuilder.getElanTag();
531 if ((subOpBuilder.getRouteAdvState() == TaskState.Pending) || (subOpBuilder.getRouteAdvState() == TaskState.Na)) {
533 // Write the Subnet Route Entry to FIB
534 // Advertise BGP Route here and set route_adv_state to DONE
535 addSubnetRouteToFib(rd, subnetIp, nhDpnId, vpnName, elanTag);
536 advertiseSubnetRouteToBgp(rd, subnetIp, nhDpnId, vpnName, elanTag);
537 subOpBuilder.setRouteAdvState(TaskState.Done);
538 } catch (Exception ex) {
539 logger.error("onInterfaceUp: Advertising NextHopDPN " + nhDpnId + " information for subnet " +
540 subnetId.getValue() + " to BGP failed {}" + ex);
543 SubnetOpDataEntry subOpEntry = subOpBuilder.build();
544 MDSALUtil.syncWrite(broker, LogicalDatastoreType.OPERATIONAL, subOpIdentifier, subOpEntry);
545 logger.info("onInterfaceUp: Updated subnetopdataentry to OP Datastore port up " + intfName);
546 } catch (Exception ex) {
547 logger.error("Creation of SubnetOpDataEntry for subnet " +
548 subnetId.getValue() + " failed {}" + ex);
554 public void onInterfaceDown(Interface intfState) {
555 logger.info("onInterfaceDown: Port " + intfState.getName());
556 //TODO(vivek): Change this to use more granularized lock at subnetId level
557 synchronized (this) {
558 String intfName = intfState.getName();
559 PortOpDataEntry portOpEntry = subOpDpnManager.getPortOpDataEntry(intfName);
560 if (portOpEntry == null) {
561 logger.info("onInterfaceDown: Port " + intfState.getName() + "is part of a subnet not in VPN, ignoring");
564 BigInteger dpnId = portOpEntry.getDpnId();
566 dpnId = InterfaceUtils.getDpIdFromInterface(intfState);
568 logger.error("onInterfaceDown: Unable to determine the DPNID for port " + intfName);
572 Uuid subnetId = portOpEntry.getSubnetId();
574 logger.debug("onInterfaceDown: Updating the SubnetOpDataEntry node for subnet: " + subnetId.getValue());
575 boolean last = subOpDpnManager.removeInterfaceFromDpn(subnetId, dpnId, intfName);
576 InstanceIdentifier<SubnetOpDataEntry> subOpIdentifier = InstanceIdentifier.builder(SubnetOpData.class).
577 child(SubnetOpDataEntry.class, new SubnetOpDataEntryKey(subnetId)).build();
578 Optional<SubnetOpDataEntry> optionalSubs = VpnUtil.read(broker,
579 LogicalDatastoreType.OPERATIONAL,
581 if (!optionalSubs.isPresent()) {
582 logger.error("onInterfaceDown: SubnetOpDataEntry for subnet " + subnetId.getValue() +
583 " is not available");
586 SubnetOpDataEntry subOpEntry = null;
587 List<SubnetToDpn> subDpnList = null;
588 SubnetOpDataEntryBuilder subOpBuilder = new SubnetOpDataEntryBuilder(optionalSubs.get());
589 String rd = subOpBuilder.getVrfId();
590 String subnetIp = subOpBuilder.getSubnetCidr();
591 String vpnName = subOpBuilder.getVpnName();
592 Long elanTag = subOpBuilder.getElanTag();
593 BigInteger nhDpnId = subOpBuilder.getNhDpnId();
594 if ((nhDpnId != null) && (nhDpnId.equals(dpnId))) {
595 // select another NhDpnId
597 logger.debug("onInterfaceDown: Last active port " + intfState.getName() + " on the subnet: " + subnetId.getValue());
598 // last port on this DPN, so we need to swap the NHDpnId
599 subDpnList = subOpBuilder.getSubnetToDpn();
600 if (subDpnList.isEmpty()) {
601 subOpBuilder.setNhDpnId(null);
603 // Withdraw route from BGP for this subnet
604 deleteSubnetRouteFromFib(rd, subnetIp);
605 withdrawSubnetRoutefromBgp(rd, subnetIp);
606 subOpBuilder.setRouteAdvState(TaskState.Na);
607 } catch (Exception ex) {
608 logger.error("onInterfaceDown: Withdrawing NextHopDPN " + dpnId + " information for subnet " +
609 subnetId.getValue() + " from BGP failed {}" + ex);
610 subOpBuilder.setRouteAdvState(TaskState.Pending);
613 nhDpnId = subDpnList.get(0).getDpnId();
614 subOpBuilder.setNhDpnId(nhDpnId);
615 logger.debug("onInterfaceDown: Swapping the Designated DPN to " + nhDpnId + " for subnet " + subnetId.getValue());
617 // Best effort Withdrawal of route from BGP for this subnet
618 withdrawSubnetRoutefromBgp(rd, subnetIp);
619 addSubnetRouteToFib(rd, subnetIp, nhDpnId, vpnName, elanTag);
620 advertiseSubnetRouteToBgp(rd, subnetIp, nhDpnId, vpnName, elanTag);
621 subOpBuilder.setRouteAdvState(TaskState.Done);
622 } catch (Exception ex) {
623 logger.error("onInterfaceDown: Swapping Withdrawing NextHopDPN " + dpnId + " information for subnet " +
624 subnetId.getValue() + " to BGP failed {}" + ex);
625 subOpBuilder.setRouteAdvState(TaskState.Pending);
630 subOpEntry = subOpBuilder.build();
631 MDSALUtil.syncWrite(broker, LogicalDatastoreType.OPERATIONAL, subOpIdentifier, subOpEntry);
632 logger.info("onInterfaceDown: Updated subnetopdataentry to OP Datastore port down " + intfName);
633 } catch (Exception ex) {
634 logger.error("Creation of SubnetOpDataEntry for subnet " +
635 subnetId.getValue() + " failed {}" + ex);
641 private static void setRdToElanOpEntry(DataBroker broker,
642 String rd, String subnetIp, String nextHopIp, String vpnName,
644 RdToElanOpEntryBuilder rdElanBuilder = null;
645 RdToElanOpEntry rdElan = null;
648 InstanceIdentifier<RdToElanOpEntry> rdIdentifier = InstanceIdentifier.builder(RdToElanOp.class).
649 child(RdToElanOpEntry.class, new RdToElanOpEntryKey(rd, subnetIp)).build();
650 Optional<RdToElanOpEntry> optionalRd = VpnUtil.read(broker, LogicalDatastoreType.OPERATIONAL, rdIdentifier);
651 if (!optionalRd.isPresent()) {
652 // Create PortOpDataEntry only if not present
653 rdElanBuilder = new RdToElanOpEntryBuilder().setKey(new RdToElanOpEntryKey(rd,subnetIp));
654 rdElanBuilder.setRd(rd).setSubnetIp(subnetIp).setNextHopIp(nextHopIp);
655 rdElanBuilder.setElanTag(elanTag);
656 rdElanBuilder.setVpnName(vpnName);
657 rdElan = rdElanBuilder.build();
659 rdElanBuilder = new RdToElanOpEntryBuilder(optionalRd.get());
660 rdElanBuilder.setRd(rd).setSubnetIp(subnetIp).setNextHopIp(nextHopIp);
661 rdElanBuilder.setElanTag(elanTag);
662 rdElanBuilder.setVpnName(vpnName);
663 rdElan = rdElanBuilder.build();
665 MDSALUtil.syncWrite(broker, LogicalDatastoreType.OPERATIONAL, rdIdentifier, rdElan);
666 logger.info("Creating RdToElan entry Rd {} SubnetIp {} NextHopIp {} Elan {} " ,rd, subnetIp, nextHopIp, elanTag);
667 } catch (Exception ex) {
668 logger.error("Exception when creating RdToElan entry {}" + ex);
673 private void addSubnetRouteToFib(String rd, String subnetIp, BigInteger nhDpnId, String vpnName,
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);
683 int label = VpnUtil.getUniqueId(idManager, VpnConstants.VPN_IDPOOL_NAME,
684 VpnUtil.getNextHopLabelKey(rd, subnetIp));
685 setRdToElanOpEntry(broker, rd, subnetIp, nexthopIp, vpnName, elanTag);
686 vpnInterfaceManager.addFibEntryToDS(rd, subnetIp, nexthopIp, label);
689 private void deleteSubnetRouteFromFib(String rd, String subnetIp) {
690 Preconditions.checkNotNull(rd, "RouteDistinguisher cannot be null or empty!");
691 Preconditions.checkNotNull(subnetIp, "SubnetRouteIp cannot be null or empty!");
692 vpnInterfaceManager.removeFibEntryFromDS(rd, subnetIp);
695 private void advertiseSubnetRouteToBgp(String rd, String subnetIp, BigInteger nhDpnId, String vpnName,
696 Long elanTag) throws Exception {
697 Preconditions.checkNotNull(rd, "RouteDistinguisher cannot be null or empty!");
698 Preconditions.checkNotNull(subnetIp, "SubnetRouteIp cannot be null or empty!");
699 Preconditions.checkNotNull(elanTag, "elanTag cannot be null or empty!");
700 Preconditions.checkNotNull(nhDpnId, "nhDpnId cannot be null or empty!");
701 Preconditions.checkNotNull(vpnName, "vpnName cannot be null or empty!");
702 String nexthopIp = null;
703 nexthopIp = InterfaceUtils.getEndpointIpAddressForDPN(broker, nhDpnId);
704 if (nexthopIp == null) {
705 logger.error("createSubnetRouteInVpn: Unable to obtain endpointIp address for DPNId " + nhDpnId);
706 throw new Exception("Unable to obtain endpointIp address for DPNId " + nhDpnId);
708 int label = VpnUtil.getUniqueId(idManager, VpnConstants.VPN_IDPOOL_NAME,
709 VpnUtil.getNextHopLabelKey(rd, subnetIp));
710 setRdToElanOpEntry(broker, rd, subnetIp, nexthopIp, vpnName, elanTag);
712 bgpManager.advertisePrefix(rd, subnetIp, nexthopIp, label);
713 } catch (Exception e) {
714 logger.error("Subnet route not advertised for rd " + rd + " failed ", e);
719 private void withdrawSubnetRoutefromBgp(String rd, String subnetIp) throws Exception {
720 Preconditions.checkNotNull(rd, "RouteDistinguisher cannot be null or empty!");
721 Preconditions.checkNotNull(subnetIp, "SubnetIp cannot be null or empty!");
723 bgpManager.withdrawPrefix(rd, subnetIp);
724 } catch (Exception e) {
725 logger.error("Subnet route not advertised for rd " + rd + " failed ", e);
731 public void onRouterAssociatedToVpn(RouterAssociatedToVpn notification) {
735 public void onRouterDisassociatedFromVpn(RouterDisassociatedFromVpn notification) {