[CORD-200] Improves Segment Routing using Network Configuration Subsystem

The missing part of gerrit #5533

Change-Id: I0830aa80f9be0e9933a99e0c12518aef67008a53
diff --git a/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/DeviceConfiguration.java b/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/DeviceConfiguration.java
index c4bb8c8..8fdf81a 100644
--- a/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/DeviceConfiguration.java
+++ b/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/DeviceConfiguration.java
@@ -19,7 +19,12 @@
 import org.onlab.packet.Ip4Address;
 import org.onlab.packet.Ip4Prefix;
 import org.onlab.packet.MacAddress;
+import org.onosproject.incubator.net.config.basics.ConfigException;
+import org.onosproject.incubator.net.config.basics.InterfaceConfig;
+import org.onosproject.incubator.net.intf.Interface;
+import org.onosproject.net.ConnectPoint;
 import org.onosproject.net.config.NetworkConfigRegistry;
+import org.onosproject.net.host.InterfaceIpAddress;
 import org.onosproject.segmentrouting.config.SegmentRoutingConfig;
 import org.onosproject.segmentrouting.config.SegmentRoutingConfig.AdjacencySid;
 import org.onosproject.segmentrouting.grouphandler.DeviceProperties;
@@ -28,8 +33,6 @@
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-import static com.google.common.base.Preconditions.checkNotNull;
-
 import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.List;
@@ -49,7 +52,6 @@
             .getLogger(DeviceConfiguration.class);
     private final List<Integer> allSegmentIds = new ArrayList<>();
     private final HashMap<DeviceId, SegmentRouterInfo> deviceConfigMap = new HashMap<>();
-    private final NetworkConfigRegistry configService;
 
     private class SegmentRouterInfo {
         int nodeSid;
@@ -65,33 +67,54 @@
     /**
      * Constructor. Reads all the configuration for all devices of type
      * Segment Router and organizes into various maps for easier access.
-     *
-     * @param cfgService handle to network configuration manager
-     * component from where the relevant configuration is retrieved.
      */
     public DeviceConfiguration(NetworkConfigRegistry cfgService) {
-        this.configService = checkNotNull(cfgService);
-
-        Set<DeviceId> subjectSet =
+        // Read config from device subject, excluding gatewayIps and subnets.
+        Set<DeviceId> deviceSubjects =
                 cfgService.getSubjects(DeviceId.class, SegmentRoutingConfig.class);
-
-        subjectSet.forEach(subject -> {
+        deviceSubjects.forEach(subject -> {
             SegmentRoutingConfig config =
                 cfgService.getConfig(subject, SegmentRoutingConfig.class);
             SegmentRouterInfo info = new SegmentRouterInfo();
-            info.nodeSid = config.getSid();
             info.deviceId = subject;
+            info.nodeSid = config.getSid();
             info.ip = config.getIp();
             info.mac = config.getMac();
             info.isEdge = config.isEdgeRouter();
-            // TODO fecth subnet and gateway information via port subject
+            info.adjacencySids = config.getAdjacencySids();
             info.gatewayIps = new HashMap<>();
             info.subnets = new HashMap<>();
-            info.adjacencySids = config.getAdjacencySids();
 
             this.deviceConfigMap.put(info.deviceId, info);
             this.allSegmentIds.add(info.nodeSid);
         });
+
+        // Read gatewayIps and subnets from port subject.
+        Set<ConnectPoint> portSubjects =
+            cfgService.getSubjects(ConnectPoint.class, InterfaceConfig.class);
+        portSubjects.forEach(subject -> {
+            InterfaceConfig config =
+                    cfgService.getConfig(subject, InterfaceConfig.class);
+            Set<Interface> networkInterfaces;
+            try {
+                networkInterfaces = config.getInterfaces();
+            } catch (ConfigException e) {
+                log.error("Error loading port configuration");
+                return;
+            }
+            networkInterfaces.forEach(networkInterface -> {
+                DeviceId dpid = networkInterface.connectPoint().deviceId();
+                PortNumber port = networkInterface.connectPoint().port();
+                SegmentRouterInfo info = this.deviceConfigMap.get(dpid);
+
+                Set<InterfaceIpAddress> interfaceAddresses = networkInterface.ipAddresses();
+                interfaceAddresses.forEach(interfaceAddress -> {
+                    info.gatewayIps.put(port, interfaceAddress.ipAddress().getIp4Address());
+                    info.subnets.put(port, interfaceAddress.subnetAddress().getIp4Prefix());
+                });
+            });
+
+        });
     }
 
     /**
@@ -370,7 +393,7 @@
     public List<Integer> getPortsForAdjacencySid(DeviceId deviceId, int sid) {
         if (deviceConfigMap.get(deviceId) != null) {
             for (AdjacencySid asid : deviceConfigMap.get(deviceId).adjacencySids) {
-                if (asid.getSid() == sid) {
+                if (asid.getAsid() == sid) {
                     return asid.getPorts();
                 }
             }
@@ -394,7 +417,7 @@
             } else {
                 for (AdjacencySid asid:
                         deviceConfigMap.get(deviceId).adjacencySids) {
-                    if (asid.getSid() == sid) {
+                    if (asid.getAsid() == sid) {
                         return true;
                     }
                 }
diff --git a/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/SegmentRoutingManager.java b/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/SegmentRoutingManager.java
index 764bed3..0566312 100644
--- a/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/SegmentRoutingManager.java
+++ b/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/SegmentRoutingManager.java
@@ -141,7 +141,9 @@
     @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
     protected NetworkConfigRegistry cfgService;
 
-    private final InternalConfigListener cfgListener = new InternalConfigListener();
+    private final InternalConfigListener cfgListener =
+            new InternalConfigListener(this);
+
     private final ConfigFactory cfgFactory =
             new ConfigFactory(SubjectFactories.DEVICE_SUBJECT_FACTORY,
                               SegmentRoutingConfig.class,
@@ -211,40 +213,8 @@
 
         cfgService.addListener(cfgListener);
         cfgService.registerConfigFactory(cfgFactory);
-        deviceConfiguration = new DeviceConfiguration(cfgService);
 
-        arpHandler = new ArpHandler(this);
-        icmpHandler = new IcmpHandler(this);
-        ipHandler = new IpHandler(this);
-        routingRulePopulator = new RoutingRulePopulator(this);
-        defaultRoutingHandler = new DefaultRoutingHandler(this);
-        tunnelHandler = new TunnelHandler(linkService, deviceConfiguration,
-                groupHandlerMap, tunnelStore);
-        policyHandler = new PolicyHandler(appId, deviceConfiguration,
-                flowObjectiveService, tunnelHandler, policyStore);
-
-        packetService.addProcessor(processor, PacketProcessor.director(2));
-        linkService.addListener(new InternalLinkListener());
-        deviceService.addListener(new InternalDeviceListener());
-
-        for (Device device : deviceService.getDevices()) {
-            //Irrespective whether the local is a MASTER or not for this device,
-            //create group handler instance and push default TTP flow rules.
-            //Because in a multi-instance setup, instances can initiate
-            //groups for any devices. Also the default TTP rules are needed
-            //to be pushed before inserting any IP table entries for any device
-            DefaultGroupHandler groupHandler = DefaultGroupHandler
-                    .createGroupHandler(device.id(), appId,
-                                        deviceConfiguration, linkService,
-                                        flowObjectiveService,
-                                        nsNextObjStore);
-            groupHandlerMap.put(device.id(), groupHandler);
-            defaultRoutingHandler.populateTtpRules(device.id());
-        }
-
-        defaultRoutingHandler.startPopulationProcess();
         log.info("Started");
-
     }
 
     @Deactivate
@@ -535,12 +505,56 @@
     }
 
     private class InternalConfigListener implements NetworkConfigListener {
+        SegmentRoutingManager segmentRoutingManager;
+
+        public InternalConfigListener(SegmentRoutingManager srMgr) {
+            this.segmentRoutingManager = srMgr;
+        }
+
+        public void configureNetwork() {
+            deviceConfiguration = new DeviceConfiguration(segmentRoutingManager.cfgService);
+
+            arpHandler = new ArpHandler(segmentRoutingManager);
+            icmpHandler = new IcmpHandler(segmentRoutingManager);
+            ipHandler = new IpHandler(segmentRoutingManager);
+            routingRulePopulator = new RoutingRulePopulator(segmentRoutingManager);
+            defaultRoutingHandler = new DefaultRoutingHandler(segmentRoutingManager);
+
+            tunnelHandler = new TunnelHandler(linkService, deviceConfiguration,
+                                              groupHandlerMap, tunnelStore);
+            policyHandler = new PolicyHandler(appId, deviceConfiguration,
+                                              flowObjectiveService,
+                                              tunnelHandler, policyStore);
+
+            packetService.addProcessor(processor, PacketProcessor.director(2));
+            linkService.addListener(new InternalLinkListener());
+            deviceService.addListener(new InternalDeviceListener());
+
+            for (Device device : deviceService.getDevices()) {
+                //Irrespective whether the local is a MASTER or not for this device,
+                //create group handler instance and push default TTP flow rules.
+                //Because in a multi-instance setup, instances can initiate
+                //groups for any devices. Also the default TTP rules are needed
+                //to be pushed before inserting any IP table entries for any device
+                DefaultGroupHandler groupHandler = DefaultGroupHandler
+                        .createGroupHandler(device.id(), appId,
+                                            deviceConfiguration, linkService,
+                                            flowObjectiveService,
+                                            nsNextObjStore);
+                groupHandlerMap.put(device.id(), groupHandler);
+                defaultRoutingHandler.populateTtpRules(device.id());
+            }
+
+            defaultRoutingHandler.startPopulationProcess();
+        }
+
         @Override
         public void event(NetworkConfigEvent event) {
             if ((event.type() == NetworkConfigEvent.Type.CONFIG_ADDED ||
                     event.type() == NetworkConfigEvent.Type.CONFIG_UPDATED) &&
                     event.configClass().equals(SegmentRoutingConfig.class)) {
-                // TODO Support dynamic configuration in the future
+                log.info("Network configuration change detected. (Re)Configuring...");
+                configureNetwork();
                 return;
             }
         }
diff --git a/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/config/SegmentRoutingConfig.java b/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/config/SegmentRoutingConfig.java
index 618ebbc..6dc3f0d 100644
--- a/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/config/SegmentRoutingConfig.java
+++ b/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/config/SegmentRoutingConfig.java
@@ -16,6 +16,7 @@
 
 package org.onosproject.segmentrouting.config;
 
+import com.fasterxml.jackson.databind.node.ArrayNode;
 import org.onlab.packet.Ip4Address;
 import org.onlab.packet.MacAddress;
 import org.onosproject.net.DeviceId;
@@ -35,6 +36,7 @@
     private static final String MAC = "routerMac";
     private static final String SID = "nodeSid";
     private static final String EDGE = "isEdgeRouter";
+    private static final String ADJSID = "adjacencySids";
 
     public Optional<String> getName() {
         String name = get(NAME, null);
@@ -79,22 +81,44 @@
         return (BasicElementConfig) setOrClear(EDGE, isEdgeRouter);
     }
 
-    // TODO extract array from JsonNode
     public List<AdjacencySid> getAdjacencySids() {
-        return new ArrayList<AdjacencySid>();
+        ArrayList<AdjacencySid> adjacencySids = new ArrayList<>();
+
+        if (!object.has(ADJSID)) {
+            return adjacencySids;
+        }
+
+        ArrayNode adjacencySidNodes = (ArrayNode) object.path(ADJSID);
+        adjacencySidNodes.forEach(adjacencySidNode -> {
+            int asid = adjacencySidNode.path(AdjacencySid.ASID).asInt();
+
+            ArrayList<Integer> ports = new ArrayList<Integer>();
+            ArrayNode portsNodes = (ArrayNode) adjacencySidNode.path(AdjacencySid.PORTS);
+            portsNodes.forEach(portNode -> {
+                ports.add(portNode.asInt());
+            });
+
+            AdjacencySid adjacencySid = new AdjacencySid(asid, ports);
+            adjacencySids.add(adjacencySid);
+        });
+
+        return adjacencySids;
     }
 
     public class AdjacencySid {
-        int sid;
+        private static final String ASID = "adjSid";
+        private static final String PORTS = "ports";
+
+        int asid;
         List<Integer> ports;
 
-        public AdjacencySid(int sid, List<Integer> ports) {
-            this.sid = sid;
+        public AdjacencySid(int asid, List<Integer> ports) {
+            this.asid = asid;
             this.ports = ports;
         }
 
-        public int getSid() {
-            return sid;
+        public int getAsid() {
+            return asid;
         }
 
         public List<Integer> getPorts() {
diff --git a/tools/package/config/samples/network-cfg.json b/tools/package/config/samples/network-cfg.json
index b3705d6..c2af8b8 100644
--- a/tools/package/config/samples/network-cfg.json
+++ b/tools/package/config/samples/network-cfg.json
@@ -19,6 +19,30 @@
 	    ]
 	}
     },
+    "devices" : {
+    	"of:0000000000000002" : {
+    		"segmentrouting" : {
+                "name" : "Leaf-R1",
+                "nodeSid" : 101,
+                "routerIp" : "10.0.1.254",
+                "routerMac" : "00:00:00:00:01:80",
+                "isEdgeRouter" : true,
+                "adjacencySids" : [
+                    { "sid" : 100, "port" : [2, 3] },
+                    { "sid" : 200, "port" : [4, 5] }
+                ]
+            }
+    	},
+    	"of:0000000000000191" : {
+    		"segmentrouting" : {
+                "name" : "Spine-R1",
+                "nodeSid" : 105,
+                "routerIp" : "192.168.0.11",
+                "routerMac" : "00:00:01:00:11:80",
+                "isEdgeRouter" : false
+            }
+    	}
+    },
     "apps" : {
 	"org.onosproject.router" : {
 	    "bgp" : {
diff --git a/tools/package/config/samples/segmentrouting.conf b/tools/package/config/samples/segmentrouting.conf
deleted file mode 100644
index fca41e0..0000000
--- a/tools/package/config/samples/segmentrouting.conf
+++ /dev/null
@@ -1,95 +0,0 @@
-{
-  "comment": "Configuration for a 4X4 leaf-spine fabric",
-  "restrictSwitches": true,
-  "restrictLinks": true,
-
-  "switchConfig":
-             [
-               { "nodeDpid" : "of:0000000000000001", "name": "Leaf-R1", "type": "Router_SR", "allowed": true,
-                 "latitude": 80.80, "longitude": 90.10,
-                 "params": { "routerIp": "10.0.1.254/32",
-                             "routerMac": "00:00:00:00:01:80",
-                             "nodeSid": 101,
-                             "isEdgeRouter" : true,
-                             "subnets": [
-                                         { "portNo": 1, "subnetIp": "10.0.1.254/24" }
-                                         ]
-                             }
-                 },
-
-               { "nodeDpid": "of:0000000000000002", "name": "Leaf-R2", "type": "Router_SR", "allowed": true,
-                 "latitude": 80.80, "longitude": 90.10,
-                 "params": { "routerIp": "10.0.2.254/32",
-                             "routerMac": "00:00:00:00:02:80",
-                             "nodeSid": 102,
-                             "isEdgeRouter" : true,
-                             "subnets": [
-                                         { "portNo": 1, "subnetIp": "10.0.2.254/24" }
-                                         ]
-                             }
-
-                 },
-
-               { "nodeDpid": "of:0000000000000003", "name": "Leaf-R3", "type": "Router_SR", "allowed": true,
-                 "latitude": 80.80, "longitude": 90.10,
-                 "params": { "routerIp": "10.0.3.101/32",
-                             "routerMac": "00:00:00:00:03:80",
-                             "nodeSid": 103,
-                             "isEdgeRouter" : true,
-                             "subnets": [
-                                         { "portNo": 1, "subnetIp": "10.0.3.254/24" }
-                                         ]
-                             }
-
-               },
-
-               { "nodeDpid": "of:0000000000000004", "name": "Leaf-R4", "type": "Router_SR", "allowed": true,
-                 "latitude": 80.80, "longitude": 90.10,
-                 "params": { "routerIp": "10.0.4.101/32",
-                             "routerMac": "00:00:00:00:04:80",
-                             "nodeSid": 104,
-                             "isEdgeRouter" : true,
-                             "subnets": [
-                                         { "portNo": 1, "subnetIp": "10.0.4.254/24" }
-                                         ]
-                             }
-
-               },
-
-		{ "nodeDpid": "of:0000000000000191", "name": "Spine-R1", "type": "Router_SR", "allowed": true,
-                 "latitude": 80.80, "longitude": 90.10,
-                 "params": { "routerIp": "192.168.0.11/32",
-                             "routerMac": "00:00:01:00:11:80",
-                             "nodeSid": 105,
-                             "isEdgeRouter" : false
-                             }
-                 },
-
-        { "nodeDpid": "of:0000000000000192", "name": "Spine-R2", "type": "Router_SR", "allowed": true,
-                 "latitude": 80.80, "longitude": 90.10,
-                 "params": { "routerIp": "192.168.0.22/32",
-                             "routerMac": "00:00:01:00:22:80",
-                             "nodeSid": 106,
-                             "isEdgeRouter" : false
-                             }
-                 },
-
-        { "nodeDpid": "of:0000000000000193", "name": "Spine-R3", "type": "Router_SR", "allowed": true,
-                 "latitude": 80.80, "longitude": 90.10,
-                 "params": { "routerIp": "192.168.0.33/32",
-                             "routerMac": "00:00:01:00:33:80",
-                             "nodeSid": 107,
-                             "isEdgeRouter" : false
-                             }
-                 },
-
-		{ "nodeDpid": "of:0000000000000194", "name": "Spine-R4", "type": "Router_SR", "allowed": true,
-                 "latitude": 80.80, "longitude": 90.10,
-                 "params": { "routerIp": "192.168.0.44/32",
-                             "routerMac": "00:00:01:00:44:80",
-                             "nodeSid": 108,
-                             "isEdgeRouter" : false
-			   }
-                 }
-               ]
-}