[Goldeneye] [ONOS-4161] BGP L3 Topology

Change-Id: I81ae4e88f6ab75202b98ed03cbe1597d0f6ddd1e
diff --git a/providers/bgp/topology/src/main/java/org/onosproject/provider/bgp/topology/impl/BgpTopologyProvider.java b/providers/bgp/topology/src/main/java/org/onosproject/provider/bgp/topology/impl/BgpTopologyProvider.java
index 064745d..7807d5f 100755
--- a/providers/bgp/topology/src/main/java/org/onosproject/provider/bgp/topology/impl/BgpTopologyProvider.java
+++ b/providers/bgp/topology/src/main/java/org/onosproject/provider/bgp/topology/impl/BgpTopologyProvider.java
@@ -15,6 +15,11 @@
 
 import static org.onosproject.bgp.controller.BgpDpid.uri;
 import static org.onosproject.net.DeviceId.deviceId;
+import static org.onosproject.net.Device.Type.ROUTER;
+import static org.onosproject.net.Device.Type.VIRTUAL;
+
+import java.util.List;
+import java.util.Set;
 
 import org.onlab.packet.ChassisId;
 import org.apache.felix.scr.annotations.Activate;
@@ -24,10 +29,31 @@
 import org.apache.felix.scr.annotations.ReferenceCardinality;
 import org.onosproject.bgp.controller.BgpController;
 import org.onosproject.bgp.controller.BgpDpid;
+import org.onosproject.bgp.controller.BgpLinkListener;
 import org.onosproject.bgp.controller.BgpNodeListener;
+import org.onosproject.bgpio.exceptions.BgpParseException;
+import org.onosproject.bgpio.protocol.linkstate.BgpLinkLSIdentifier;
+import org.onosproject.bgpio.protocol.linkstate.BgpLinkLsNlriVer4;
+import org.onosproject.bgpio.protocol.linkstate.BgpNodeLSIdentifier;
 import org.onosproject.bgpio.protocol.linkstate.BgpNodeLSNlriVer4;
+import org.onosproject.bgpio.protocol.linkstate.NodeDescriptors;
+import org.onosproject.bgpio.protocol.linkstate.PathAttrNlriDetails;
+import org.onosproject.bgpio.types.AutonomousSystemTlv;
+import org.onosproject.bgpio.types.BgpLSIdentifierTlv;
+import org.onosproject.bgpio.types.BgpValueType;
+import org.onosproject.bgpio.types.IPv4AddressTlv;
+import org.onosproject.bgpio.types.IsIsNonPseudonode;
+import org.onosproject.bgpio.types.IsIsPseudonode;
+import org.onosproject.bgpio.types.LinkLocalRemoteIdentifiersTlv;
+import org.onosproject.bgpio.types.OspfNonPseudonode;
+import org.onosproject.bgpio.types.OspfPseudonode;
+import org.onosproject.core.CoreService;
+import org.onosproject.net.AnnotationKeys;
+import org.onosproject.net.ConnectPoint;
+import org.onosproject.net.DefaultAnnotations;
 import org.onosproject.net.Device;
 import org.onosproject.net.DeviceId;
+import org.onosproject.net.Link;
 import org.onosproject.net.MastershipRole;
 import org.onosproject.net.PortNumber;
 import org.onosproject.net.device.DefaultDeviceDescription;
@@ -35,6 +61,13 @@
 import org.onosproject.net.device.DeviceProvider;
 import org.onosproject.net.device.DeviceProviderRegistry;
 import org.onosproject.net.device.DeviceProviderService;
+import org.onosproject.net.device.DeviceService;
+import org.onosproject.net.link.DefaultLinkDescription;
+import org.onosproject.net.link.LinkDescription;
+import org.onosproject.net.link.LinkProvider;
+import org.onosproject.net.link.LinkProviderRegistry;
+import org.onosproject.net.link.LinkProviderService;
+import org.onosproject.net.link.LinkService;
 import org.onosproject.net.provider.AbstractProvider;
 import org.onosproject.net.provider.ProviderId;
 import org.slf4j.Logger;
@@ -44,7 +77,7 @@
  * Provider which uses an BGP controller to detect network infrastructure topology.
  */
 @Component(immediate = true)
-public class BgpTopologyProvider extends AbstractProvider implements DeviceProvider {
+public class BgpTopologyProvider extends AbstractProvider implements DeviceProvider, LinkProvider {
 
     public BgpTopologyProvider() {
         super(new ProviderId("bgp", "org.onosproject.provider.bgp"));
@@ -56,42 +89,100 @@
     protected DeviceProviderRegistry deviceProviderRegistry;
 
     @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected LinkProviderRegistry linkProviderRegistry;
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
     protected BgpController controller;
 
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected LinkService linkService;
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected DeviceService deviceService;
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected CoreService coreService;
+
     private DeviceProviderService deviceProviderService;
+    private LinkProviderService linkProviderService;
 
     private InternalBgpProvider listener = new InternalBgpProvider();
     private static final String UNKNOWN = "unknown";
+    public static final long IDENTIFIER_SET = 0x100000000L;
+    public static final String AS_NUMBER = "asNumber";
+    public static final String DOMAIN_IDENTIFIER = "domainIdentifier";
+    public static final String ROUTING_UNIVERSE = "routingUniverse";
+    public static final long PSEUDO_PORT = 0xffffffff;
 
     @Activate
     public void activate() {
+        log.debug("BgpTopologyProvider activate");
         deviceProviderService = deviceProviderRegistry.register(this);
+        linkProviderService = linkProviderRegistry.register(this);
         controller.addListener(listener);
+        controller.addLinkListener(listener);
     }
 
     @Deactivate
     public void deactivate() {
+        log.debug("BgpTopologyProvider deactivate");
+        deviceProviderRegistry.unregister(this);
+        deviceProviderService = null;
+        linkProviderRegistry.unregister(this);
+        linkProviderService = null;
         controller.removeListener(listener);
+        controller.removeLinkListener(listener);
     }
 
     /*
      * Implements device and link update.
      */
-    private class InternalBgpProvider implements BgpNodeListener {
+    private class InternalBgpProvider implements BgpNodeListener, BgpLinkListener {
 
         @Override
-        public void addNode(BgpNodeLSNlriVer4 nodeNlri) {
+        public void addNode(BgpNodeLSNlriVer4 nodeNlri, PathAttrNlriDetails details) {
             log.debug("Add node {}", nodeNlri.toString());
 
             if (deviceProviderService == null) {
                 return;
             }
+            Device.Type deviceType = ROUTER;
             BgpDpid nodeUri = new BgpDpid(nodeNlri);
             DeviceId deviceId = deviceId(uri(nodeUri.toString()));
             ChassisId cId = new ChassisId();
 
-            DeviceDescription description = new DefaultDeviceDescription(uri(nodeUri.toString()), Device.Type.ROUTER,
-                                                                         UNKNOWN, UNKNOWN, UNKNOWN, UNKNOWN, cId);
+            DefaultAnnotations.Builder newBuilder = DefaultAnnotations.builder();
+
+            newBuilder.set(AnnotationKeys.TYPE, "L3");
+            newBuilder.set(ROUTING_UNIVERSE, Long.toString(nodeNlri.getIdentifier()));
+
+            List<BgpValueType> tlvs = nodeNlri.getLocalNodeDescriptors().getNodedescriptors().getSubTlvs();
+            for (BgpValueType tlv : tlvs) {
+                if (tlv instanceof AutonomousSystemTlv) {
+                    newBuilder.set(AS_NUMBER, Integer.toString(((AutonomousSystemTlv) tlv).getAsNum()));
+                } else if (tlv instanceof BgpLSIdentifierTlv) {
+                    newBuilder.set(DOMAIN_IDENTIFIER,
+                            Integer.toString(((BgpLSIdentifierTlv) tlv).getBgpLsIdentifier()));
+                }
+                if (tlv.getType() == NodeDescriptors.IGP_ROUTERID_TYPE) {
+                    if (tlv instanceof IsIsPseudonode) {
+                        deviceType = VIRTUAL;
+                        newBuilder.set(AnnotationKeys.ROUTER_ID, new String(((IsIsPseudonode) tlv).getIsoNodeId()));
+                    } else if (tlv instanceof OspfPseudonode) {
+                        deviceType = VIRTUAL;
+                        newBuilder
+                                .set(AnnotationKeys.ROUTER_ID, Integer.toString(((OspfPseudonode) tlv).getrouterID()));
+                    } else if (tlv instanceof IsIsNonPseudonode) {
+                        newBuilder.set(AnnotationKeys.ROUTER_ID, new String(((IsIsNonPseudonode) tlv).getIsoNodeId()));
+                    } else if (tlv instanceof OspfNonPseudonode) {
+                        newBuilder.set(AnnotationKeys.ROUTER_ID,
+                                Integer.toString(((OspfNonPseudonode) tlv).getrouterID()));
+                    }
+                }
+            }
+
+            DeviceDescription description = new DefaultDeviceDescription(uri(nodeUri.toString()), deviceType, UNKNOWN,
+                    UNKNOWN, UNKNOWN, UNKNOWN, cId, newBuilder.build());
             deviceProviderService.deviceConnected(deviceId, description);
 
         }
@@ -104,8 +195,131 @@
                 return;
             }
 
-            BgpDpid nodeUri = new BgpDpid(nodeNlri);
-            deviceProviderService.deviceDisconnected(deviceId(uri(nodeUri.toString())));
+            BgpDpid deviceUri = new BgpDpid(nodeNlri);
+            DeviceId deviceId = deviceId(uri(deviceUri.toString()));
+            deviceProviderService.deviceDisconnected(deviceId);
+        }
+
+        @Override
+        public void addLink(BgpLinkLsNlriVer4 linkNlri, PathAttrNlriDetails details) throws BgpParseException {
+            log.debug("Addlink {}", linkNlri.toString());
+
+            if (linkProviderService == null) {
+                return;
+            }
+            LinkDescription linkDes = buildLinkDes(linkNlri, details, true);
+            linkProviderService.linkDetected(linkDes);
+        }
+
+        //Build link description.
+        private LinkDescription buildLinkDes(BgpLinkLsNlriVer4 linkNlri, PathAttrNlriDetails details, boolean isAddLink)
+                throws BgpParseException {
+            long srcAddress = 0;
+            long dstAddress = 0;
+            boolean localPseduo = false;
+            boolean remotePseduo = false;
+
+            List<BgpValueType> localTlvs = linkNlri.getLinkIdentifier().localNodeDescriptors().getSubTlvs();
+            for (BgpValueType localTlv : localTlvs) {
+                if (localTlv instanceof IsIsPseudonode || localTlv instanceof OspfPseudonode) {
+                    localPseduo = true;
+                }
+            }
+            List<BgpValueType> remoteTlvs = linkNlri.getLinkIdentifier().remoteNodeDescriptors().getSubTlvs();
+            for (BgpValueType remoteTlv : remoteTlvs) {
+                if (remoteTlv instanceof IsIsPseudonode || remoteTlv instanceof OspfPseudonode) {
+                    remotePseduo = true;
+                }
+            }
+
+            List<BgpValueType> tlvs = linkNlri.getLinkIdentifier().linkDescriptors();
+            for (BgpValueType tlv : tlvs) {
+                if (tlv instanceof LinkLocalRemoteIdentifiersTlv) {
+                    srcAddress = ((LinkLocalRemoteIdentifiersTlv) tlv).getLinkLocalIdentifier();
+                    //Set 32nd bit.
+                    srcAddress = srcAddress | IDENTIFIER_SET;
+                    dstAddress = ((LinkLocalRemoteIdentifiersTlv) tlv).getLinkRemoteIdentifier();
+                    dstAddress = dstAddress | IDENTIFIER_SET;
+                } else if (tlv instanceof IPv4AddressTlv) {
+                    if (tlv.getType() == BgpLinkLSIdentifier.IPV4_INTERFACE_ADDRESS_TYPE) {
+                        srcAddress = ((IPv4AddressTlv) tlv).address().toInt();
+                    } else {
+                        dstAddress = ((IPv4AddressTlv) tlv).address().toInt();
+                    }
+                }
+            }
+
+            DeviceId srcId = deviceId(uri(new BgpDpid(linkNlri, BgpDpid.NODE_DESCRIPTOR_LOCAL).toString()));
+            DeviceId dstId = deviceId(uri(new BgpDpid(linkNlri, BgpDpid.NODE_DESCRIPTOR_REMOTE).toString()));
+
+            if (localPseduo && srcAddress == 0) {
+                srcAddress = PSEUDO_PORT;
+            } else if (remotePseduo && dstAddress == 0) {
+                dstAddress = PSEUDO_PORT;
+            }
+
+            ConnectPoint src = new ConnectPoint(srcId, PortNumber.portNumber(srcAddress));
+            ConnectPoint dst = new ConnectPoint(dstId, PortNumber.portNumber(dstAddress));
+            BgpNodeLSNlriVer4 srcNodeNlri = new BgpNodeLSNlriVer4(linkNlri.getIdentifier(), linkNlri.getProtocolId()
+                    .getType(), new BgpNodeLSIdentifier(linkNlri.getLinkIdentifier().localNodeDescriptors()), false,
+                    linkNlri.getRouteDistinguisher());
+
+            BgpNodeLSNlriVer4 dstNodeNlri = new BgpNodeLSNlriVer4(linkNlri.getIdentifier(), linkNlri.getProtocolId()
+                    .getType(), new BgpNodeLSIdentifier(linkNlri.getLinkIdentifier().remoteNodeDescriptors()), false,
+                    linkNlri.getRouteDistinguisher());
+
+            addOrDeletePseudoNode(isAddLink, localPseduo, remotePseduo, srcNodeNlri,
+                     dstNodeNlri, srcId, dstId, details);
+            return new DefaultLinkDescription(src, dst, Link.Type.DIRECT, false);
+        }
+
+        private void addOrDeletePseudoNode(boolean isAddLink, boolean localPseduo, boolean remotePseduo,
+                BgpNodeLSNlriVer4 srcNodeNlri, BgpNodeLSNlriVer4 dstNodeNlri, DeviceId srcId, DeviceId dstId,
+                PathAttrNlriDetails details) {
+            if (isAddLink) {
+                if (localPseduo) {
+                    if (deviceService.getDevice(srcId) == null) {
+                        for (BgpNodeListener l : controller.listener()) {
+                            l.addNode(srcNodeNlri, details);
+                        }
+                    }
+                } else if (remotePseduo) {
+                    if (deviceService.getDevice(dstId) == null) {
+                        for (BgpNodeListener l : controller.listener()) {
+                            l.addNode(dstNodeNlri, details);
+                        }
+                    }
+                }
+            } else {
+                if (localPseduo) {
+                    Set<Link> links = linkService.getDeviceLinks(srcId);
+                    if (links == null || links.isEmpty()) {
+                        for (BgpNodeListener l : controller.listener()) {
+                            l.deleteNode(srcNodeNlri);
+                        }
+                    }
+                } else if (remotePseduo) {
+                    log.info("Remote pseudo delete link ");
+                    Set<Link> links = linkService.getDeviceLinks(dstId);
+                    if (links == null || links.isEmpty()) {
+                        for (BgpNodeListener l : controller.listener()) {
+                            l.deleteNode(dstNodeNlri);
+                        }
+                    }
+                }
+            }
+        }
+
+        @Override
+        public void deleteLink(BgpLinkLsNlriVer4 linkNlri) throws BgpParseException {
+            log.debug("Delete link {}", linkNlri.toString());
+
+            if (linkProviderService == null) {
+                return;
+            }
+
+            LinkDescription linkDes = buildLinkDes(linkNlri, null, false);
+            linkProviderService.linkVanished(linkDes);
         }
     }