Adding support for adjacency label configuration, auto-generation
and publication

Change-Id: I9e5195c99299c7457230d71dad7002d15bf62bf9
diff --git a/conf/onos.properties b/conf/onos.properties
index 60283c8..a3e1d19 100644
--- a/conf/onos.properties
+++ b/conf/onos.properties
@@ -23,5 +23,4 @@
 # Uncomment and list all the ZooKeeper instances after localhost on multi-instance deployment.
 #net.onrc.onos.core.registry.ZookeeperRegistry.connectionString = localhost:2181,otherhost:2181
 # Specify a network configuration file to be used by the NetworkConfigManager
-net.onrc.onos.core.configmanager.NetworkConfigManager.networkConfigFile = conf/sr-ecmp10.conf
-
+net.onrc.onos.core.configmanager.NetworkConfigManager.networkConfigFile = conf/sr-ecmp.conf
diff --git a/conf/sr-3node.conf b/conf/sr-3node.conf
index 19eeff5..8d0a25b 100644
--- a/conf/sr-3node.conf
+++ b/conf/sr-3node.conf
@@ -11,10 +11,6 @@
                              "routerMac": "00:00:01:01:01:80",
                              "nodeSid": 101,
                              "isEdgeRouter" : true,
-                             "adjacencySids": [
-                                               { "portNo": 6, "adjSid": 10234 },
-                                               { "portNo": 7, "adjSid": 29019 }
-                                               ],
                              "subnets": [
                                          { "portNo": 1, "subnetIp": "10.0.1.128/24" },
                     					 { "portNo": 2, "subnetIp": "10.0.1.128/24" },
@@ -30,12 +26,7 @@
                  "params": { "routerIp": "192.168.0.2/32",
                              "routerMac": "00:00:02:02:02:80",
                              "nodeSid": 102,
-                             "isEdgeRouter" : false,
-                             "adjacencySids": [
-                                               { "portNo": 1, "adjSid": 12453 },
-                                               { "portNo": 2, "adjSid": 23333 },
-                                               { "portNo": 3, "adjSid": 22233 }
-                                               ]
+                             "isEdgeRouter" : false
                              }
                  },
 
@@ -45,9 +36,6 @@
                              "routerMac": "00:00:07:07:07:80",
                              "nodeSid": 103,
                              "isEdgeRouter" : true,
-                             "adjacencySids": [
-                                               { "portNo": 2, "adjSid": 92033 }
-                                               ],
                              "subnets": [
                                          { "portNo": 1, "subnetIp": "7.7.7.128/24" }
                                          ]
diff --git a/conf/sr-ecmp.conf b/conf/sr-ecmp.conf
index a6c2d79..7c2c812 100644
--- a/conf/sr-ecmp.conf
+++ b/conf/sr-ecmp.conf
@@ -1,5 +1,5 @@
 {
-  "comment": " Multilayer topology description and configuration",
+  "comment": " Fist topology 1 - [2,3] - [5,4] - 6",
   "restrictSwitches": true,
   "restrictLinks": true,
 
@@ -11,10 +11,6 @@
                              "routerMac": "00:00:01:01:01:80",
                              "nodeSid": 101,
                              "isEdgeRouter" : true,
-                             "adjacencySids": [
-                                               { "portNo": 6, "adjSid": 10234 },
-                                               { "portNo": 7, "adjSid": 29019 }
-                                               ],
                              "subnets": [
                                          { "portNo": 1, "subnetIp": "10.0.1.128/24" }
                                          ]
@@ -28,9 +24,7 @@
                              "nodeSid": 102,
                              "isEdgeRouter" : false,
                              "adjacencySids": [
-                                               { "portNo": 1, "adjSid": 12453 },
-                                               { "portNo": 2, "adjSid": 23333 },
-                                               { "portNo": 3, "adjSid": 22233 }
+                                               { "adjSid": 12453 , "ports": [ 2 ,3 ] }
                                                ]
                              }
                  },
@@ -40,10 +34,7 @@
                  "params": { "routerIp": "192.168.0.3/32",
                              "routerMac": "00:00:03:03:03:80",
                              "nodeSid": 103,
-                             "isEdgeRouter" : false,
-                             "adjacencySids": [
-                                               { "portNo": 2, "adjSid": 92033 }
-                                               ]
+                             "isEdgeRouter" : false
                              }
                  },
 
@@ -52,10 +43,7 @@
                  "params": { "routerIp": "192.168.0.4/32",
                              "routerMac": "00:00:04:04:04:80",
                              "nodeSid": 104,
-                             "isEdgeRouter" : false,
-                             "adjacencySids": [
-                                               { "portNo": 2, "adjSid": 92033 }
-                                               ]
+                             "isEdgeRouter" : false
                              }
                  },
 
@@ -64,10 +52,7 @@
                  "params": { "routerIp": "192.168.0.5/32",
                              "routerMac": "00:00:05:05:05:80",
                              "nodeSid": 105,
-                             "isEdgeRouter" : false,
-                             "adjacencySids": [
-                                               { "portNo": 2, "adjSid": 92033 }
-                                               ]
+                             "isEdgeRouter" : false
                              }
                  },
 
@@ -77,9 +62,6 @@
                              "routerMac": "00:00:07:07:07:80",
                              "nodeSid": 106,
                              "isEdgeRouter" : true,
-                             "adjacencySids": [
-                                               { "portNo": 2, "adjSid": 92033 }
-                                               ],
                              "subnets": [
                                          { "portNo": 1, "subnetIp": "7.7.7.128/24" }
                                          ]
diff --git a/src/main/java/net/floodlightcontroller/core/IOF13Switch.java b/src/main/java/net/floodlightcontroller/core/IOF13Switch.java
index 94b8930..4203c13 100644
--- a/src/main/java/net/floodlightcontroller/core/IOF13Switch.java
+++ b/src/main/java/net/floodlightcontroller/core/IOF13Switch.java
@@ -4,6 +4,7 @@
 import java.util.Collection;
 import java.util.HashSet;
 import java.util.List;
+import java.util.Map;
 import java.util.Set;
 
 import net.onrc.onos.core.matchaction.MatchActionOperationEntry;
@@ -167,4 +168,6 @@
      * @return first Group ID for the tunnel or -1 if not found
      */
     public int getTunnelGroupId(String tunnelID);
+
+    public Map<String, String> getPublishAttributes();
 }
diff --git a/src/main/java/net/onrc/onos/core/configmanager/SegmentRouterConfig.java b/src/main/java/net/onrc/onos/core/configmanager/SegmentRouterConfig.java
index 89df3c6..2020dea 100644
--- a/src/main/java/net/onrc/onos/core/configmanager/SegmentRouterConfig.java
+++ b/src/main/java/net/onrc/onos/core/configmanager/SegmentRouterConfig.java
@@ -33,6 +33,7 @@
     public static final String ADJACENCY_SIDS = "adjacencySids";
     public static final String SUBNETS = "subnets";
     public static final String ISEDGE = "isEdgeRouter";
+    private static final int SRGB_MAX = 1000;
 
     public SegmentRouterConfig(SwitchConfig swc) {
         this.setName(swc.getName());
@@ -86,20 +87,20 @@
     }
 
     public static class AdjacencySid {
-        private int portNo;
         private int adjSid;
+        private List<Integer> ports;
 
-        public AdjacencySid(int portNo, int adjSid) {
-            this.portNo = portNo;
+        public AdjacencySid(int adjSid, List<Integer> ports) {
+            this.ports = ports;
             this.adjSid = adjSid;
         }
 
-        public int getPortNo() {
-            return portNo;
+        public List<Integer> getPorts() {
+            return ports;
         }
 
-        public void setPortNo(int portNo) {
-            this.portNo = portNo;
+        public void setPorts(List<Integer> ports) {
+            this.ports = ports;
         }
 
         public int getAdjSid() {
@@ -190,6 +191,7 @@
             int portNo = -1;
             int adjSid = -1;
             String subnetIp = null;
+            List<Integer> ports = null;
             while (f.hasNext()) {
                 Entry<String, JsonNode> fe = f.next();
                 if (fe.getKey().equals("portNo")) {
@@ -198,12 +200,20 @@
                     adjSid = fe.getValue().asInt();
                 } else if (fe.getKey().equals("subnetIp")) {
                     subnetIp = fe.getValue().asText();
+                } else if (fe.getKey().equals("ports")) {
+                    if (fe.getValue().isArray()) {
+                        Iterator<JsonNode> i = fe.getValue().getElements();
+                        ports = new ArrayList<Integer>();
+                        while (i.hasNext()) {
+                            ports.add(i.next().asInt());
+                        }
+                    }
                 } else {
                     throw new UnknownSegmentRouterConfig(fe.getKey(), dpid);
                 }
             }
             if (innerParam.equals("adjacencySids")) {
-                AdjacencySid ads = new AdjacencySid(portNo, adjSid);
+                AdjacencySid ads = new AdjacencySid(adjSid, ports);
                 adjacencySids.add(ads);
             } else {
                 Subnet sip = new Subnet(portNo, subnetIp);
@@ -222,6 +232,20 @@
         if (!isEdgeRouter && !subnets.isEmpty()) {
             throw new SubnetSpecifiedInBackboneRouter(dpid);
         }
+        if (nodeSid > SRGB_MAX) {
+            throw new NodeLabelNotInSRGB(nodeSid, dpid);
+        }
+        for (AdjacencySid as : adjacencySids) {
+            int label = as.getAdjSid();
+            List<Integer> plist = as.getPorts();
+            if (label <= SRGB_MAX) {
+                throw new AdjacencyLabelInSRGB(label, dpid);
+            }
+            if (plist.size() <= 1) {
+                throw new AdjacencyLabelNotEnoughPorts(label, dpid);
+            }
+        }
+
 
         // TODO more validations
     }
@@ -284,7 +308,7 @@
     }
 
     public static class SubnetSpecifiedInBackboneRouter extends RuntimeException {
-        private static final long serialVersionUID = -5855458472668581268L;
+        private static final long serialVersionUID = 1L;
 
         public SubnetSpecifiedInBackboneRouter(long dpid) {
             super();
@@ -293,6 +317,37 @@
         }
     }
 
+    public static class NodeLabelNotInSRGB extends RuntimeException {
+        private static final long serialVersionUID = -8482670903748519526L;
 
+        public NodeLabelNotInSRGB(int label, long dpid) {
+            super();
+            log.error("Node sif {} specified in not in global label-base "
+                    + "in dpid: {}", label,
+                    HexString.toHexString(dpid));
+        }
+    }
 
+    public static class AdjacencyLabelInSRGB extends RuntimeException {
+        private static final long serialVersionUID = -8482670903748519526L;
+
+        public AdjacencyLabelInSRGB(int label, long dpid) {
+            super();
+            log.error("Adjaceny label {} specified from global label-base "
+                    + "in dpid: {}", label,
+                    HexString.toHexString(dpid));
+        }
+    }
+
+    public static class AdjacencyLabelNotEnoughPorts extends RuntimeException {
+        private static final long serialVersionUID = -8482670903748519526L;
+
+        public AdjacencyLabelNotEnoughPorts(int label, long dpid) {
+            super();
+            log.error("Adjaceny label {} must be specified for at least 2 ports. "
+                    + "Adjacency labels for single ports are auto-generated "
+                    + "in dpid: {}", label,
+                    HexString.toHexString(dpid));
+        }
+    }
 }
diff --git a/src/main/java/net/onrc/onos/core/drivermanager/OFSwitchImplCPqD13.java b/src/main/java/net/onrc/onos/core/drivermanager/OFSwitchImplCPqD13.java
index 2353fb7..fec88c5 100644
--- a/src/main/java/net/onrc/onos/core/drivermanager/OFSwitchImplCPqD13.java
+++ b/src/main/java/net/onrc/onos/core/drivermanager/OFSwitchImplCPqD13.java
@@ -4,9 +4,11 @@
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Collections;
+import java.util.HashMap;
 import java.util.HashSet;
 import java.util.Iterator;
 import java.util.List;
+import java.util.Map;
 import java.util.Set;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.ConcurrentMap;
@@ -30,6 +32,7 @@
 import net.onrc.onos.core.configmanager.NetworkConfigManager;
 import net.onrc.onos.core.configmanager.PktLinkConfig;
 import net.onrc.onos.core.configmanager.SegmentRouterConfig;
+import net.onrc.onos.core.configmanager.SegmentRouterConfig.AdjacencySid;
 import net.onrc.onos.core.matchaction.MatchAction;
 import net.onrc.onos.core.matchaction.MatchActionOperationEntry;
 import net.onrc.onos.core.matchaction.MatchActionOperations;
@@ -55,6 +58,7 @@
 import net.onrc.onos.core.util.IPv4Net;
 import net.onrc.onos.core.util.PortNumber;
 
+import org.codehaus.jackson.map.ObjectMapper;
 import org.projectfloodlight.openflow.protocol.OFAsyncGetReply;
 import org.projectfloodlight.openflow.protocol.OFBarrierRequest;
 import org.projectfloodlight.openflow.protocol.OFBucket;
@@ -141,11 +145,11 @@
     private ConcurrentMap<PortNumber, Dpid> portToNeighbors;
     private List<Integer> segmentIds;
     private boolean isEdgeRouter;
-    private int sid;
     private ConcurrentMap<NeighborSet, EcmpInfo> ecmpGroups;
     private ConcurrentMap<String, List<Integer>> tunnelGroupIdTable;
     private ConcurrentMap<PortNumber, ArrayList<NeighborSet>> portNeighborSetMap;
     private AtomicInteger groupid;
+    private Map<String, String> publishAttributes;
 
     public OFSwitchImplCPqD13(OFDescStatsReply desc, boolean usePipeline13) {
         super();
@@ -489,6 +493,7 @@
 
     private void processGroupDesc(OFGroupDescStatsReply gdsr) {
         log.info("Sw: {} Group Desc {}", getStringId(), gdsr);
+        // TODO -- actually do verification
         try {
             nextDriverState();
         } catch (IOException e) {
@@ -608,7 +613,6 @@
         if (scs.getConfigState() == NetworkConfigState.ACCEPT_ADD) {
             srConfig = (SegmentRouterConfig) scs.getSwitchConfig();
             isEdgeRouter = srConfig.isEdgeRouter();
-            sid = srConfig.getNodeSid();
         } else {
             log.error("Switch not configured as Segment-Router");
         }
@@ -1248,7 +1252,30 @@
     }
 
     private void assignAdjacencyLabels() {
-        // TODO
+        List<AdjacencySid> autogenAdjSids = new ArrayList<AdjacencySid>();
+        publishAttributes = new HashMap<String, String>();
+        for (OFPortDesc p : getPorts()) {
+            int pnum = p.getPortNo().getPortNumber();
+
+            if (U32.ofRaw(pnum).compareTo(U32.ofRaw(OFPort.MAX.getPortNumber())) >= 1) {
+                continue;
+            }
+            // create unique adj-sid assuming that operator only
+            // enters adjSids for multiple-ports and only in the range
+            // 1-10k XXX make sure that happens
+            int adjSid = srConfig.getNodeSid() * 1000 + pnum;
+            AdjacencySid as = new AdjacencySid(adjSid,
+                    Collections.singletonList(pnum));
+            autogenAdjSids.add(as);
+        }
+        ObjectMapper mapper = new ObjectMapper();
+        try {
+            publishAttributes.put("autogenAdjSids",
+                    mapper.writeValueAsString(autogenAdjSids));
+        } catch (IOException e1) {
+            log.error("Error while writing adjacency labels: {}", e1.getCause());
+        }
+
         try {
             nextDriverState();
         } catch (IOException e) {
@@ -1691,6 +1718,10 @@
         return groups.get(0);
     }
 
+    @Override
+    public Map<String, String> getPublishAttributes() {
+        return publishAttributes;
+    }
 
     // *****************************
     // Unused
@@ -1956,4 +1987,6 @@
         }
     }
 
+
+
 }
diff --git a/src/main/java/net/onrc/onos/core/topology/TopologyPublisher.java b/src/main/java/net/onrc/onos/core/topology/TopologyPublisher.java
index 2fe43ba..66e1b88 100644
--- a/src/main/java/net/onrc/onos/core/topology/TopologyPublisher.java
+++ b/src/main/java/net/onrc/onos/core/topology/TopologyPublisher.java
@@ -16,6 +16,7 @@
 
 import net.floodlightcontroller.core.IFloodlightProviderService;
 import net.floodlightcontroller.core.IFloodlightProviderService.Role;
+import net.floodlightcontroller.core.IOF13Switch;
 import net.floodlightcontroller.core.IOFSwitch;
 import net.floodlightcontroller.core.IOFSwitch.PortChangeType;
 import net.floodlightcontroller.core.IOFSwitchListener;
@@ -390,6 +391,12 @@
             for (Entry<String, String> e : attr.entrySet()) {
                 switchData.createStringAttribute(e.getKey(), e.getValue());
             }
+            if (sw instanceof IOF13Switch) {
+                Map<String, String> pa = ((IOF13Switch) sw).getPublishAttributes();
+                for (Entry<String, String> e : pa.entrySet()) {
+                    switchData.createStringAttribute(e.getKey(), e.getValue());
+                }
+            }
             switchData.createStringAttribute(TopologyElement.ELEMENT_CONFIG_STATE,
                     ConfigState.CONFIGURED.toString());
         } else {