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.subnet.op.data.SubnetOpDataEntry;
29 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.subnet.op.data.SubnetOpDataEntryKey;
30 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.subnet.op.data.SubnetOpDataEntryBuilder;
31 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn.rev150602.*;
32 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn.rev150602.subnetmaps.Subnetmap;
33 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn.rev150602.subnetmaps.SubnetmapKey;
34 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.subnet.op.data.subnet.op.data.entry.SubnetToDpn;
35 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.subnet.op.data.subnet.op.data.entry.subnet.to.dpn.VpnInterfaces;
36 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.state.Interface;
37 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.ExternalNetworks;
38 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.external.networks.Networks;
39 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.external.networks.NetworksKey;
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 Subnetmap subMap = null;
92 // Please check if subnetId belongs to an External Network
93 InstanceIdentifier<Subnetmap> subMapid = InstanceIdentifier.builder(Subnetmaps.class).
94 child(Subnetmap.class, new SubnetmapKey(subnetId)).build();
95 Optional<Subnetmap> sm = VpnUtil.read(broker, LogicalDatastoreType.CONFIGURATION, subMapid);
96 if (!sm.isPresent()) {
97 logger.error("onSubnetAddedToVpn: Unable to retrieve subnetmap entry for subnet : " + subnetId);
101 InstanceIdentifier<Networks> netsIdentifier = InstanceIdentifier.builder(ExternalNetworks.class).
102 child(Networks.class, new NetworksKey(subMap.getNetworkId())).build();
103 Optional<Networks> optionalNets = VpnUtil.read(broker, LogicalDatastoreType.CONFIGURATION, netsIdentifier);
104 if (optionalNets.isPresent()) {
105 logger.info("onSubnetAddedToVpn: subnet {} is an external subnet on external network {}, so ignoring this for SubnetRoute",
106 subnetId.getValue(), subMap.getNetworkId().getValue());
109 //Create and add SubnetOpDataEntry object for this subnet to the SubnetOpData container
110 InstanceIdentifier<SubnetOpDataEntry> subOpIdentifier = InstanceIdentifier.builder(SubnetOpData.class).
111 child(SubnetOpDataEntry.class, new SubnetOpDataEntryKey(subnetId)).build();
112 Optional<SubnetOpDataEntry> optionalSubs = VpnUtil.read(broker,
113 LogicalDatastoreType.OPERATIONAL,
115 if (optionalSubs.isPresent()) {
116 logger.error("onSubnetAddedToVpn: SubnetOpDataEntry for subnet " + subnetId.getValue() +
117 " already detected to be present");
120 logger.debug("onSubnetAddedToVpn: Creating new SubnetOpDataEntry node for subnet: " + subnetId.getValue());
121 Map<BigInteger, SubnetToDpn> subDpnMap = new HashMap<>();
122 SubnetOpDataEntry subOpEntry = null;
123 BigInteger dpnId = null;
124 BigInteger nhDpnId = null;
125 SubnetToDpn subDpn = null;
127 SubnetOpDataEntryBuilder subOpBuilder = new SubnetOpDataEntryBuilder().setKey(new SubnetOpDataEntryKey(subnetId));
128 subOpBuilder.setSubnetId(subnetId);
129 subOpBuilder.setSubnetCidr(subnetIp);
130 String rd = VpnUtil.getVpnRdFromVpnInstanceConfig(broker, vpnName);
132 logger.error("onSubnetAddedToVpn: The VPN Instance name " + notification.getVpnName() + " does not have RD ");
135 subOpBuilder.setVrfId(rd);
136 subOpBuilder.setVpnName(vpnName);
137 subOpBuilder.setSubnetToDpn(new ArrayList<>());
138 subOpBuilder.setRouteAdvState(TaskState.Na);
139 subOpBuilder.setElanTag(elanTag);
141 // First recover set of ports available in this subnet
142 List<Uuid> portList = subMap.getPortList();
143 if (portList != null) {
144 for (Uuid port: portList) {
145 Interface intfState = InterfaceUtils.getInterfaceStateFromOperDS(broker,port.getValue());
146 if (intfState != null) {
147 dpnId = InterfaceUtils.getDpIdFromInterface(intfState);
149 logger.info("onSubnetAddedToVpn: Port " + port.getValue() + " is not assigned DPN yet, ignoring ");
152 subOpDpnManager.addPortOpDataEntry(port.getValue(), subnetId, dpnId);
153 if (intfState.getOperStatus() != OperStatus.Up) {
154 logger.info("onSubnetAddedToVpn: Port " + port.getValue() + " is not UP yet, ignoring ");
157 subDpn = subOpDpnManager.addInterfaceToDpn(subnetId, dpnId, port.getValue());
158 if (intfState.getOperStatus() == OperStatus.Up) {
160 subDpnMap.put(dpnId, subDpn);
161 if (nhDpnId == null) {
166 subOpDpnManager.addPortOpDataEntry(port.getValue(), subnetId, null);
169 if (subDpnMap.size() > 0) {
170 subOpBuilder.setSubnetToDpn(new ArrayList<>(subDpnMap.values()));
174 if (nhDpnId != null) {
175 subOpBuilder.setNhDpnId(nhDpnId);
178 Write the subnet route entry to the FIB.
179 And also advertise the subnet route entry via BGP.
181 int label = getLabel(rd, subnetIp);
182 addSubnetRouteToFib(rd, subnetIp, nhDpnId, vpnName, elanTag, label);
183 advertiseSubnetRouteToBgp(rd, subnetIp, nhDpnId, vpnName, elanTag, label);
184 subOpBuilder.setRouteAdvState(TaskState.Done);
185 } catch (Exception ex) {
186 logger.error("onSubnetAddedToVpn: FIB rules and Advertising nhDpnId " + nhDpnId +
187 " information for subnet " + subnetId.getValue() + " to BGP failed {}", ex);
188 subOpBuilder.setRouteAdvState(TaskState.Pending);
193 Write the subnet route entry to the FIB.
194 NOTE: Will not advertise to BGP as NextHopDPN is not available yet.
196 int label = getLabel(rd, subnetIp);
197 addSubnetRouteToFib(rd, subnetIp, null, vpnName, elanTag, label);
198 } catch (Exception ex) {
199 logger.error("onSubnetAddedToVpn: FIB rules writing for subnet {} with exception {} " +
200 subnetId.getValue(), ex);
201 subOpBuilder.setRouteAdvState(TaskState.Pending);
205 subOpEntry = subOpBuilder.build();
206 MDSALUtil.syncWrite(broker, LogicalDatastoreType.OPERATIONAL, subOpIdentifier, subOpEntry);
207 logger.info("onSubnetAddedToVpn: Added subnetopdataentry to OP Datastore for subnet " + subnetId.getValue());
208 } catch (Exception ex) {
209 logger.error("Creation of SubnetOpDataEntry for subnet " +
210 subnetId.getValue() + " failed {}", ex);
217 public void onSubnetDeletedFromVpn(SubnetDeletedFromVpn notification) {
218 Uuid subnetId = notification.getSubnetId();
220 if (!notification.isExternalVpn()) {
223 logger.info("onSubnetDeletedFromVpn: Subnet " + subnetId.getValue() + " being removed to vpn");
224 //TODO(vivek): Change this to use more granularized lock at subnetId level
225 synchronized (this) {
227 InstanceIdentifier<SubnetOpDataEntry> subOpIdentifier = InstanceIdentifier.builder(SubnetOpData.class).
228 child(SubnetOpDataEntry.class, new SubnetOpDataEntryKey(subnetId)).build();
229 logger.trace(" Removing the SubnetOpDataEntry node for subnet: " + subnetId.getValue());
230 Optional<SubnetOpDataEntry> optionalSubs = VpnUtil.read(broker,
231 LogicalDatastoreType.OPERATIONAL,
233 if (!optionalSubs.isPresent()) {
234 logger.error("onSubnetDeletedFromVpn: SubnetOpDataEntry for subnet " + subnetId.getValue() +
235 " not available in datastore");
238 SubnetOpDataEntryBuilder subOpBuilder = new SubnetOpDataEntryBuilder(optionalSubs.get());
239 List<SubnetToDpn> subDpnList = subOpBuilder.getSubnetToDpn();
240 for (SubnetToDpn subDpn: subDpnList) {
241 List<VpnInterfaces> vpnIntfList = subDpn.getVpnInterfaces();
242 for (VpnInterfaces vpnIntf: vpnIntfList) {
243 subOpDpnManager.removePortOpDataEntry(vpnIntf.getInterfaceName());
246 //Removing Stale Ports in portOpData
247 InstanceIdentifier<Subnetmap> subMapid = InstanceIdentifier.builder(Subnetmaps.class).
248 child(Subnetmap.class, new SubnetmapKey(subnetId)).build();
249 Optional<Subnetmap> sm = VpnUtil.read(broker, LogicalDatastoreType.CONFIGURATION, subMapid);
250 if (!sm.isPresent()) {
251 logger.error("Stale ports removal: Unable to retrieve subnetmap entry for subnet : " + subnetId);
253 Subnetmap subMap = sm.get();
254 List<Uuid> portList = subMap.getPortList();
256 InstanceIdentifier<PortOpData> portOpIdentifier = InstanceIdentifier.builder(PortOpData.class).build();
257 Optional<PortOpData> optionalPortOp = VpnUtil.read(broker, LogicalDatastoreType.OPERATIONAL, portOpIdentifier);
258 if(!optionalPortOp.isPresent()){
259 logger.error("Stale ports removal: Cannot delete port. Not available in data store");
262 PortOpData portOpData = optionalPortOp.get();
263 List<PortOpDataEntry> portOpDataList = portOpData.getPortOpDataEntry();
264 if(portOpDataList!=null){
265 for(PortOpDataEntry portOpDataListEntry : portOpDataList){
266 if(portList.contains(new Uuid(portOpDataListEntry.getPortId()))){
267 logger.trace("Removing stale port: " + portOpDataListEntry + "for dissociated subnetId: " + subnetId);
268 MDSALUtil.syncDelete(broker, LogicalDatastoreType.OPERATIONAL, portOpIdentifier.
269 child(PortOpDataEntry.class, new PortOpDataEntryKey(portOpDataListEntry.getKey())));
276 String rd = subOpBuilder.getVrfId();
277 String subnetIp = subOpBuilder.getSubnetCidr();
278 BigInteger nhDpnId = subOpBuilder.getNhDpnId();
279 MDSALUtil.syncDelete(broker, LogicalDatastoreType.OPERATIONAL, subOpIdentifier);
280 logger.info("onSubnetDeletedFromVpn: Removed subnetopdataentry for subnet {} successfully from Datastore", subnetId.getValue());
282 //Withdraw the routes for all the interfaces on this subnet
283 //Remove subnet route entry from FIB
284 deleteSubnetRouteFromFib(rd, subnetIp);
285 withdrawSubnetRoutefromBgp(rd, subnetIp);
286 } catch (Exception ex) {
287 logger.error("onSubnetAddedToVpn: Withdrawing routes from BGP for subnet " +
288 subnetId.getValue() + " failed {}" + ex);
290 } catch (Exception ex) {
291 logger.error("Removal of SubnetOpDataEntry for subnet " +
292 subnetId.getValue() + " failed {}" + ex);
299 public void onSubnetUpdatedInVpn(SubnetUpdatedInVpn notification) {
300 Uuid subnetId = notification.getSubnetId();
301 String vpnName = notification.getVpnName();
302 String subnetIp = notification.getSubnetIp();
303 Long elanTag = notification.getElanTag();
305 Preconditions.checkNotNull(subnetId, "SubnetId cannot be null or empty!");
306 Preconditions.checkNotNull(subnetIp, "SubnetPrefix cannot be null or empty!");
307 Preconditions.checkNotNull(vpnName, "VpnName cannot be null or empty!");
308 Preconditions.checkNotNull(elanTag, "ElanTag cannot be null or empty!");
310 InstanceIdentifier<SubnetOpDataEntry> subOpIdentifier = InstanceIdentifier.builder(SubnetOpData.class).
311 child(SubnetOpDataEntry.class, new SubnetOpDataEntryKey(subnetId)).build();
312 Optional<SubnetOpDataEntry> optionalSubs = VpnUtil.read(broker,
313 LogicalDatastoreType.OPERATIONAL,
315 if (optionalSubs.isPresent()) {
316 if (!notification.isExternalVpn()) {
317 SubnetDeletedFromVpnBuilder bldr = new SubnetDeletedFromVpnBuilder().setVpnName(vpnName);
318 bldr.setElanTag(elanTag).setExternalVpn(true).setSubnetIp(subnetIp).setSubnetId(subnetId);
319 onSubnetDeletedFromVpn(bldr.build());
321 // TODO(vivek): Something got updated, but we donot know what ?
323 if (notification.isExternalVpn()) {
324 SubnetAddedToVpnBuilder bldr = new SubnetAddedToVpnBuilder().setVpnName(vpnName).setElanTag(elanTag);
325 bldr.setSubnetIp(subnetIp).setSubnetId(subnetId).setExternalVpn(true);
326 onSubnetAddedToVpn(bldr.build());
328 // TODO(vivek): Something got updated, but we donot know what ?
333 public void onPortAddedToSubnet(PortAddedToSubnet notification) {
334 Uuid subnetId = notification.getSubnetId();
335 Uuid portId = notification.getPortId();
337 logger.info("onPortAddedToSubnet: Port " + portId.getValue() + " being added to subnet " + subnetId.getValue());
338 //TODO(vivek): Change this to use more granularized lock at subnetId level
339 synchronized (this) {
341 InstanceIdentifier<SubnetOpDataEntry> subOpIdentifier = InstanceIdentifier.builder(SubnetOpData.class).
342 child(SubnetOpDataEntry.class, new SubnetOpDataEntryKey(subnetId)).build();
344 Optional<SubnetOpDataEntry> optionalSubs = VpnUtil.read(broker, LogicalDatastoreType.OPERATIONAL,
346 if (!optionalSubs.isPresent()) {
347 logger.info("onPortAddedToSubnet: Port " + portId.getValue() + " is part of a subnet " + subnetId.getValue() +
348 " that is not in VPN, ignoring");
351 Interface intfState = InterfaceUtils.getInterfaceStateFromOperDS(broker,portId.getValue());
352 if (intfState == null) {
353 // Interface State not yet available
354 subOpDpnManager.addPortOpDataEntry(portId.getValue(), subnetId, null);
357 BigInteger dpnId = InterfaceUtils.getDpIdFromInterface(intfState);
359 logger.info("onPortAddedToSubnet: Port " + portId.getValue() + " is not assigned DPN yet, ignoring ");
362 subOpDpnManager.addPortOpDataEntry(portId.getValue(), subnetId, dpnId);
363 if (intfState.getOperStatus() != OperStatus.Up) {
364 logger.info("onPortAddedToSubnet: Port " + portId.getValue() + " is not UP yet, ignoring ");
367 logger.debug("onPortAddedToSubnet: Updating the SubnetOpDataEntry node for subnet: " + subnetId.getValue());
368 SubnetToDpn subDpn = subOpDpnManager.addInterfaceToDpn(subnetId, dpnId, portId.getValue());
369 if (subDpn == null) {
372 SubnetOpDataEntryBuilder subOpBuilder = new SubnetOpDataEntryBuilder(optionalSubs.get());
373 List<SubnetToDpn> subDpnList = subOpBuilder.getSubnetToDpn();
374 subDpnList.add(subDpn);
375 subOpBuilder.setSubnetToDpn(subDpnList);
376 if (subOpBuilder.getNhDpnId() == null) {
377 subOpBuilder.setNhDpnId(dpnId);
379 BigInteger nhDpnId = subOpBuilder.getNhDpnId();
380 String rd = subOpBuilder.getVrfId();
381 String subnetIp = subOpBuilder.getSubnetCidr();
382 String vpnName = subOpBuilder.getVpnName();
383 Long elanTag = subOpBuilder.getElanTag();
384 if ((subOpBuilder.getRouteAdvState() == TaskState.Pending) ||
385 (subOpBuilder.getRouteAdvState() == TaskState.Na)) {
387 // Write the Subnet Route Entry to FIB
388 // Advertise BGP Route here and set route_adv_state to DONE
389 int label = getLabel(rd, subnetIp);
390 addSubnetRouteToFib(rd, subnetIp, nhDpnId, vpnName, elanTag, label);
391 advertiseSubnetRouteToBgp(rd, subnetIp, nhDpnId, vpnName, elanTag, label);
392 subOpBuilder.setRouteAdvState(TaskState.Done);
393 } catch (Exception ex) {
394 logger.error("onPortAddedToSubnet: Advertising NextHopDPN "+ nhDpnId +
395 " information for subnet " + subnetId.getValue() + " to BGP failed {}", ex);
398 SubnetOpDataEntry subOpEntry = subOpBuilder.build();
399 MDSALUtil.syncWrite(broker, LogicalDatastoreType.OPERATIONAL, subOpIdentifier, subOpEntry);
400 logger.info("onPortAddedToSubnet: Updated subnetopdataentry to OP Datastore for port " + portId.getValue());
402 } catch (Exception ex) {
403 logger.error("Creation of SubnetOpDataEntry for subnet " +
404 subnetId.getValue() + " failed {}", ex);
411 public void onPortRemovedFromSubnet(PortRemovedFromSubnet notification) {
412 Uuid subnetId = notification.getSubnetId();
413 Uuid portId = notification.getPortId();
415 logger.info("onPortRemovedFromSubnet: Port " + portId.getValue() + " being removed from subnet " + subnetId.getValue());
416 //TODO(vivek): Change this to use more granularized lock at subnetId level
417 synchronized (this) {
419 PortOpDataEntry portOpEntry = subOpDpnManager.removePortOpDataEntry(portId.getValue());
420 if (portOpEntry == null) {
423 BigInteger dpnId = portOpEntry.getDpnId();
425 logger.debug("onPortRemovedFromSubnet: Port {} does not have a DPNId associated, ignoring", portId.getValue());
428 logger.debug("onPortRemovedFromSubnet: Updating the SubnetOpDataEntry node for subnet: " + subnetId.getValue());
429 boolean last = subOpDpnManager.removeInterfaceFromDpn(subnetId, dpnId, portId.getValue());
430 InstanceIdentifier<SubnetOpDataEntry> subOpIdentifier = InstanceIdentifier.builder(SubnetOpData.class).
431 child(SubnetOpDataEntry.class, new SubnetOpDataEntryKey(subnetId)).build();
432 Optional<SubnetOpDataEntry> optionalSubs = VpnUtil.read(broker, LogicalDatastoreType.OPERATIONAL,
434 if (!optionalSubs.isPresent()) {
435 logger.info("onPortRemovedFromSubnet: Port " + portId.getValue() + " is part of a subnet " + subnetId.getValue() +
436 " that is not in VPN, ignoring");
439 SubnetOpDataEntry subOpEntry = null;
440 List<SubnetToDpn> subDpnList = null;
441 SubnetOpDataEntryBuilder subOpBuilder = new SubnetOpDataEntryBuilder(optionalSubs.get());
442 String rd = subOpBuilder.getVrfId();
443 String subnetIp = subOpBuilder.getSubnetCidr();
444 String vpnName = subOpBuilder.getVpnName();
445 Long elanTag = subOpBuilder.getElanTag();
446 BigInteger nhDpnId = subOpBuilder.getNhDpnId();
447 if ((nhDpnId != null) && (nhDpnId.equals(dpnId))) {
448 // select another NhDpnId
450 logger.debug("onPortRemovedFromSubnet: Last port " + portId + " on the subnet: " + subnetId.getValue());
451 // last port on this DPN, so we need to swap the NHDpnId
452 subDpnList = subOpBuilder.getSubnetToDpn();
453 if (subDpnList.isEmpty()) {
454 subOpBuilder.setNhDpnId(null);
456 // withdraw route from BGP
457 deleteSubnetRouteFromFib(rd, subnetIp);
458 withdrawSubnetRoutefromBgp(rd, subnetIp);
459 subOpBuilder.setRouteAdvState(TaskState.Na);
460 } catch (Exception ex) {
461 logger.error("onPortRemovedFromSubnet: Withdrawing NextHopDPN " + dpnId + " information for subnet " +
462 subnetId.getValue() + " from BGP failed ", ex);
463 subOpBuilder.setRouteAdvState(TaskState.Pending);
466 nhDpnId = subDpnList.get(0).getDpnId();
467 subOpBuilder.setNhDpnId(nhDpnId);
468 logger.debug("onInterfaceDown: Swapping the Designated DPN to " + nhDpnId + " for subnet " + subnetId.getValue());
470 // Best effort Withdrawal of route from BGP for this subnet
471 // Advertise the new NexthopIP to BGP for this subnet
472 //withdrawSubnetRoutefromBgp(rd, subnetIp);
473 int label = getLabel(rd, subnetIp);
474 addSubnetRouteToFib(rd, subnetIp, nhDpnId, vpnName, elanTag, label);
475 advertiseSubnetRouteToBgp(rd, subnetIp, nhDpnId, vpnName, elanTag, label);
476 subOpBuilder.setRouteAdvState(TaskState.Done);
477 } catch (Exception ex) {
478 logger.error("onPortRemovedFromSubnet: Swapping Withdrawing NextHopDPN " + dpnId +
479 " information for subnet " + subnetId.getValue() +
480 " to BGP failed {}" + ex);
481 subOpBuilder.setRouteAdvState(TaskState.Pending);
486 subOpEntry = subOpBuilder.build();
487 MDSALUtil.syncWrite(broker, LogicalDatastoreType.OPERATIONAL, subOpIdentifier, subOpEntry);
488 logger.info("onPortRemovedFromSubnet: Updated subnetopdataentry to OP Datastore removing port " + portId.getValue());
489 } catch (Exception ex) {
490 logger.error("Creation of SubnetOpDataEntry for subnet " +
491 subnetId.getValue() + " failed {}" + ex);
497 public void onInterfaceUp(Interface intfState) {
499 logger.info("onInterfaceUp: Port " + intfState.getName());
500 //TODO(vivek): Change this to use more granularized lock at subnetId level
501 synchronized (this) {
502 SubnetToDpn subDpn = null;
503 String intfName = intfState.getName();
504 PortOpDataEntry portOpEntry = subOpDpnManager.getPortOpDataEntry(intfName);
505 if (portOpEntry == null) {
506 logger.info("onInterfaceUp: Port " + intfState.getName() + "is part of a subnet not in VPN, ignoring");
509 BigInteger dpnId = portOpEntry.getDpnId();
511 dpnId = InterfaceUtils.getDpIdFromInterface(intfState);
513 logger.error("onInterfaceUp: Unable to determine the DPNID for port " + intfName);
517 Uuid subnetId = portOpEntry.getSubnetId();
519 InstanceIdentifier<SubnetOpDataEntry> subOpIdentifier = InstanceIdentifier.builder(SubnetOpData.class).
520 child(SubnetOpDataEntry.class, new SubnetOpDataEntryKey(subnetId)).build();
521 Optional<SubnetOpDataEntry> optionalSubs = VpnUtil.read(broker, LogicalDatastoreType.OPERATIONAL,
523 if (!optionalSubs.isPresent()) {
524 logger.error("onInterfaceUp: SubnetOpDataEntry for subnet " + subnetId.getValue() +
525 " is not available");
529 SubnetOpDataEntryBuilder subOpBuilder = new SubnetOpDataEntryBuilder(optionalSubs.get());
530 logger.debug("onInterfaceUp: Updating the SubnetOpDataEntry node for subnet: " + subnetId.getValue());
531 subOpDpnManager.addPortOpDataEntry(intfName, subnetId, dpnId);
532 subDpn = subOpDpnManager.addInterfaceToDpn(subnetId, dpnId, intfName);
533 if (subDpn == null) {
536 List<SubnetToDpn> subDpnList = subOpBuilder.getSubnetToDpn();
537 subDpnList.add(subDpn);
538 subOpBuilder.setSubnetToDpn(subDpnList);
539 if (subOpBuilder.getNhDpnId() == null) {
540 subOpBuilder.setNhDpnId(dpnId);
542 BigInteger nhDpnId = subOpBuilder.getNhDpnId();
543 String rd = subOpBuilder.getVrfId();
544 String subnetIp = subOpBuilder.getSubnetCidr();
545 String vpnName = subOpBuilder.getVpnName();
546 Long elanTag = subOpBuilder.getElanTag();
547 if ((subOpBuilder.getRouteAdvState() == TaskState.Pending) || (subOpBuilder.getRouteAdvState() == TaskState.Na)) {
549 // Write the Subnet Route Entry to FIB
550 // Advertise BGP Route here and set route_adv_state to DONE
551 int label = getLabel(rd, subnetIp);
552 addSubnetRouteToFib(rd, subnetIp, nhDpnId, vpnName, elanTag, label);
553 advertiseSubnetRouteToBgp(rd, subnetIp, nhDpnId, vpnName, elanTag, label);
554 subOpBuilder.setRouteAdvState(TaskState.Done);
555 } catch (Exception ex) {
556 logger.error("onInterfaceUp: Advertising NextHopDPN " + nhDpnId + " information for subnet " +
557 subnetId.getValue() + " to BGP failed {}" + ex);
560 SubnetOpDataEntry subOpEntry = subOpBuilder.build();
561 MDSALUtil.syncWrite(broker, LogicalDatastoreType.OPERATIONAL, subOpIdentifier, subOpEntry);
562 logger.info("onInterfaceUp: Updated subnetopdataentry to OP Datastore port up " + intfName);
563 } catch (Exception ex) {
564 logger.error("Creation of SubnetOpDataEntry for subnet " +
565 subnetId.getValue() + " failed {}" + ex);
571 public void onInterfaceDown(Interface intfState) {
572 logger.info("onInterfaceDown: Port " + intfState.getName());
573 //TODO(vivek): Change this to use more granularized lock at subnetId level
574 synchronized (this) {
575 String intfName = intfState.getName();
576 PortOpDataEntry portOpEntry = subOpDpnManager.getPortOpDataEntry(intfName);
577 if (portOpEntry == null) {
578 logger.info("onInterfaceDown: Port " + intfState.getName() + "is part of a subnet not in VPN, ignoring");
581 BigInteger dpnId = portOpEntry.getDpnId();
583 dpnId = InterfaceUtils.getDpIdFromInterface(intfState);
585 logger.error("onInterfaceDown: Unable to determine the DPNID for port " + intfName);
589 Uuid subnetId = portOpEntry.getSubnetId();
591 logger.debug("onInterfaceDown: Updating the SubnetOpDataEntry node for subnet: " + subnetId.getValue());
592 boolean last = subOpDpnManager.removeInterfaceFromDpn(subnetId, dpnId, intfName);
593 InstanceIdentifier<SubnetOpDataEntry> subOpIdentifier = InstanceIdentifier.builder(SubnetOpData.class).
594 child(SubnetOpDataEntry.class, new SubnetOpDataEntryKey(subnetId)).build();
595 Optional<SubnetOpDataEntry> optionalSubs = VpnUtil.read(broker,
596 LogicalDatastoreType.OPERATIONAL,
598 if (!optionalSubs.isPresent()) {
599 logger.error("onInterfaceDown: SubnetOpDataEntry for subnet " + subnetId.getValue() +
600 " is not available");
603 SubnetOpDataEntry subOpEntry = null;
604 List<SubnetToDpn> subDpnList = null;
605 SubnetOpDataEntryBuilder subOpBuilder = new SubnetOpDataEntryBuilder(optionalSubs.get());
606 String rd = subOpBuilder.getVrfId();
607 String subnetIp = subOpBuilder.getSubnetCidr();
608 String vpnName = subOpBuilder.getVpnName();
609 Long elanTag = subOpBuilder.getElanTag();
610 BigInteger nhDpnId = subOpBuilder.getNhDpnId();
611 if ((nhDpnId != null) && (nhDpnId.equals(dpnId))) {
612 // select another NhDpnId
614 logger.debug("onInterfaceDown: Last active port " + intfState.getName() + " on the subnet: " + subnetId.getValue());
615 // last port on this DPN, so we need to swap the NHDpnId
616 subDpnList = subOpBuilder.getSubnetToDpn();
617 if (subDpnList.isEmpty()) {
618 subOpBuilder.setNhDpnId(null);
620 // Withdraw route from BGP for this subnet
621 deleteSubnetRouteFromFib(rd, subnetIp);
622 withdrawSubnetRoutefromBgp(rd, subnetIp);
623 subOpBuilder.setRouteAdvState(TaskState.Na);
624 } catch (Exception ex) {
625 logger.error("onInterfaceDown: Withdrawing NextHopDPN " + dpnId + " information for subnet " +
626 subnetId.getValue() + " from BGP failed {}" + ex);
627 subOpBuilder.setRouteAdvState(TaskState.Pending);
630 nhDpnId = subDpnList.get(0).getDpnId();
631 subOpBuilder.setNhDpnId(nhDpnId);
632 logger.debug("onInterfaceDown: Swapping the Designated DPN to " + nhDpnId + " for subnet " + subnetId.getValue());
634 // Best effort Withdrawal of route from BGP for this subnet
635 //withdrawSubnetRoutefromBgp(rd, subnetIp);
636 int label = getLabel(rd, subnetIp);
637 addSubnetRouteToFib(rd, subnetIp, nhDpnId, vpnName, elanTag, label);
638 advertiseSubnetRouteToBgp(rd, subnetIp, nhDpnId, vpnName, elanTag, label);
639 subOpBuilder.setRouteAdvState(TaskState.Done);
640 } catch (Exception ex) {
641 logger.error("onInterfaceDown: Swapping Withdrawing NextHopDPN " + dpnId + " information for subnet " +
642 subnetId.getValue() + " to BGP failed {}" + ex);
643 subOpBuilder.setRouteAdvState(TaskState.Pending);
648 subOpEntry = subOpBuilder.build();
649 MDSALUtil.syncWrite(broker, LogicalDatastoreType.OPERATIONAL, subOpIdentifier, subOpEntry);
650 logger.info("onInterfaceDown: Updated subnetopdataentry to OP Datastore port down " + intfName);
651 } catch (Exception ex) {
652 logger.error("Creation of SubnetOpDataEntry for subnet " +
653 subnetId.getValue() + " failed {}" + ex);
659 private void addSubnetRouteToFib(String rd, String subnetIp, BigInteger nhDpnId, String vpnName,
660 Long elanTag, int label) {
661 Preconditions.checkNotNull(rd, "RouteDistinguisher cannot be null or empty!");
662 Preconditions.checkNotNull(subnetIp, "SubnetRouteIp cannot be null or empty!");
663 Preconditions.checkNotNull(vpnName, "vpnName cannot be null or empty!");
664 Preconditions.checkNotNull(elanTag, "elanTag cannot be null or empty!");
665 String nexthopIp = null;
666 if (nhDpnId != null) {
667 nexthopIp = InterfaceUtils.getEndpointIpAddressForDPN(broker, nhDpnId);
669 vpnInterfaceManager.addSubnetRouteFibEntryToDS(rd, subnetIp, nexthopIp, label, elanTag);
672 private int getLabel(String rd, String subnetIp) {
673 int label = VpnUtil.getUniqueId(idManager, VpnConstants.VPN_IDPOOL_NAME,
674 VpnUtil.getNextHopLabelKey(rd, subnetIp));
675 logger.trace("Allocated subnetroute label {} for rd {} prefix {}", label, rd, subnetIp);
679 private void deleteSubnetRouteFromFib(String rd, String subnetIp) {
680 Preconditions.checkNotNull(rd, "RouteDistinguisher cannot be null or empty!");
681 Preconditions.checkNotNull(subnetIp, "SubnetRouteIp cannot be null or empty!");
682 vpnInterfaceManager.removeFibEntryFromDS(rd, subnetIp);
685 private void advertiseSubnetRouteToBgp(String rd, String subnetIp, BigInteger nhDpnId, String vpnName,
686 Long elanTag, int label) throws Exception {
687 Preconditions.checkNotNull(rd, "RouteDistinguisher cannot be null or empty!");
688 Preconditions.checkNotNull(subnetIp, "SubnetRouteIp cannot be null or empty!");
689 Preconditions.checkNotNull(elanTag, "elanTag cannot be null or empty!");
690 Preconditions.checkNotNull(nhDpnId, "nhDpnId cannot be null or empty!");
691 Preconditions.checkNotNull(vpnName, "vpnName cannot be null or empty!");
692 String nexthopIp = null;
693 nexthopIp = InterfaceUtils.getEndpointIpAddressForDPN(broker, nhDpnId);
694 if (nexthopIp == null) {
695 logger.error("createSubnetRouteInVpn: Unable to obtain endpointIp address for DPNId " + nhDpnId);
696 throw new Exception("Unable to obtain endpointIp address for DPNId " + nhDpnId);
699 // BGPManager (inside ODL) requires a withdraw followed by advertise
700 // due to bugs with ClusterDataChangeListener used by BGPManager.
701 bgpManager.withdrawPrefix(rd, subnetIp);
702 bgpManager.advertisePrefix(rd, subnetIp, nexthopIp, label);
703 } catch (Exception e) {
704 logger.error("Subnet route not advertised for rd " + rd + " failed ", e);
709 private void withdrawSubnetRoutefromBgp(String rd, String subnetIp) throws Exception {
710 Preconditions.checkNotNull(rd, "RouteDistinguisher cannot be null or empty!");
711 Preconditions.checkNotNull(subnetIp, "SubnetIp cannot be null or empty!");
713 bgpManager.withdrawPrefix(rd, subnetIp);
714 } catch (Exception e) {
715 logger.error("Subnet route not advertised for rd " + rd + " failed ", e);
721 public void onRouterAssociatedToVpn(RouterAssociatedToVpn notification) {
725 public void onRouterDisassociatedFromVpn(RouterDisassociatedFromVpn notification) {