/*
* Copyright (c) 2013, 2015 IBM Corporation and others. All rights reserved.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v1.0 which accompanies this distribution,
* and is available at http://www.eclipse.org/legal/epl-v10.html
*/
package org.opendaylight.openflowplugin.applications.statistics.manager.impl.helper;
import java.util.ArrayList;
import java.util.Collection;
import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.flow.Match;
import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.Flow;
public final class FlowComparatorFactory {
private FlowComparatorFactory() {
// NOOP
}
private static final Collection<SimpleComparator<Match>> MATCH_COMPARATORS = new ArrayList<>();
static {
MATCH_COMPARATORS.add(MatchComparatorFactory.createEthernet());
MATCH_COMPARATORS.add(MatchComparatorFactory.createIcmpv4());
MATCH_COMPARATORS.add(MatchComparatorFactory.createInPhyPort());
MATCH_COMPARATORS.add(MatchComparatorFactory.createInPort());
MATCH_COMPARATORS.add(MatchComparatorFactory.createIp());
MATCH_COMPARATORS.add(MatchComparatorFactory.createL3());
MATCH_COMPARATORS.add(MatchComparatorFactory.createL4());
MATCH_COMPARATORS.add(MatchComparatorFactory.createProtocolMatchFields());
MATCH_COMPARATORS.add(MatchComparatorFactory.createMetadata());
MATCH_COMPARATORS.add(MatchComparatorFactory.createNull());
MATCH_COMPARATORS.add(MatchComparatorFactory.createTunnel());
MATCH_COMPARATORS.add(MatchComparatorFactory.createVlan());
}
public static SimpleComparator<Flow> createContainerName() {
return new SimpleComparator<Flow>() {
/**
* Compares flows by container name
*/
@Override
public boolean areObjectsEqual(Flow statsFlow, Flow storedFlow) {
if (statsFlow.getContainerName() == null) {
if (storedFlow.getContainerName() != null) {
return false;
}
} else if (!statsFlow.getContainerName().equals(storedFlow.getContainerName())) {
return false;
}
return true;
}
};
}
public static SimpleComparator<Flow> createPriority() {
return new SimpleComparator<Flow>() {
/**
* Compares flows by priority
*/
@Override
public boolean areObjectsEqual(final Flow statsFlow, final Flow storedFlow) {
if (storedFlow.getPriority() == null) {
if (statsFlow.getPriority() != null && statsFlow.getPriority() != 0x8000) {
return false;
}
} else if (!statsFlow.getPriority().equals(storedFlow.getPriority())) {
return false;
}
return true;
}
};
}
public static SimpleComparator<Flow> createTableId() {
return new SimpleComparator<Flow>() {
/**
* Compares flows by table ID
*/
@Override
public boolean areObjectsEqual(final Flow statsFlow, final Flow storedFlow) {
if (statsFlow.getTableId() == null) {
if (storedFlow.getTableId() != null) {
return false;
}
} else if (!statsFlow.getTableId().equals(storedFlow.getTableId())) {
return false;
}
return true;
}
};
}
/*
* TODO:Cookie is used in flow comparison for the applications using match extensions
* in their flow body. As of now openflowplugin don't use match extensions
* in flow comparison, that can create a scenario where more then one stored flow
* can match to any stats flow, if stored flows differ only by match extension.
* Once match extensions are part of flow comparison, we should remove cookie
* from flow comparison.
*/
public static SimpleComparator<Flow> createCookie() {
return new SimpleComparator<Flow>() {
/**
* Compares flows by cookie value
*/
@Override
public boolean areObjectsEqual(final Flow statsFlow, final Flow storedFlow) {
/*
* Cookie is an optional field, so user might not set it, but if switch
* get flow without cookie value , it will use 0 as a default cookie value
* and return cookie=0 when openflowplugin fetch the flow stats from switch.
* In this scenario flow comparison will fail. Below check make sure that
* if user didn't set cookie value while flow installation, skip the comparison.
*/
if(storedFlow.getCookie() == null){
return true;
}
if (statsFlow.getCookie() == null) {
if (storedFlow.getCookie() != null) {
return false;
}
} else if (!statsFlow.getCookie().equals(storedFlow.getCookie())) {
return false;
}
return true;
}
};
}
public static SimpleComparator<Flow> createMatch() {
return new SimpleComparator<Flow>() {
/**
* Compares flows by whole match
*/
@Override
public boolean areObjectsEqual(final Flow statsFlow, final Flow storedFlow) {
if (statsFlow.getMatch() == null) {
if (storedFlow.getMatch() != null) {
return false;
}
} else if (!compareMatches(statsFlow.getMatch(), storedFlow.getMatch())) {
return false;
}
return true;
}
};
}
/**
* Explicit equals method to compare the 'match' for flows stored in the data-stores and flow fetched from the switch.
* 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
*
* 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.
*
* 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.
*
*
* @param statsMatch
* @param storedMatch
* @return
*/
private static boolean compareMatches(final Match statsMatch, final Match storedMatch) {
if (statsMatch == storedMatch) {
return true;
}
for (SimpleComparator<Match> matchComp : MATCH_COMPARATORS) {
if (!matchComp.areObjectsEqual(statsMatch, storedMatch)) {
return false;
}
}
return true;
}
}