Gerrit contains following fixes: 22/4822/1
authorAnil Vishnoi <avishnoi@in.ibm.com>
Sun, 26 Jan 2014 14:17:19 +0000 (19:47 +0530)
committerAnil Vishnoi <avishnoi@in.ibm.com>
Sun, 26 Jan 2014 20:11:47 +0000 (01:41 +0530)
1) Fix for bug 284 - Added custom ip matching method to compare ip addresses with mask
2) Fix for bug 286
3) Fix for bug 379

Change-Id: I64b36a817eab6654d24b3f91835cdc068f19c401
Signed-off-by: Anil Vishnoi <avishnoi@in.ibm.com>
opendaylight/md-sal/statistics-manager/src/main/java/org/opendaylight/controller/md/statistics/manager/StatisticsProvider.java
opendaylight/md-sal/statistics-manager/src/main/java/org/opendaylight/controller/md/statistics/manager/StatisticsUpdateCommiter.java

index b925ebdc99738b72aa98e2a3186cc6d57f9578cb..325b342cd88bdb71d6d2138169f2f7ae36c83785 100644 (file)
@@ -293,23 +293,27 @@ public class StatisticsProvider implements AutoCloseable {
         NodeRef targetNodeRef = new NodeRef(targetInstanceId);
     
         try{
-            sendAggregateFlowsStatsFromAllTablesRequest(targetNode.getKey());
-        
-            sendAllFlowsStatsFromAllTablesRequest(targetNodeRef);
-
-            sendAllNodeConnectorsStatisticsRequest(targetNodeRef);
-        
-            sendAllFlowTablesStatisticsRequest(targetNodeRef);
-        
-            sendAllQueueStatsFromAllNodeConnector (targetNodeRef);
-
-            sendAllGroupStatisticsRequest(targetNodeRef);
-            
-            sendAllMeterStatisticsRequest(targetNodeRef);
-            
-            sendGroupDescriptionRequest(targetNodeRef);
-            
-            sendMeterConfigStatisticsRequest(targetNodeRef);
+            if(flowStatsService != null){
+                sendAggregateFlowsStatsFromAllTablesRequest(targetNode.getKey());
+                sendAllFlowsStatsFromAllTablesRequest(targetNodeRef);
+            }
+            if(flowTableStatsService != null){
+                sendAllFlowTablesStatisticsRequest(targetNodeRef);
+            }
+            if(portStatsService != null){
+                sendAllNodeConnectorsStatisticsRequest(targetNodeRef);
+            }
+            if(groupStatsService != null){
+                sendAllGroupStatisticsRequest(targetNodeRef);
+                sendGroupDescriptionRequest(targetNodeRef);
+            }
+            if(meterStatsService != null){
+                sendAllMeterStatisticsRequest(targetNodeRef);
+                sendMeterConfigStatisticsRequest(targetNodeRef);
+            }
+            if(queueStatsService != null){
+                sendAllQueueStatsFromAllNodeConnector (targetNodeRef);
+            }
         }catch(Exception e){
             spLogger.error("Exception occured while sending statistics requests : {}", e);
         }
@@ -366,23 +370,28 @@ public class StatisticsProvider implements AutoCloseable {
         if(tablesId.size() != 0){
             for(Short id : tablesId){
                 
-                spLogger.debug("Send aggregate stats request for flow table {} to node {}",id,targetNodeKey);
-                GetAggregateFlowStatisticsFromFlowTableForAllFlowsInputBuilder input = 
-                        new GetAggregateFlowStatisticsFromFlowTableForAllFlowsInputBuilder();
-                
-                input.setNode(new NodeRef(InstanceIdentifier.builder(Nodes.class).child(Node.class, targetNodeKey).toInstance()));
-                input.setTableId(new org.opendaylight.yang.gen.v1.urn.opendaylight.table.types.rev131026.TableId(id));
-                Future<RpcResult<GetAggregateFlowStatisticsFromFlowTableForAllFlowsOutput>> response = 
-                        flowStatsService.getAggregateFlowStatisticsFromFlowTableForAllFlows(input.build());
-                
-                multipartMessageManager.setTxIdAndTableIdMapEntry(targetNodeKey.getId(), response.get().getResult().getTransactionId(), id);
-                this.multipartMessageManager.addTxIdToRequestTypeEntry(targetNodeKey.getId(), response.get().getResult().getTransactionId()
-                        , StatsRequestType.AGGR_FLOW);
+                sendAggregateFlowsStatsFromTableRequest(targetNodeKey,id);
             }
         }else{
             spLogger.debug("No details found in data store for flow tables associated with Node {}",targetNodeKey);
         }
     }
+    
+    public void sendAggregateFlowsStatsFromTableRequest(NodeKey targetNodeKey,Short tableId) throws InterruptedException, ExecutionException{
+        
+        spLogger.debug("Send aggregate stats request for flow table {} to node {}",tableId,targetNodeKey);
+        GetAggregateFlowStatisticsFromFlowTableForAllFlowsInputBuilder input = 
+                new GetAggregateFlowStatisticsFromFlowTableForAllFlowsInputBuilder();
+                
+        input.setNode(new NodeRef(InstanceIdentifier.builder(Nodes.class).child(Node.class, targetNodeKey).toInstance()));
+        input.setTableId(new org.opendaylight.yang.gen.v1.urn.opendaylight.table.types.rev131026.TableId(tableId));
+        Future<RpcResult<GetAggregateFlowStatisticsFromFlowTableForAllFlowsOutput>> response = 
+                flowStatsService.getAggregateFlowStatisticsFromFlowTableForAllFlows(input.build());
+                
+        multipartMessageManager.setTxIdAndTableIdMapEntry(targetNodeKey.getId(), response.get().getResult().getTransactionId(), tableId);
+        this.multipartMessageManager.addTxIdToRequestTypeEntry(targetNodeKey.getId(), response.get().getResult().getTransactionId()
+                , StatsRequestType.AGGR_FLOW);
+    }
 
     public void sendAllNodeConnectorsStatisticsRequest(NodeRef targetNode) throws InterruptedException, ExecutionException{
         
index 907534bcf5a240c8ea661d24d8d086860c1b0bcc..19f25944b45f0ce34a94c9224ce4b107da4f467b 100644 (file)
@@ -7,12 +7,16 @@
  */
 package org.opendaylight.controller.md.statistics.manager;
 
+import java.net.Inet4Address;
+import java.net.InetAddress;
+import java.net.UnknownHostException;
 import java.util.List;
 import java.util.concurrent.ConcurrentMap;
 
 import org.opendaylight.controller.md.statistics.manager.NodeStatisticsAger.FlowEntry;
 import org.opendaylight.controller.md.statistics.manager.NodeStatisticsAger.QueueEntry;
 import org.opendaylight.controller.sal.binding.api.data.DataModificationTransaction;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.Ipv4Prefix;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowCapableNode;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowCapableNodeConnector;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowId;
@@ -88,6 +92,8 @@ import org.opendaylight.yang.gen.v1.urn.opendaylight.meter.statistics.rev131111.
 import org.opendaylight.yang.gen.v1.urn.opendaylight.meter.statistics.rev131111.nodes.node.meter.MeterStatisticsBuilder;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.meter.types.rev130918.meter.config.stats.reply.MeterConfigStats;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.meter.types.rev130918.meter.statistics.reply.MeterStats;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev131026.match.Layer3Match;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev131026.match.layer._3.match.Ipv4Match;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.model.statistics.types.rev130925.GenericStatistics;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.port.statistics.rev131214.FlowCapableNodeConnectorStatisticsData;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.port.statistics.rev131214.FlowCapableNodeConnectorStatisticsDataBuilder;
@@ -445,11 +451,13 @@ public class StatisticsUpdateCommiter implements OpendaylightGroupStatisticsList
             //TODO: Not a good way to do it, need to figure out better way.
             //TODO: major issue in any alternate approach is that flow key is incrementally assigned 
             //to the flows stored in data store.
+            // Augment same statistics to all the matching masked flow
             if(table != null){
 
                 for(Flow existingFlow : table.getFlow()){
                     sucLogger.debug("Existing flow in data store : {}",existingFlow.toString());
                     if(flowEquals(flowRule,existingFlow)){
+                        it = this.statisticsManager.startChange();
                         InstanceIdentifier<Flow> flowRef = InstanceIdentifier.builder(Nodes.class).child(Node.class, key)
                                 .augmentation(FlowCapableNode.class)
                                 .child(Table.class, new TableKey(tableId))
@@ -460,7 +468,6 @@ public class StatisticsUpdateCommiter implements OpendaylightGroupStatisticsList
                         foundOriginalFlow = true;
                         it.putOperationalData(flowRef, flowBuilder.build());
                         it.commit();
-                        break;
                     }
                 }
             }
@@ -703,8 +710,8 @@ public class StatisticsUpdateCommiter implements OpendaylightGroupStatisticsList
         else if(!matchEquals(statsFlow.getMatch(), storedFlow.getMatch())) {
             return false;
         }
-        if (statsFlow.getPriority() == null) {
-            if (storedFlow.getPriority() != null) {
+        if (storedFlow.getPriority() == null) {
+            if (statsFlow.getPriority() != null && statsFlow.getPriority()!= 0x8000) {
                 return false;
             }
         } else if(!statsFlow.getPriority().equals(storedFlow.getPriority())) {
@@ -722,24 +729,23 @@ public class StatisticsUpdateCommiter implements OpendaylightGroupStatisticsList
     
     /**
      * Explicit equals method to compare the 'match' for flows stored in the data-stores and flow fetched from the switch.
-     * Usecase: e.g If user don't set any ethernet source and destination address for match,data store will store null for 
-     * these address.
-     * e.g [_ethernetMatch=EthernetMatch [_ethernetDestination=null, _ethernetSource=null, _ethernetType=
-     * EthernetType [_type=EtherType [_value=2048], _mask=null, augmentation=[]]
+     * Flow installation process has three steps 
+     * 1) Store flow in config data store 
+     * 2) and send it to plugin for installation
+     * 3) Flow gets installed in switch
      * 
-     * But when you fetch the flows from switch, openflow driver library converts all zero bytes of mac address in the 
-     * message stream to 00:00:00:00:00:00. Following string shows how library interpret the zero mac address bytes and 
-     * eventually when translator convert it to MD-SAL match, this is how it looks 
-     * [_ethernetDestination=EthernetDestination [_address=MacAddress [_value=00:00:00:00:00:00], _mask=null, augmentation=[]], 
-     * _ethernetSource=EthernetSource [_address=MacAddress [_value=00:00:00:00:00:00], _mask=null, augmentation=[]], 
-     * _ethernetType=EthernetType [_type=EtherType [_value=2048], _mask=null, augmentation=[]]
+     * The flow user wants to install and what finally gets installed in switch can be slightly different. 
+     * E.g, If user installs flow with src/dst ip=10.0.0.1/24, when it get installed in the switch
+     * src/dst ip will be changes to 10.0.0.0/24 because of netmask of 24. When statistics manager fetch 
+     * stats it gets 10.0.0.0/24 rather then 10.0.0.1/24. Custom match takes care of by using masked ip
+     * while comparing two ip addresses.
      * 
-     * Similarly for inPort, if user/application don't set any value for it, FRM will store null value for it in data store. 
-     * When we fetch the same flow (with its statistics) from switch, plugin converts its value to openflow:X:0.
-     *  e.g _inPort=Uri [_value=openflow:1:0]  
+     * Sometimes when user don't provide few values that is required by flow installation request, like 
+     * priority,hard timeout, idle timeout, cookies etc, plugin usages default values before sending 
+     * request to the switch. So when statistics manager gets flow statistics, it gets the default value.
+     * But the flow stored in config data store don't have those defaults value. I included those checks 
+     * in the customer flow/match equal function. 
      * 
-     * So this custom equals method add additional check to take care of these scenario, in case any match element is null in data-store-flow, but not
-     * in the flow fetched from switch.
      * 
      * @param statsFlow
      * @param storedFlow
@@ -799,7 +805,7 @@ public class StatisticsUpdateCommiter implements OpendaylightGroupStatisticsList
             if (statsFlow.getLayer3Match() != null) {
                     return false;
             }
-        } else if(!storedFlow.getLayer3Match().equals(statsFlow.getLayer3Match())) {
+        } else if(!layer3MatchEquals(statsFlow.getLayer3Match(),storedFlow.getLayer3Match())) {
             return false;
         }
         if (storedFlow.getLayer4Match()== null) {
@@ -839,4 +845,100 @@ public class StatisticsUpdateCommiter implements OpendaylightGroupStatisticsList
         }
         return true;
     }
+
+    private boolean layer3MatchEquals(Layer3Match statsLayer3Match, Layer3Match storedLayer3Match){
+
+        if(statsLayer3Match instanceof Ipv4Match && storedLayer3Match instanceof Ipv4Match){
+            Ipv4Match statsIpv4Match = (Ipv4Match)statsLayer3Match;
+            Ipv4Match storedIpv4Match = (Ipv4Match)storedLayer3Match;
+            
+            if (storedIpv4Match.getIpv4Destination()== null) {
+                if (statsIpv4Match.getIpv4Destination()!= null) {
+                    return false;
+                }
+            } else if(!IpAddressEquals(statsIpv4Match.getIpv4Destination(),storedIpv4Match.getIpv4Destination())){
+                return false;
+            }
+            if (storedIpv4Match.getIpv4Source() == null) {
+                if (statsIpv4Match.getIpv4Source() != null) {
+                    return false;
+                }
+            } else if(!IpAddressEquals(statsIpv4Match.getIpv4Source(),storedIpv4Match.getIpv4Source())) {
+                return false;
+            }
+            
+            return true;
+        }else{
+            return storedLayer3Match.equals(statsLayer3Match);
+        }
+    }
+    
+    private boolean IpAddressEquals(Ipv4Prefix statsIpAddress, Ipv4Prefix storedIpAddress) {
+        IntegerIpAddress statsIpAddressInt = StrIpToIntIp(statsIpAddress.getValue());
+        IntegerIpAddress storedIpAddressInt = StrIpToIntIp(storedIpAddress.getValue());
+
+        if(IpAndMaskBasedMatch(statsIpAddressInt,storedIpAddressInt)){
+            return true;
+        }
+        if(IpBasedMatch(statsIpAddressInt,storedIpAddressInt)){
+            return true;
+        }
+        return false;
+    }
+
+    private boolean IpAndMaskBasedMatch(IntegerIpAddress statsIpAddressInt,IntegerIpAddress storedIpAddressInt){
+        return ((statsIpAddressInt.getIp() & statsIpAddressInt.getMask()) ==  (storedIpAddressInt.getIp() & storedIpAddressInt.getMask()));
+    }
+
+    private boolean IpBasedMatch(IntegerIpAddress statsIpAddressInt,IntegerIpAddress storedIpAddressInt){
+        return (statsIpAddressInt.getIp() == storedIpAddressInt.getIp());
+    }
+    
+    /*
+     * Method return integer version of ip address. Converted int will be mask if 
+     * mask specified
+     */
+    private IntegerIpAddress StrIpToIntIp(String ipAddresss){
+        
+        String[] parts = ipAddresss.split("/");
+        String ip = parts[0];
+        int prefix;
+
+        if (parts.length < 2) {
+            prefix = 32;
+        } else {
+            prefix = Integer.parseInt(parts[1]);
+        }
+
+        Inet4Address addr =null;
+        try {
+            addr = (Inet4Address) InetAddress.getByName(ip);
+        } catch (UnknownHostException e){}
+
+        byte[] addrBytes = addr.getAddress();
+        int ipInt = ((addrBytes[0] & 0xFF) << 24) |
+                         ((addrBytes[1] & 0xFF) << 16) |
+                         ((addrBytes[2] & 0xFF) << 8)  |
+                         ((addrBytes[3] & 0xFF) << 0);
+
+        int mask = 0xffffffff << 32 - prefix;
+
+        return new IntegerIpAddress(ipInt, mask);
+    }
+    
+    class IntegerIpAddress{
+        int ip;
+        int mask;
+        public IntegerIpAddress(int ip, int mask) {
+            this.ip = ip;
+            this.mask = mask;
+        }
+        public int getIp() {
+            return ip;
+        }
+        public int getMask() {
+            return mask;
+        }
+    }
 }
+