2 * Copyright (c) 2016 Ericsson India Global Services Pvt Ltd. and others. All rights reserved.
4 * This program and the accompanying materials are made available under the
5 * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6 * and is available at http://www.eclipse.org/legal/epl-v10.html
8 package org.opendaylight.netvirt.vpnmanager;
10 import java.util.ArrayList;
11 import java.util.List;
13 import java.util.HashMap;
15 import com.google.common.base.Preconditions;
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;
42 import java.math.BigInteger;
44 import org.slf4j.Logger;
45 import org.slf4j.LoggerFactory;
47 import com.google.common.base.Optional;
50 public class VpnSubnetRouteHandler implements NeutronvpnListener {
51 private static final Logger logger = LoggerFactory.getLogger(VpnSubnetRouteHandler.class);
53 private final DataBroker broker;
54 private SubnetOpDpnManager subOpDpnManager;
55 private final IBgpManager bgpManager;
56 private IdManagerService idManager;
57 private VpnInterfaceManager vpnInterfaceManager;
59 public VpnSubnetRouteHandler(final DataBroker db, IBgpManager bgpManager, VpnInterfaceManager vpnIntfManager) {
61 subOpDpnManager = new SubnetOpDpnManager(broker);
62 this.bgpManager = bgpManager;
63 this.vpnInterfaceManager = vpnIntfManager;
66 public void setIdManager(IdManagerService idManager) {
67 this.idManager = idManager;
71 public void onSubnetAddedToVpn(SubnetAddedToVpn notification) {
72 if (!notification.isExternalVpn()) {
76 Uuid subnetId = notification.getSubnetId();
77 String vpnName = notification.getVpnName();
78 String subnetIp = notification.getSubnetIp();
79 Long elanTag = notification.getElanTag();
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!");
86 logger.info("onSubnetAddedToVpn: Subnet" + subnetId.getValue() + " being added to vpn");
87 //TODO(vivek): Change this to use more granularized lock at subnetId level
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,
96 if (optionalSubs.isPresent()) {
97 logger.error("onSubnetAddedToVpn: SubnetOpDataEntry for subnet " + subnetId.getValue() +
98 " already detected to be present");
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;
108 SubnetOpDataEntryBuilder subOpBuilder = new SubnetOpDataEntryBuilder().setKey(new SubnetOpDataEntryKey(subnetId));
109 subOpBuilder.setSubnetId(subnetId);
110 subOpBuilder.setSubnetCidr(subnetIp);
111 String rd = VpnUtil.getVpnRdFromVpnInstanceConfig(broker, vpnName);
113 logger.error("onSubnetAddedToVpn: The VPN Instance name " + notification.getVpnName() + " does not have RD ");
116 subOpBuilder.setVrfId(rd);
117 subOpBuilder.setVpnName(vpnName);
118 subOpBuilder.setSubnetToDpn(new ArrayList<>());
119 subOpBuilder.setRouteAdvState(TaskState.Na);
120 subOpBuilder.setElanTag(elanTag);
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);
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);
138 logger.info("onSubnetAddedToVpn: Port " + port.getValue() + " is not assigned DPN yet, ignoring ");
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 ");
146 subDpn = subOpDpnManager.addInterfaceToDpn(subnetId, dpnId, port.getValue());
147 if (intfState.getOperStatus() == OperStatus.Up) {
149 subDpnMap.put(dpnId, subDpn);
150 if (nhDpnId == null) {
155 subOpDpnManager.addPortOpDataEntry(port.getValue(), subnetId, null);
158 if (subDpnMap.size() > 0) {
159 subOpBuilder.setSubnetToDpn(new ArrayList<>(subDpnMap.values()));
163 if (nhDpnId != null) {
164 subOpBuilder.setNhDpnId(nhDpnId);
167 Write the subnet route entry to the FIB.
168 And also advertise the subnet route entry via BGP.
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);
181 Write the subnet route entry to the FIB.
182 NOTE: Will not advertise to BGP as NextHopDPN is not available yet.
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);
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);
204 public void onSubnetDeletedFromVpn(SubnetDeletedFromVpn notification) {
205 Uuid subnetId = notification.getSubnetId();
207 if (!notification.isExternalVpn()) {
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) {
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,
220 if (!optionalSubs.isPresent()) {
221 logger.error("onSubnetDeletedFromVpn: SubnetOpDataEntry for subnet " + subnetId.getValue() +
222 " not available in datastore");
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());
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);
240 Subnetmap subMap = sm.get();
241 List<Uuid> portList = subMap.getPortList();
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");
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())));
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");
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);
277 } catch (Exception ex) {
278 logger.error("Removal of SubnetOpDataEntry for subnet " +
279 subnetId.getValue() + " failed {}" + ex);
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();
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!");
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,
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());
308 // TODO(vivek): Something got updated, but we donot know what ?
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());
315 // TODO(vivek): Something got updated, but we donot know what ?
320 public void onPortAddedToSubnet(PortAddedToSubnet notification) {
321 Uuid subnetId = notification.getSubnetId();
322 Uuid portId = notification.getPortId();
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) {
328 InstanceIdentifier<SubnetOpDataEntry> subOpIdentifier = InstanceIdentifier.builder(SubnetOpData.class).
329 child(SubnetOpDataEntry.class, new SubnetOpDataEntryKey(subnetId)).build();
331 Optional<SubnetOpDataEntry> optionalSubs = VpnUtil.read(broker, LogicalDatastoreType.OPERATIONAL,
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");
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);
344 BigInteger dpnId = InterfaceUtils.getDpIdFromInterface(intfState);
346 logger.info("onPortAddedToSubnet: Port " + portId.getValue() + " is not assigned DPN yet, ignoring ");
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 ");
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) {
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);
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)) {
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);
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());
388 } catch (Exception ex) {
389 logger.error("Creation of SubnetOpDataEntry for subnet " +
390 subnetId.getValue() + " failed {}", ex);
397 public void onPortRemovedFromSubnet(PortRemovedFromSubnet notification) {
398 Uuid subnetId = notification.getSubnetId();
399 Uuid portId = notification.getPortId();
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) {
405 PortOpDataEntry portOpEntry = subOpDpnManager.removePortOpDataEntry(portId.getValue());
406 if (portOpEntry == null) {
409 BigInteger dpnId = portOpEntry.getDpnId();
411 logger.debug("onPortRemovedFromSubnet: Port {} does not have a DPNId associated, ignoring", portId.getValue());
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,
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");
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
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);
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);
452 nhDpnId = subDpnList.get(0).getDpnId();
453 subOpBuilder.setNhDpnId(nhDpnId);
454 logger.debug("onInterfaceDown: Swapping the Designated DPN to " + nhDpnId + " for subnet " + subnetId.getValue());
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);
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);
482 public void onInterfaceUp(Interface intfState) {
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");
494 BigInteger dpnId = portOpEntry.getDpnId();
496 dpnId = InterfaceUtils.getDpIdFromInterface(intfState);
498 logger.error("onInterfaceUp: Unable to determine the DPNID for port " + intfName);
502 Uuid subnetId = portOpEntry.getSubnetId();
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,
508 if (!optionalSubs.isPresent()) {
509 logger.error("onInterfaceUp: SubnetOpDataEntry for subnet " + subnetId.getValue() +
510 " is not available");
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) {
521 List<SubnetToDpn> subDpnList = subOpBuilder.getSubnetToDpn();
522 subDpnList.add(subDpn);
523 subOpBuilder.setSubnetToDpn(subDpnList);
524 if (subOpBuilder.getNhDpnId() == null) {
525 subOpBuilder.setNhDpnId(dpnId);
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)) {
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);
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);
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");
565 BigInteger dpnId = portOpEntry.getDpnId();
567 dpnId = InterfaceUtils.getDpIdFromInterface(intfState);
569 logger.error("onInterfaceDown: Unable to determine the DPNID for port " + intfName);
573 Uuid subnetId = portOpEntry.getSubnetId();
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,
582 if (!optionalSubs.isPresent()) {
583 logger.error("onInterfaceDown: SubnetOpDataEntry for subnet " + subnetId.getValue() +
584 " is not available");
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
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);
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);
614 nhDpnId = subDpnList.get(0).getDpnId();
615 subOpBuilder.setNhDpnId(nhDpnId);
616 logger.debug("onInterfaceDown: Swapping the Designated DPN to " + nhDpnId + " for subnet " + subnetId.getValue());
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);
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);
642 private static void setRdToElanOpEntry(DataBroker broker,
643 String rd, String subnetIp, String nextHopIp, String vpnName,
645 RdToElanOpEntryBuilder rdElanBuilder = null;
646 RdToElanOpEntry rdElan = null;
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();
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();
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);
674 private void addSubnetRouteToFib(String rd, String subnetIp, BigInteger nhDpnId, String vpnName,
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);
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);
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);
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);
709 int label = VpnUtil.getUniqueId(idManager, VpnConstants.VPN_IDPOOL_NAME,
710 VpnUtil.getNextHopLabelKey(rd, subnetIp));
711 setRdToElanOpEntry(broker, rd, subnetIp, nexthopIp, vpnName, elanTag);
713 bgpManager.advertisePrefix(rd, subnetIp, nexthopIp, label);
714 } catch (Exception e) {
715 logger.error("Subnet route not advertised for rd " + rd + " failed ", e);
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!");
724 bgpManager.withdrawPrefix(rd, subnetIp);
725 } catch (Exception e) {
726 logger.error("Subnet route not advertised for rd " + rd + " failed ", e);
732 public void onRouterAssociatedToVpn(RouterAssociatedToVpn notification) {
736 public void onRouterDisassociatedFromVpn(RouterDisassociatedFromVpn notification) {