/**
- * Copyright IBM Corporation, 2013. All rights reserved.
+ * Copyright IBM Corporation and others, 2013-2014. 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,
*/
package org.opendaylight.openflowplugin.openflow.md.util;
+import java.math.BigInteger;
+import java.util.Objects;
import org.opendaylight.openflowplugin.api.OFConstants;
+import org.opendaylight.openflowplugin.openflow.md.core.sal.convertor.FlowConvertor;
import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.Ipv4Address;
import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev100924.MacAddress;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.flow.update.OriginalFlow;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.flow.update.UpdatedFlow;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.FlowCookie;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.FlowModFlags;
import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.common.types.rev130731.FlowWildcardsV10;
import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.oxm.rev130731.OxmMatchType;
import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.oxm.rev130731.match.grouping.Match;
import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.protocol.rev130731.multipart.request.multipart.request.body.multipart.request.flow._case.MultipartRequestFlowBuilder;
public final class FlowCreatorUtil {
+ /**
+ * Default FLOW_MOD flags.
+ */
+ public static final FlowModFlags DEFAULT_FLOW_MOD_FLAGS =
+ new FlowModFlags(FlowConvertor.DEFAULT_OFPFF_CHECK_OVERLAP,
+ FlowConvertor.DEFAULT_OFPFF_NO_BYT_COUNTS,
+ FlowConvertor.DEFAULT_OFPFF_NO_PKT_COUNTS,
+ FlowConvertor.DEFAULT_OFPFF_RESET_COUNTS,
+ FlowConvertor.DEFAULT_OFPFF_FLOW_REM);
private FlowCreatorUtil() {
throw new AssertionError("FlowCreatorUtil is not expected to be instantiated.");
public static Match createWildcardedMatch() {
return new MatchBuilder().setType(OxmMatchType.class).build();
}
+
+ /**
+ * Determine whether a flow entry can be modified or not.
+ *
+ * @param original An original flow entry.
+ * @param updated An updated flow entry.
+ * @param version Protocol version.
+ * @return {@code true} only if a flow entry can be modified.
+ */
+ public static boolean canModifyFlow(OriginalFlow original,
+ UpdatedFlow updated, Short version) {
+ // FLOW_MOD does not change match, priority, idle_timeout, hard_timeout,
+ // flags, and cookie.
+ if (!Objects.equals(original.getMatch(), updated.getMatch()) ||
+ !equalsWithDefault(original.getPriority(), updated.getPriority(),
+ FlowConvertor.DEFAULT_PRIORITY) ||
+ !equalsWithDefault(original.getIdleTimeout(),
+ updated.getIdleTimeout(),
+ FlowConvertor.DEFAULT_IDLE_TIMEOUT) ||
+ !equalsWithDefault(original.getHardTimeout(),
+ updated.getHardTimeout(),
+ FlowConvertor.DEFAULT_HARD_TIMEOUT) ||
+ !equalsFlowModFlags(original.getFlags(), updated.getFlags())) {
+ return false;
+ }
+
+ if (!Boolean.TRUE.equals(updated.isStrict()) &&
+ version != null &&
+ version.shortValue() != OFConstants.OFP_VERSION_1_0) {
+ FlowCookie cookieMask = updated.getCookieMask();
+ if (cookieMask != null) {
+ BigInteger mask = cookieMask.getValue();
+ if (mask != null && !mask.equals(BigInteger.ZERO)) {
+ // Allow FLOW_MOD with filtering by cookie.
+ return true;
+ }
+ }
+ }
+
+ FlowCookie oc = original.getCookie();
+ FlowCookie uc = updated.getCookie();
+ BigInteger orgCookie;
+ BigInteger updCookie;
+ if (oc == null) {
+ if (uc == null) {
+ return true;
+ }
+
+ orgCookie = OFConstants.DEFAULT_COOKIE;
+ updCookie = uc.getValue();
+ } else {
+ orgCookie = oc.getValue();
+ updCookie = (uc == null)
+ ? OFConstants.DEFAULT_COOKIE : uc.getValue();
+ }
+
+ return equalsWithDefault(orgCookie, updCookie,
+ OFConstants.DEFAULT_COOKIE);
+ }
+
+ /**
+ * Return {@code true} only if given two FLOW_MOD flags are identical.
+ *
+ * @param flags1 A value to be compared.
+ * @param flags2 A value to be compared.
+ * @return
+ * {@code true} only if {@code flags1} and {@code flags2} are identical.
+ */
+ public static boolean equalsFlowModFlags(FlowModFlags flags1,
+ FlowModFlags flags2) {
+ FlowModFlags f1;
+ FlowModFlags f2;
+ if (flags1 == null) {
+ if (flags2 == null) {
+ return true;
+ }
+
+ f1 = DEFAULT_FLOW_MOD_FLAGS;
+ f2 = flags2;
+ } else {
+ f1 = flags1;
+ f2 = (flags2 == null) ? DEFAULT_FLOW_MOD_FLAGS : flags2;
+ }
+
+ return equalsWithDefault(f1.isCHECKOVERLAP(), f2.isCHECKOVERLAP(),
+ Boolean.FALSE) &&
+ equalsWithDefault(f1.isNOBYTCOUNTS(), f2.isNOBYTCOUNTS(),
+ Boolean.FALSE) &&
+ equalsWithDefault(f1.isNOPKTCOUNTS(), f2.isNOPKTCOUNTS(),
+ Boolean.FALSE) &&
+ equalsWithDefault(f1.isRESETCOUNTS(), f2.isRESETCOUNTS(),
+ Boolean.FALSE) &&
+ equalsWithDefault(f1.isSENDFLOWREM(), f2.isSENDFLOWREM(),
+ Boolean.FALSE);
+ }
+
+ /**
+ * Return {@code true} only if given two values are identical.
+ *
+ * @param value1 A value to be compared.
+ * @param value2 A value to be compared.
+ * @param def
+ * Default value. This value is used if {@code null} is passed to
+ * {@code value1} or {@code value2}.
+ * @param <T> Type of values.
+ * @return
+ * {@code true} only if {@code value1} and {@code value2} are identical.
+ */
+ public static <T> boolean equalsWithDefault(T value1, T value2, T def) {
+ if (value1 == null) {
+ return value2 == null || value2.equals(def);
+ } else if (value2 == null) {
+ return value1.equals(def);
+ }
+
+ return value1.equals(value2);
+ }
}
package org.opendaylight.openflowplugin.openflow.md.util;
import static junit.framework.Assert.assertTrue;
-import static junit.framework.TestCase.assertEquals;
+import static junit.framework.Assert.assertFalse;
+import static junit.framework.Assert.assertEquals;
+import java.math.BigInteger;
import org.junit.Test;
import org.opendaylight.openflowplugin.api.OFConstants;
import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.Ipv4Address;
import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev100924.MacAddress;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.flow.update.OriginalFlow;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.flow.update.OriginalFlowBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.flow.update.UpdatedFlow;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.flow.update.UpdatedFlowBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.FlowCookie;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.FlowModFlags;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.flow.MatchBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.l2.types.rev130827.EtherType;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev131026.ethernet.match.fields.EthernetTypeBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev131026.match.EthernetMatchBuilder;
import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.oxm.rev130731.OxmMatchType;
import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.oxm.rev130731.match.grouping.Match;
import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.oxm.rev130731.match.v10.grouping.MatchV10;
}
+ /**
+ * Test method for
+ * {@link FlowCreatorUtil#canModifyFlow(OriginalFlow, UpdatedFlow, Short)}.
+ */
+ @Test
+ public void testCanModifyFlow() {
+ Short of10 = Short.valueOf(OFConstants.OFP_VERSION_1_0);
+ Short of13 = Short.valueOf(OFConstants.OFP_VERSION_1_3);
+ Short[] versions = {null, of10, of13};
+ Boolean[] bools = {null, Boolean.TRUE, Boolean.FALSE};
+
+ Integer defPri = Integer.valueOf(0x8000);
+ Integer defIdle = Integer.valueOf(300);
+ Integer defHard = Integer.valueOf(600);
+ FlowModFlags defFlags = FlowModFlags.getDefaultInstance("sENDFLOWREM");
+ FlowModFlags flags = new FlowModFlags(false, true, false, true, false);
+ FlowCookie defCookie = new FlowCookie(BigInteger.ZERO);
+ FlowCookie cookie = new FlowCookie(BigInteger.valueOf(0x12345L));
+ FlowCookie cookie1 = new FlowCookie(BigInteger.valueOf(0x67890L));
+ FlowCookie cookieMask = new FlowCookie(BigInteger.valueOf(0xffff00L));
+
+ for (Short ver: versions) {
+ OriginalFlowBuilder originalBuilder = new OriginalFlowBuilder();
+ UpdatedFlowBuilder updatedBuilder = new UpdatedFlowBuilder();
+ canModifyFlowTest(true, originalBuilder, updatedBuilder, ver);
+
+ // Default value tests.
+ canModifyFlowTest(true,
+ new OriginalFlowBuilder().setPriority(defPri),
+ updatedBuilder, ver);
+ canModifyFlowTest(true, originalBuilder,
+ new UpdatedFlowBuilder().setPriority(defPri),
+ ver);
+ canModifyFlowTest(true,
+ new OriginalFlowBuilder().setIdleTimeout(defIdle),
+ updatedBuilder, ver);
+ canModifyFlowTest(true, originalBuilder,
+ new UpdatedFlowBuilder().setIdleTimeout(defIdle),
+ ver);
+ canModifyFlowTest(true,
+ new OriginalFlowBuilder().setHardTimeout(defHard),
+ updatedBuilder, ver);
+ canModifyFlowTest(true, originalBuilder,
+ new UpdatedFlowBuilder().setHardTimeout(defHard),
+ ver);
+ canModifyFlowTest(true,
+ new OriginalFlowBuilder().setFlags(defFlags),
+ updatedBuilder, ver);
+ canModifyFlowTest(true, originalBuilder,
+ new UpdatedFlowBuilder().setFlags(defFlags),
+ ver);
+ canModifyFlowTest(true,
+ new OriginalFlowBuilder().setCookie(defCookie),
+ updatedBuilder, ver);
+ canModifyFlowTest(true, originalBuilder,
+ new UpdatedFlowBuilder().setCookie(defCookie),
+ ver);
+
+ // Set non-default values.
+ canModifyFlowTest(true,
+ originalBuilder.setMatch(createMatch(0x800L)),
+ updatedBuilder.setMatch(createMatch(0x800L)),
+ ver);
+ canModifyFlowTest(true, originalBuilder.setIdleTimeout(600),
+ updatedBuilder.setIdleTimeout(600), ver);
+ canModifyFlowTest(true, originalBuilder.setHardTimeout(1200),
+ updatedBuilder.setHardTimeout(1200), ver);
+ canModifyFlowTest(true, originalBuilder.setPriority(100),
+ updatedBuilder.setPriority(100), ver);
+ canModifyFlowTest(true, originalBuilder.setFlags(flags),
+ updatedBuilder.setFlags(flags), ver);
+ canModifyFlowTest(true, originalBuilder.setCookie(cookie),
+ updatedBuilder.setCookie(cookie), ver);
+
+ OriginalFlow org = originalBuilder.build();
+ UpdatedFlow upd = updatedBuilder.build();
+
+ // Set different match.
+ org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.flow.Match[] matches = {null, createMatch(0x86ddL)};
+ for (org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.flow.Match m: matches) {
+ canModifyFlowTest(false, originalBuilder,
+ new UpdatedFlowBuilder(upd).setMatch(m),
+ ver);
+ canModifyFlowTest(false,
+ new OriginalFlowBuilder(org).setMatch(m),
+ updatedBuilder, ver);
+ }
+
+ // Set different idle-timeout, hard-timeout, priority.
+ Integer[] integers = {null, Integer.valueOf(3600)};
+ for (Integer i: integers) {
+ canModifyFlowTest(false, originalBuilder,
+ new UpdatedFlowBuilder(upd).setIdleTimeout(i),
+ ver);
+ canModifyFlowTest(false,
+ new OriginalFlowBuilder(org).setIdleTimeout(i),
+ updatedBuilder, ver);
+
+ canModifyFlowTest(false, originalBuilder,
+ new UpdatedFlowBuilder(upd).setHardTimeout(i),
+ ver);
+ canModifyFlowTest(false,
+ new OriginalFlowBuilder(org).setHardTimeout(i),
+ updatedBuilder, ver);
+
+ canModifyFlowTest(false, originalBuilder,
+ new UpdatedFlowBuilder(upd).setPriority(i),
+ ver);
+ canModifyFlowTest(false,
+ new OriginalFlowBuilder(org).setPriority(i),
+ updatedBuilder, ver);
+ }
+
+ // Set different FLOW_MOD flags.
+ FlowModFlags[] flowModFlags = {
+ null,
+ defFlags,
+ new FlowModFlags(true, true, true, true, true),
+ };
+ for (FlowModFlags f: flowModFlags) {
+ canModifyFlowTest(false, originalBuilder,
+ new UpdatedFlowBuilder(upd).setFlags(f),
+ ver);
+ canModifyFlowTest(false,
+ new OriginalFlowBuilder(org).setFlags(f),
+ updatedBuilder, ver);
+ }
+
+ // Set different cookie.
+ FlowCookie[] cookies = {
+ null,
+ defCookie,
+ new FlowCookie(BigInteger.valueOf(0x123456L)),
+ };
+ for (FlowCookie c: cookies) {
+ canModifyFlowTest(false, originalBuilder,
+ new UpdatedFlowBuilder(upd).setCookie(c),
+ ver);
+ canModifyFlowTest(false,
+ new OriginalFlowBuilder(org).setCookie(c),
+ updatedBuilder, ver);
+ }
+
+ // Cookie mask test.
+ // Cookie mask is used by OF13 non-strict MODIFY command.
+ updatedBuilder.setCookie(cookie1);
+ for (Boolean strict: bools) {
+ updatedBuilder.setCookieMask(null).setStrict(strict);
+ canModifyFlowTest(false, originalBuilder, updatedBuilder, ver);
+
+ updatedBuilder.setCookieMask(defCookie);
+ canModifyFlowTest(false, originalBuilder, updatedBuilder, ver);
+
+ updatedBuilder.setCookieMask(cookieMask);
+ boolean expected = (of13.equals(ver) &&
+ !Boolean.TRUE.equals(strict));
+ canModifyFlowTest(expected, originalBuilder, updatedBuilder,
+ ver);
+ }
+ }
+ }
+
+ /**
+ * Test method for
+ * {@link FlowCreatorUtil#equalsFlowModFlags(FlowModFlags, FlowModFlags)}.
+ */
+ @Test
+ public void testEqualsFlowModFlags() {
+ FlowModFlags[] defaults = {
+ null,
+ FlowModFlags.getDefaultInstance("sENDFLOWREM"),
+ new FlowModFlags(false, false, false, false, true),
+ new FlowModFlags(false, null, false, null, Boolean.TRUE),
+ };
+ FlowModFlags all = new FlowModFlags(true, true, true, true, true);
+ FlowModFlags none = new FlowModFlags(null, null, null, null, null);
+
+ for (FlowModFlags f: defaults) {
+ assertTrue(FlowCreatorUtil.
+ equalsFlowModFlags(f, (FlowModFlags)null));
+ assertTrue(FlowCreatorUtil.
+ equalsFlowModFlags((FlowModFlags)null, f));
+ assertFalse(FlowCreatorUtil.
+ equalsFlowModFlags((FlowModFlags)null, all));
+ assertFalse(FlowCreatorUtil.
+ equalsFlowModFlags(all, (FlowModFlags)null));
+ assertFalse(FlowCreatorUtil.
+ equalsFlowModFlags((FlowModFlags)null, none));
+ assertFalse(FlowCreatorUtil.
+ equalsFlowModFlags(none, (FlowModFlags)null));
+ }
+
+ String[] bitNames = {
+ "cHECKOVERLAP",
+ "nOBYTCOUNTS",
+ "nOPKTCOUNTS",
+ "rESETCOUNTS",
+ "sENDFLOWREM"
+ };
+ int bit = 0;
+ for (String name: bitNames) {
+ FlowModFlags flags = FlowModFlags.getDefaultInstance(name);
+ assertFalse(FlowCreatorUtil.equalsFlowModFlags(flags, all));
+ assertFalse(FlowCreatorUtil.equalsFlowModFlags(all, flags));
+ assertFalse(FlowCreatorUtil.equalsFlowModFlags(flags, none));
+ assertFalse(FlowCreatorUtil.equalsFlowModFlags(none, flags));
+
+ for (String nm: bitNames) {
+ FlowModFlags f = FlowModFlags.getDefaultInstance(nm);
+ boolean expected = nm.equals(name);
+ assertEquals(expected,
+ FlowCreatorUtil.equalsFlowModFlags(flags, f));
+ assertEquals(expected,
+ FlowCreatorUtil.equalsFlowModFlags(f, flags));
+ }
+
+ boolean overlap = (bit == 0);
+ boolean noByte = (bit == 1);
+ boolean noPacket = (bit == 2);
+ boolean reset = (bit == 3);
+ boolean flowRem = (bit == 4);
+ FlowModFlags f =
+ new FlowModFlags(overlap, noByte, noPacket, reset, flowRem);
+ assertTrue(FlowCreatorUtil.equalsFlowModFlags(flags, f));
+ assertTrue(FlowCreatorUtil.equalsFlowModFlags(f, flags));
+ assertTrue(FlowCreatorUtil.
+ equalsFlowModFlags(f, new FlowModFlags(f)));
+
+ f = new FlowModFlags(!overlap, noByte, noPacket, reset, flowRem);
+ assertFalse(FlowCreatorUtil.equalsFlowModFlags(flags, f));
+ f = new FlowModFlags(overlap, !noByte, noPacket, reset, flowRem);
+ assertFalse(FlowCreatorUtil.equalsFlowModFlags(flags, f));
+ f = new FlowModFlags(overlap, noByte, !noPacket, reset, flowRem);
+ assertFalse(FlowCreatorUtil.equalsFlowModFlags(flags, f));
+ f = new FlowModFlags(overlap, noByte, noPacket, !reset, flowRem);
+ assertFalse(FlowCreatorUtil.equalsFlowModFlags(flags, f));
+ f = new FlowModFlags(overlap, noByte, noPacket, reset, !flowRem);
+ assertFalse(FlowCreatorUtil.equalsFlowModFlags(flags, f));
+
+ bit++;
+ }
+ }
+
+ /**
+ * Test method for
+ * {@link FlowCreatorUtil#equalsWithDefault(Object, Object, Object)}.
+ */
+ @Test
+ public void testEqualsWithDefault() {
+ // Boolean
+ for (Boolean def: new Boolean[]{Boolean.TRUE, Boolean.FALSE}) {
+ assertTrue(FlowCreatorUtil.equalsWithDefault(null, null, def));
+ assertTrue(FlowCreatorUtil.equalsWithDefault(def, null, def));
+ assertTrue(FlowCreatorUtil.equalsWithDefault(null, def, def));
+
+ Boolean inv = Boolean.valueOf(!def.booleanValue());
+ assertFalse(FlowCreatorUtil.equalsWithDefault(null, inv, def));
+ assertFalse(FlowCreatorUtil.equalsWithDefault(inv, null, def));
+ }
+
+ // Integer
+ Integer[] integers = {
+ Integer.valueOf(-100),
+ Integer.valueOf(0),
+ Integer.valueOf(100),
+ };
+ for (Integer def: integers) {
+ Integer same = new Integer(def.intValue());
+ assertTrue(FlowCreatorUtil.equalsWithDefault(null, null, def));
+ assertTrue(FlowCreatorUtil.equalsWithDefault(same, null, def));
+ assertTrue(FlowCreatorUtil.equalsWithDefault(null, same, def));
+
+ Integer diff = new Integer(def.intValue() +1);
+ assertFalse(FlowCreatorUtil.equalsWithDefault(null, diff, def));
+ assertFalse(FlowCreatorUtil.equalsWithDefault(diff, null, def));
+ }
+
+ // String
+ String[] strings = {
+ "",
+ "test string 1",
+ "test string 2",
+ };
+ for (String def: strings) {
+ String same = new String(def);
+ assertTrue(FlowCreatorUtil.equalsWithDefault(null, null, def));
+ assertTrue(FlowCreatorUtil.equalsWithDefault(same, null, def));
+ assertTrue(FlowCreatorUtil.equalsWithDefault(null, same, def));
+
+ String diff = def + "-1";
+ assertFalse(FlowCreatorUtil.equalsWithDefault(null, diff, def));
+ assertFalse(FlowCreatorUtil.equalsWithDefault(diff, null, def));
+ }
+ }
+
private void assertMatch(Match match) {
assertTrue(match.getType().getClass().isInstance(OxmMatchType.class));
}
assertTrue(matchV10.getTpDst().intValue() == 0);
}
+ /**
+ * Verify that {@link FlowCreatorUtil#canModifyFlow(OriginalFlow, UpdatedFlow, Short)}
+ * returns expected value.
+ *
+ * @param expected
+ * An expected return value.
+ * @param org
+ * A original flow builder that contains original flow to be tested.
+ * @param upd
+ * An updated flow builder that contains updated flow to be tested.
+ * @param version
+ * OpenFlow protocol version.
+ */
+ private void canModifyFlowTest(boolean expected, OriginalFlowBuilder org,
+ UpdatedFlowBuilder upd, Short version) {
+ boolean result = FlowCreatorUtil.
+ canModifyFlow(org.build(), upd.build(), version);
+ assertEquals(expected, result);
+ }
+
+ /**
+ * Create a flow match that specifies ethernet type.
+ *
+ * @param etherType An ethernet type value.
+ * @return A flow match that specifies the given ethernet type.
+ */
+ private org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.flow.Match createMatch(long etherType) {
+ EthernetTypeBuilder ethType = new EthernetTypeBuilder().
+ setType(new EtherType(etherType));
+ EthernetMatchBuilder ether = new EthernetMatchBuilder().
+ setEthernetType(ethType.build());
+ return new MatchBuilder().setEthernetMatch(ether.build()).build();
+ }
}