From 558c0b7fc4c22e6ecacd86cf31016d355d9a7ee3 Mon Sep 17 00:00:00 2001 From: Anil Vishnoi Date: Sun, 26 Jan 2014 19:47:19 +0530 Subject: [PATCH 1/1] Gerrit contains following fixes: 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 --- .../manager/StatisticsProvider.java | 67 +++++---- .../manager/StatisticsUpdateCommiter.java | 140 +++++++++++++++--- 2 files changed, 159 insertions(+), 48 deletions(-) diff --git a/opendaylight/md-sal/statistics-manager/src/main/java/org/opendaylight/controller/md/statistics/manager/StatisticsProvider.java b/opendaylight/md-sal/statistics-manager/src/main/java/org/opendaylight/controller/md/statistics/manager/StatisticsProvider.java index b925ebdc99..325b342cd8 100644 --- a/opendaylight/md-sal/statistics-manager/src/main/java/org/opendaylight/controller/md/statistics/manager/StatisticsProvider.java +++ b/opendaylight/md-sal/statistics-manager/src/main/java/org/opendaylight/controller/md/statistics/manager/StatisticsProvider.java @@ -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> 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> 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{ diff --git a/opendaylight/md-sal/statistics-manager/src/main/java/org/opendaylight/controller/md/statistics/manager/StatisticsUpdateCommiter.java b/opendaylight/md-sal/statistics-manager/src/main/java/org/opendaylight/controller/md/statistics/manager/StatisticsUpdateCommiter.java index 907534bcf5..19f25944b4 100644 --- a/opendaylight/md-sal/statistics-manager/src/main/java/org/opendaylight/controller/md/statistics/manager/StatisticsUpdateCommiter.java +++ b/opendaylight/md-sal/statistics-manager/src/main/java/org/opendaylight/controller/md/statistics/manager/StatisticsUpdateCommiter.java @@ -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 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; + } + } } + -- 2.36.6