Add REST APIs for flow filter. 01/9701/2
authorShigeru Yasuda <s-yasuda@da.jp.nec.com>
Tue, 5 Aug 2014 15:35:41 +0000 (00:35 +0900)
committerShigeru Yasuda <s-yasuda@da.jp.nec.com>
Thu, 7 Aug 2014 00:59:48 +0000 (09:59 +0900)
Note that flow filter feature is not yet implemented.
Currently all flow filter REST APIs do nothing.

Change-Id: Ic6a3e65355e8162b8e19df6320008cecab209d0f
Signed-off-by: Shigeru Yasuda <s-yasuda@da.jp.nec.com>
40 files changed:
manager/api/pom.xml
manager/api/src/main/java/org/opendaylight/vtn/manager/ErrorVNodePath.java [new file with mode: 0644]
manager/api/src/main/java/org/opendaylight/vtn/manager/VBridgeIfPath.java
manager/api/src/main/java/org/opendaylight/vtn/manager/VBridgePath.java
manager/api/src/main/java/org/opendaylight/vtn/manager/VInterfacePath.java
manager/api/src/main/java/org/opendaylight/vtn/manager/VInterfacePathAdapter.java [new file with mode: 0644]
manager/api/src/main/java/org/opendaylight/vtn/manager/VNodeLocation.java
manager/api/src/main/java/org/opendaylight/vtn/manager/VNodePath.java
manager/api/src/main/java/org/opendaylight/vtn/manager/VNodePathAdapter.java
manager/api/src/main/java/org/opendaylight/vtn/manager/VTenantPath.java
manager/api/src/main/java/org/opendaylight/vtn/manager/VTerminalIfPath.java
manager/api/src/main/java/org/opendaylight/vtn/manager/VTerminalPath.java
manager/api/src/main/java/org/opendaylight/vtn/manager/flow/cond/package-info.java
manager/api/src/main/java/org/opendaylight/vtn/manager/flow/filter/DropFilter.java [new file with mode: 0644]
manager/api/src/main/java/org/opendaylight/vtn/manager/flow/filter/FilterType.java [new file with mode: 0644]
manager/api/src/main/java/org/opendaylight/vtn/manager/flow/filter/FlowFilter.java [new file with mode: 0644]
manager/api/src/main/java/org/opendaylight/vtn/manager/flow/filter/PassFilter.java [new file with mode: 0644]
manager/api/src/main/java/org/opendaylight/vtn/manager/flow/filter/RedirectFilter.java [new file with mode: 0644]
manager/api/src/main/java/org/opendaylight/vtn/manager/flow/filter/package-info.java [new file with mode: 0644]
manager/api/src/test/java/org/opendaylight/vtn/manager/TestBase.java
manager/api/src/test/java/org/opendaylight/vtn/manager/VBridgeIfPathTest.java
manager/api/src/test/java/org/opendaylight/vtn/manager/VBridgePathTest.java
manager/api/src/test/java/org/opendaylight/vtn/manager/VInterfacePathAdapterTest.java [new file with mode: 0644]
manager/api/src/test/java/org/opendaylight/vtn/manager/VNodePathAdapterTest.java [new file with mode: 0644]
manager/api/src/test/java/org/opendaylight/vtn/manager/VTenantPathTest.java
manager/api/src/test/java/org/opendaylight/vtn/manager/VTerminalIfPathTest.java
manager/api/src/test/java/org/opendaylight/vtn/manager/VTerminalPathTest.java
manager/api/src/test/java/org/opendaylight/vtn/manager/flow/filter/DropFilterTest.java [new file with mode: 0644]
manager/api/src/test/java/org/opendaylight/vtn/manager/flow/filter/FlowFilterTest.java [new file with mode: 0644]
manager/api/src/test/java/org/opendaylight/vtn/manager/flow/filter/PassFilterTest.java [new file with mode: 0644]
manager/api/src/test/java/org/opendaylight/vtn/manager/flow/filter/RedirectFilterTest.java [new file with mode: 0644]
manager/implementation/pom.xml
manager/northbound/pom.xml
manager/northbound/src/main/java/org/opendaylight/vtn/manager/northbound/FlowFilterList.java [new file with mode: 0644]
manager/northbound/src/main/java/org/opendaylight/vtn/manager/northbound/VBridgeFlowFilterNorthbound.java [new file with mode: 0644]
manager/northbound/src/main/java/org/opendaylight/vtn/manager/northbound/VBridgeIfFlowFilterNorthbound.java [new file with mode: 0644]
manager/northbound/src/main/java/org/opendaylight/vtn/manager/northbound/VTenantFlowFilterNorthbound.java [new file with mode: 0644]
manager/northbound/src/main/java/org/opendaylight/vtn/manager/northbound/VTerminalIfFlowFilterNorthbound.java [new file with mode: 0644]
manager/northbound/src/test/java/org/opendaylight/vtn/manager/northbound/FlowFilterListTest.java [new file with mode: 0644]
manager/northbound/src/test/java/org/opendaylight/vtn/manager/northbound/TestBase.java

index 4744fe2c4184ee1cb84157801cccd8986a2fcd72..3970fa8403d6b23ab8e655400fcdaac4bb67d189 100644 (file)
@@ -55,7 +55,8 @@
               org.opendaylight.vtn.manager,
               org.opendaylight.vtn.manager.flow,
               org.opendaylight.vtn.manager.flow.action,
-              org.opendaylight.vtn.manager.flow.cond
+              org.opendaylight.vtn.manager.flow.cond,
+              org.opendaylight.vtn.manager.flow.filter
             </Export-Package>
             <Import-Package>
               org.opendaylight.controller.hosttracker.hostAware,
diff --git a/manager/api/src/main/java/org/opendaylight/vtn/manager/ErrorVNodePath.java b/manager/api/src/main/java/org/opendaylight/vtn/manager/ErrorVNodePath.java
new file mode 100644 (file)
index 0000000..755dd63
--- /dev/null
@@ -0,0 +1,124 @@
+/*
+ * Copyright (c) 2014 NEC Corporation
+ * 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.vtn.manager;
+
+import org.opendaylight.controller.sal.core.UpdateType;
+
+/**
+ * {@code ErrorVNodePath} class is used to indicate that an invalid
+ * virtual node path is specified.
+ *
+ * <p>
+ *   This class is provided only for internal use.
+ *   Java application must not use this class.
+ * </p>
+ *
+ * @since  Helium
+ */
+public final class ErrorVNodePath extends VNodePath
+    implements VInterfacePath {
+    /**
+     * Version number for serialization.
+     */
+    private static final long serialVersionUID = 2748579569569933516L;
+
+    /**
+     * A string which represents type type of this node path.
+     */
+    private static final String  NODETYPE_ERROR = "*** ERROR ***";
+
+    /**
+     * Construct a new instance.
+     *
+     * @param msg  An error message.
+     */
+    ErrorVNodePath(String msg) {
+        super(msg, null);
+    }
+
+    /**
+     * Return an error message configured in this instance.
+     *
+     * @return  An error message.
+     */
+    public String getError() {
+        return getTenantName();
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public String getNodeType() {
+        return NODETYPE_ERROR;
+    }
+
+    /**
+     * This method should never be called.
+     *
+     * @return  Never returns.
+     * @throws IllegalStateException  Always thrown.
+     */
+    @Override
+    public VNodeLocation toVNodeLocation() {
+        throw unexpected();
+    }
+
+    /**
+     * Create an {@link IllegalStateException} that indicates unexpected method
+     * is called.
+     *
+     * @return  An exception.
+     */
+    private IllegalStateException unexpected() {
+        return new IllegalStateException("Should never be called.");
+    }
+
+    // VInterfacePath
+
+    /**
+     * This method should never be called.
+     *
+     * @return  Never returns.
+     * @throws IllegalStateException  Always thrown.
+     */
+    @Override
+    public String getInterfaceName() {
+        throw unexpected();
+    }
+
+    /**
+     * This method should never be called.
+     *
+     * @param listener  Unused.
+     * @param viface    Unused.
+     * @param type      Unused.
+     * @throws IllegalStateException  Always thrown.
+     */
+    @Override
+    public void vInterfaceChanged(IVTNManagerAware listener, VInterface viface,
+                                  UpdateType type) {
+        throw unexpected();
+    }
+
+    /**
+     * This method should never be called.
+     *
+     * @param listener  Unused.
+     * @param pmap      Unused.
+     * @param type      Unused.
+     * @throws IllegalStateException  Always thrown.
+     */
+    @Override
+    public void portMapChanged(IVTNManagerAware listener, PortMap pmap,
+                               UpdateType type) {
+        throw unexpected();
+    }
+}
index ee45943de5a1843357c225c10ec21099f927b36f..3877d22b3bb1ce64e6d0bcb4b0db5ad5a945357b 100644 (file)
@@ -33,7 +33,7 @@ public class VBridgeIfPath extends VBridgePath implements VInterfacePath {
     /**
      * Version number for serialization.
      */
-    private static final long serialVersionUID = 3530008424797872881L;
+    private static final long serialVersionUID = -4792520332881436474L;
 
     /**
      * A string which represents that the node type is vBridge interface.
@@ -155,6 +155,16 @@ public class VBridgeIfPath extends VBridgePath implements VInterfacePath {
         return components;
     }
 
+    /**
+     * Convert this instance into a {@link VNodeLocation} instance.
+     *
+     * @return  A {@link VNodeLocation} instance.
+     */
+    @Override
+    public VNodeLocation toVNodeLocation() {
+        return new VNodeLocation(this);
+    }
+
     /**
      * Return the hash code of this object.
      *
index f83de99b332fc30130a0e5d0a95fbc09a9bd33e5..ed78de78eba2c1ab9071b8277d2ff48261f16906 100644 (file)
@@ -26,7 +26,7 @@ public class VBridgePath extends VNodePath {
     /**
      * Version number for serialization.
      */
-    private static final long serialVersionUID = -2598911916422555753L;
+    private static final long serialVersionUID = -2918772111159548106L;
 
     /**
      * A string which represents that the node type is vBridge.
@@ -100,4 +100,14 @@ public class VBridgePath extends VNodePath {
     public String getNodeType() {
         return NODETYPE_VBRIDGE;
     }
+
+    /**
+     * Convert this instance into a {@link VNodeLocation} instance.
+     *
+     * @return  A {@link VNodeLocation} instance.
+     */
+    @Override
+    public VNodeLocation toVNodeLocation() {
+        return new VNodeLocation(this);
+    }
 }
index baa618377d25ff345f4c28be9accd41407983a69..6f62df80e4c5e4ac2d750b09cf9b823b90322a3b 100644 (file)
@@ -54,6 +54,13 @@ public interface VInterfacePath {
      */
     String getNodeType();
 
+    /**
+     * Convert this instance into a {@link VNodeLocation} instance.
+     *
+     * @return  A {@link VNodeLocation} instance.
+     */
+    VNodeLocation toVNodeLocation();
+
     /**
      * Call method that listens the change of the virtual interface.
      *
diff --git a/manager/api/src/main/java/org/opendaylight/vtn/manager/VInterfacePathAdapter.java b/manager/api/src/main/java/org/opendaylight/vtn/manager/VInterfacePathAdapter.java
new file mode 100644 (file)
index 0000000..a305769
--- /dev/null
@@ -0,0 +1,76 @@
+/*
+ * Copyright (c) 2014 NEC Corporation
+ * 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.vtn.manager;
+
+import javax.xml.bind.annotation.adapters.XmlAdapter;
+
+/**
+ * This class is used to establish JAXB mapping between {@link VInterfacePath}
+ * and {@link VNodeLocation}.
+ *
+ * @since  Helium
+ */
+public class VInterfacePathAdapter
+    extends XmlAdapter<VNodeLocation, VInterfacePath> {
+    /**
+     * Construct a new instance.
+     */
+    public VInterfacePathAdapter() {
+    }
+
+    /**
+     * Convert an instance of {@link VNodeLocation} into
+     * {@link VInterfacePath}.
+     *
+     * @param loc  A {@link VNodeLocation} instance to be converted.
+     * @return  A {@link VInterfacePath} instance.
+     *          {@code null} is returned if the specified {@link VNodeLocation}
+     *          does not point any virtual interface.
+     */
+    @Override
+    public VInterfacePath unmarshal(VNodeLocation loc) {
+        if (loc == null) {
+            return null;
+        }
+
+        String tname = loc.getTenantName();
+        String ifname = loc.getInterfaceName();
+        if (ifname == null) {
+            return new ErrorVNodePath("Interface name is not specified: " +
+                                      loc);
+        }
+
+        String bname = loc.getBridgeName();
+        if (bname != null) {
+            return new VBridgeIfPath(tname, bname, ifname);
+        }
+
+        String tmname = loc.getTerminalName();
+        if (tmname != null) {
+            return new VTerminalIfPath(tname, tmname, ifname);
+        }
+
+        return new ErrorVNodePath("Unexpected interface location: " + loc);
+    }
+
+    /**
+     * Convert an instance of {@link VInterfacePath} into an instance of
+     * {@link VNodeLocation}.
+     *
+     * @param path  A {@link VInterfacePath} instance to be converted.
+     * @return  A {@link VNodeLocation} instance.
+     *          {@code null} is returned a value passed to {@code path} is
+     *          {@code null} or an instance of unexpected class.
+     */
+    @Override
+    public VNodeLocation marshal(VInterfacePath path) {
+        return (path == null) ? null : path.toVNodeLocation();
+    }
+}
index e6a0d878c6842d02f728429c2e8a194e7019e4ad..7143c41c643e7339e3f046cce4e6861fa2f15c45 100644 (file)
@@ -10,6 +10,7 @@
 package org.opendaylight.vtn.manager;
 
 import java.io.Serializable;
+import java.util.Objects;
 
 import javax.xml.bind.annotation.XmlAccessType;
 import javax.xml.bind.annotation.XmlAccessorType;
@@ -45,7 +46,7 @@ public final class VNodeLocation implements Serializable {
     /**
      * Version number for serialization.
      */
-    private static final long serialVersionUID = 8688432687151988512L;
+    private static final long serialVersionUID = 1379126227527453095L;
 
     /**
      * The name of the VTN.
@@ -243,4 +244,38 @@ public final class VNodeLocation implements Serializable {
     public String getInterfaceName() {
         return interfaceName;
     }
+
+    /**
+     * Determine whether the given object is identical to this object.
+     *
+     * @param o  An object to be compared.
+     * @return   {@code true} if identical. Otherwise {@code false}.
+     */
+    @Override
+    public boolean equals(Object o) {
+        if (o == this) {
+            return true;
+        }
+        if (o == null || !getClass().equals(o.getClass())) {
+            return false;
+        }
+
+        VNodeLocation loc = (VNodeLocation)o;
+        return (Objects.equals(tenantName, loc.tenantName) &&
+                Objects.equals(bridgeName, loc.bridgeName) &&
+                Objects.equals(routerName, loc.routerName) &&
+                Objects.equals(terminalName, loc.terminalName) &&
+                Objects.equals(interfaceName, loc.interfaceName));
+    }
+
+    /**
+     * Return the hash code of this object.
+     *
+     * @return  The hash code.
+     */
+    @Override
+    public int hashCode() {
+        return Objects.hash(tenantName, bridgeName, routerName, terminalName,
+                            interfaceName);
+    }
 }
index 9f1763674c71019a6686d4f60b62e74079a4d8ad..ba388e478451cc5278d67d731a030296d1fab208 100644 (file)
@@ -29,7 +29,7 @@ public abstract class VNodePath extends VTenantPath {
     /**
      * Version number for serialization.
      */
-    private static final long serialVersionUID = -3691447682025580103L;
+    private static final long serialVersionUID = 569227328812607675L;
 
     /**
      * The name of the virtual node inside the VTN.
@@ -49,7 +49,7 @@ public abstract class VNodePath extends VTenantPath {
      * @param tenantName  The name of the VTN.
      * @param name        The name of the virtual node inside the VTN.
      */
-    protected VNodePath(String tenantName, String name) {
+    VNodePath(String tenantName, String name) {
         super(tenantName);
         tenantNodeName = name;
     }
@@ -70,7 +70,7 @@ public abstract class VNodePath extends VTenantPath {
      * @param name        The name of the virtual node.
      * @throws NullPointerException  {@code tenantPath} is {@code null}.
      */
-    protected VNodePath(VTenantPath tenantPath, String name) {
+    VNodePath(VTenantPath tenantPath, String name) {
         this(tenantPath.getTenantName(), name);
     }
 
@@ -83,6 +83,13 @@ public abstract class VNodePath extends VTenantPath {
         return tenantNodeName;
     }
 
+    /**
+     * Convert this instance into a {@link VNodeLocation} instance.
+     *
+     * @return  A {@link VNodeLocation} instance.
+     */
+    public abstract VNodeLocation toVNodeLocation();
+
     /**
      * {@inheritDoc}
      */
index 259765a42be553ddd3e5aeabdfd969169fac476d..d7665db2ad280f9fc533e02a1eaaad1dce638046 100644 (file)
@@ -12,7 +12,7 @@ package org.opendaylight.vtn.manager;
 import javax.xml.bind.annotation.adapters.XmlAdapter;
 
 /**
- * This class is used to establish JAXB mapping between {@code VNodePath} and
+ * This class is used to establish JAXB mapping between {@link VNodePath} and
  * {@link VNodeLocation}.
  *
  * @since  Helium
@@ -39,10 +39,6 @@ public class VNodePathAdapter extends XmlAdapter<VNodeLocation, VNodePath> {
         }
 
         String tname = loc.getTenantName();
-        if (tname == null) {
-            return null;
-        }
-
         String ifname = loc.getInterfaceName();
         String bname = loc.getBridgeName();
         if (bname != null) {
@@ -58,32 +54,20 @@ public class VNodePathAdapter extends XmlAdapter<VNodeLocation, VNodePath> {
                 : new VTerminalIfPath(tname, tmname, ifname);
         }
 
-        return null;
+        return new ErrorVNodePath("Unexpected node location: " + loc);
     }
 
     /**
-     * Convert an instance of {@link VNodePath} into {@link VNodeLocation}.
+     * Convert an instance of {@link VNodePath} into an instance of
+     * {@link VNodeLocation}.
      *
      * @param path  A {@link VNodePath} instance to be converted.
      * @return  A {@link VNodeLocation} instance.
-     *          {@code null} is returned a value passed to {@code path} is
-     *          {@code null} or an instance of unexpected class.
+     *          {@code null} is returned if {@code null} is passed to
+     *          {@code path}.
      */
     @Override
     public VNodeLocation marshal(VNodePath path) {
-        if (path instanceof VBridgeIfPath) {
-            return new VNodeLocation((VBridgeIfPath)path);
-        }
-        if (path instanceof VTerminalIfPath) {
-            return new VNodeLocation((VTerminalIfPath)path);
-        }
-        if (path instanceof VBridgePath) {
-            return new VNodeLocation((VBridgePath)path);
-        }
-        if (path instanceof VTerminalPath) {
-            return new VNodeLocation((VTerminalPath)path);
-        }
-
-        return null;
+        return (path == null) ? null : path.toVNodeLocation();
     }
 }
index 1456dcb243856731c7aa704f686623d2673b5bab..4461440cd429721fbfd604a91136487eb08b0f76 100644 (file)
@@ -25,11 +25,12 @@ import java.util.List;
  *
  * @see  <a href="package-summary.html#VTN">VTN</a>
  */
-public class VTenantPath implements Serializable, Comparable<VTenantPath> {
+public class VTenantPath
+    implements Serializable, Comparable<VTenantPath>, Cloneable {
     /**
      * Version number for serialization.
      */
-    private static final long serialVersionUID = 8776060135426029534L;
+    private static final long serialVersionUID = -3796118278733925106L;
 
     /**
      * A string which represents that the node type is VTN.
@@ -39,7 +40,7 @@ public class VTenantPath implements Serializable, Comparable<VTenantPath> {
     /**
      * The name of the {@linkplain <a href="package-summary.html#VTN">VTN</a>}.
      */
-    private final String  tenantName;
+    private String  tenantName;
 
     /**
      * Construct a new object which represents the position of the
@@ -99,6 +100,26 @@ public class VTenantPath implements Serializable, Comparable<VTenantPath> {
         return NODETYPE_VTN;
     }
 
+    /**
+     * Create a shallow copy of this instance, and replace the tenant name
+     * with the specified name.
+     *
+     * @param tenantName  The name of the VTN to be configured into a new
+     *                    instance.
+     * @return  A constructed instance.
+     * @since   Helium
+     */
+    public final VTenantPath replaceTenantName(String tenantName) {
+        try {
+            VTenantPath path = (VTenantPath)super.clone();
+            path.tenantName = tenantName;
+            return path;
+        } catch (CloneNotSupportedException e) {
+            // This should never happen.
+            throw new IllegalStateException("clone() failed", e);
+        }
+    }
+
     /**
      * Return a {@link StringBuilder} object which contains a string
      * representation of this object.
@@ -276,4 +297,20 @@ public class VTenantPath implements Serializable, Comparable<VTenantPath> {
 
         return ret;
     }
+
+    // Cloneable
+
+    /**
+     * Create a shallow copy of this instance.
+     *
+     * @return  A shallow copy of this instance.
+     */
+    public VTenantPath clone() {
+        try {
+            return (VTenantPath)super.clone();
+        } catch (CloneNotSupportedException e) {
+            // This should never happen.
+            throw new IllegalStateException("clone() failed", e);
+        }
+    }
 }
index 96acccdb732f437576b38e9f5f87ead5351fd9ac..49530569d8a943f3d7025c31934809e601c1e749 100644 (file)
@@ -34,7 +34,7 @@ public class VTerminalIfPath extends VTerminalPath implements VInterfacePath {
     /**
      * Version number for serialization.
      */
-    private static final long serialVersionUID = 627513994821323718L;
+    private static final long serialVersionUID = -7165865505431233649L;
 
     /**
      * A string which represents that the node type is vTerminal interface.
@@ -156,6 +156,16 @@ public class VTerminalIfPath extends VTerminalPath implements VInterfacePath {
         return components;
     }
 
+    /**
+     * Convert this instance into a {@link VNodeLocation} instance.
+     *
+     * @return  A {@link VNodeLocation} instance.
+     */
+    @Override
+    public VNodeLocation toVNodeLocation() {
+        return new VNodeLocation(this);
+    }
+
     /**
      * Return the hash code of this object.
      *
index 7b7fd2535351ff12b4df447cc87c70a8f7649fca..6237727f96e8cc8c0e86a8d3af48b7af7ab26d8c 100644 (file)
@@ -28,7 +28,7 @@ public class VTerminalPath extends VNodePath {
     /**
      * Version number for serialization.
      */
-    private static final long serialVersionUID = -8111755848677458802L;
+    private static final long serialVersionUID = -4095702145474291746L;
 
     /**
      * A string which represents that the node type is vTerminal.
@@ -99,4 +99,14 @@ public class VTerminalPath extends VNodePath {
     public String getNodeType() {
         return NODETYPE_VTERMINAL;
     }
+
+    /**
+     * Convert this instance into a {@link VNodeLocation} instance.
+     *
+     * @return  A {@link VNodeLocation} instance.
+     */
+    @Override
+    public VNodeLocation toVNodeLocation() {
+        return new VNodeLocation(this);
+    }
 }
index 15a5f7bcd22282f3e2988ab1719a361350144641..cd1099a6c963c20bf41326e70c7ae8299d6667e7 100644 (file)
  * This package provides public APIs for flow condition, which tests whether
  * the packet satisfies the specified conditions.
  *
+ * <p>
+ *   A flow condition is a named list of flow match conditions, and it is
+ *   used to select packets. Each flow match condition must have a match index,
+ *   which is an unique index in a flow condition. When a flow condition
+ *   tests a packet, flow match conditions in a flow condition are evaluated
+ *   in ascending order of match indices. A packet is selected if at least one
+ *   flow match condition matches the packet.
+ * </p>
+ * <p>
+ *   Flow conditions configured in the container is shared with all VTNs
+ *   in the container.
+ * </p>
+ *
  * @since  Helium
  */
 package org.opendaylight.vtn.manager.flow.cond;
diff --git a/manager/api/src/main/java/org/opendaylight/vtn/manager/flow/filter/DropFilter.java b/manager/api/src/main/java/org/opendaylight/vtn/manager/flow/filter/DropFilter.java
new file mode 100644 (file)
index 0000000..e7c5674
--- /dev/null
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2014 NEC Corporation
+ * 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.vtn.manager.flow.filter;
+
+import javax.xml.bind.annotation.XmlAccessType;
+import javax.xml.bind.annotation.XmlAccessorType;
+import javax.xml.bind.annotation.XmlRootElement;
+
+import com.fasterxml.jackson.databind.annotation.JsonSerialize;
+
+/**
+ * {@code DropFilter} class describes the DROP flow filter which discards
+ * the specified packets.
+ *
+ * <h4>Example JSON</h4>
+ * <pre class="prettyprint lang-json">{}</pre>
+ *
+ * @since  Helium
+ */
+@JsonSerialize(include = JsonSerialize.Inclusion.NON_NULL)
+@XmlRootElement(name = "drop")
+@XmlAccessorType(XmlAccessType.NONE)
+public final class DropFilter extends FilterType {
+    /**
+     * Version number for serialization.
+     */
+    private static final long serialVersionUID = 7396754685166202723L;
+
+    /**
+     * Construct a new instance.
+     */
+    public DropFilter() {
+    }
+}
diff --git a/manager/api/src/main/java/org/opendaylight/vtn/manager/flow/filter/FilterType.java b/manager/api/src/main/java/org/opendaylight/vtn/manager/flow/filter/FilterType.java
new file mode 100644 (file)
index 0000000..382cf71
--- /dev/null
@@ -0,0 +1,80 @@
+/*
+ * Copyright (c) 2014 NEC Corporation
+ * 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.vtn.manager.flow.filter;
+
+import java.io.Serializable;
+
+import javax.xml.bind.annotation.XmlAccessType;
+import javax.xml.bind.annotation.XmlAccessorType;
+import javax.xml.bind.annotation.XmlRootElement;
+import javax.xml.bind.annotation.XmlSeeAlso;
+
+import com.fasterxml.jackson.databind.annotation.JsonSerialize;
+
+/**
+ * {@code FilterType} is an abstract class that describes the type of
+ * flow filter.
+ *
+ * @since  Helium
+ */
+@JsonSerialize(include = JsonSerialize.Inclusion.NON_NULL)
+@XmlRootElement(name = "filtertype")
+@XmlAccessorType(XmlAccessType.NONE)
+@XmlSeeAlso({
+    PassFilter.class,
+    DropFilter.class,
+    RedirectFilter.class
+})
+public abstract class FilterType implements Serializable {
+    /**
+     * Version number for serialization.
+     */
+    private static final long serialVersionUID = -4140830853020675751L;
+
+    /**
+     * Construct a new instance.
+     */
+    FilterType() {
+    }
+
+    /**
+     * Determine whether the given object is identical to this object.
+     *
+     * @param o  An object to be compared.
+     * @return   {@code true} if identical. Otherwise {@code false}.
+     */
+    @Override
+    public boolean equals(Object o) {
+        if (o == this) {
+            return true;
+        }
+        return (o != null && getClass().equals(o.getClass()));
+    }
+
+    /**
+     * Return the hash code of this object.
+     *
+     * @return  The hash code.
+     */
+    @Override
+    public int hashCode() {
+        return getClass().getName().hashCode();
+    }
+
+    /**
+     * Return a string representation of this object.
+     *
+     * @return  A string representation of this object.
+     */
+    @Override
+    public String toString() {
+        return getClass().getSimpleName();
+    }
+}
diff --git a/manager/api/src/main/java/org/opendaylight/vtn/manager/flow/filter/FlowFilter.java b/manager/api/src/main/java/org/opendaylight/vtn/manager/flow/filter/FlowFilter.java
new file mode 100644 (file)
index 0000000..7de97fa
--- /dev/null
@@ -0,0 +1,503 @@
+/*
+ * Copyright (c) 2014 NEC Corporation
+ * 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.vtn.manager.flow.filter;
+
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+
+import javax.xml.bind.annotation.XmlAccessType;
+import javax.xml.bind.annotation.XmlAccessorType;
+import javax.xml.bind.annotation.XmlAttribute;
+import javax.xml.bind.annotation.XmlElement;
+import javax.xml.bind.annotation.XmlElementWrapper;
+import javax.xml.bind.annotation.XmlElements;
+import javax.xml.bind.annotation.XmlRootElement;
+
+import com.fasterxml.jackson.databind.annotation.JsonSerialize;
+
+import org.opendaylight.vtn.manager.flow.action.FlowAction;
+import org.opendaylight.vtn.manager.flow.action.SetDlDstAction;
+import org.opendaylight.vtn.manager.flow.action.SetDlSrcAction;
+import org.opendaylight.vtn.manager.flow.action.SetDscpAction;
+import org.opendaylight.vtn.manager.flow.action.SetIcmpCodeAction;
+import org.opendaylight.vtn.manager.flow.action.SetIcmpTypeAction;
+import org.opendaylight.vtn.manager.flow.action.SetInet4DstAction;
+import org.opendaylight.vtn.manager.flow.action.SetInet4SrcAction;
+import org.opendaylight.vtn.manager.flow.action.SetTpDstAction;
+import org.opendaylight.vtn.manager.flow.action.SetTpSrcAction;
+import org.opendaylight.vtn.manager.flow.action.SetVlanPcpAction;
+
+/**
+ * {@code FlowFilter} class describes configuration information about
+ * flow filter.
+ *
+ * <p>
+ *   An instance of this class represents a rule of a packet filter applied
+ *   to packets forwarded in the VTN. If a flow condition configured in
+ *   a flow filter matches a packet, actions configured in that flow filter
+ *   are applied to the packet.
+ * </p>
+ *
+ * <h4>Example JSON</h4>
+ * <pre class="prettyprint lang-json">
+ * {
+ * &nbsp;&nbsp;"index": 10,
+ * &nbsp;&nbsp;"condition": "flowcond_1",
+ * &nbsp;&nbsp;"filterType": {
+ * &nbsp;&nbsp;&nbsp;&nbsp;"redirect": {
+ * &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"destination": {
+ * &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"bridge": "vbridge_1",
+ * &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"interface": "if_2"
+ * &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;},
+ * &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"output": false
+ * &nbsp;&nbsp;&nbsp;&nbsp;}
+ * &nbsp;&nbsp;},
+ * &nbsp;&nbsp;"actions": [
+ * &nbsp;&nbsp;&nbsp;&nbsp;{"dlsrc": {"address": "f0:f1:f2:f3:f4:f5"}},
+ * &nbsp;&nbsp;&nbsp;&nbsp;{"vlanpcp": {"priority": 7}}
+ * &nbsp;&nbsp;]
+ * }</pre>
+ *
+ * @since  Helium
+ */
+@JsonSerialize(include = JsonSerialize.Inclusion.NON_NULL)
+@XmlRootElement(name = "flowfilter")
+@XmlAccessorType(XmlAccessType.NONE)
+public final class FlowFilter implements Serializable {
+    /**
+     * Version number for serialization.
+     */
+    private static final long serialVersionUID = -2789802133918528483L;
+
+    /**
+     * An index value assigned to this flow filter.
+     *
+     * <ul>
+     *   <li>
+     *     The range of value that can be specified is from
+     *     <strong>1</strong> to <strong>65535</strong>.
+     *   </li>
+     *   <li>
+     *     This value is used to determine order of evaluation.
+     *     Flow filters in the same list are evaluated against packet
+     *     in ascending order of indices assigned to
+     *     <strong>flowfilter</strong> elements, and only the first matched
+     *     flow filter is applied to the packet.
+     *   </li>
+     * </ul>
+     */
+    @XmlAttribute(name = "index")
+    private Integer  index;
+
+    /**
+     * The name of the flow condition that selects packets.
+     *
+     * <ul>
+     *   <li>
+     *     This flow filter will be applied to the packet if the packet is
+     *     selected by the flow condition specified by this attribute.
+     *   </li>
+     *   <li>
+     *     This flow filter is invalidated if the flow condition specified by
+     *     this attribute does not exist.
+     *   </li>
+     * </ul>
+     */
+    @XmlAttribute(required = true)
+    private String  condition;
+
+    /**
+     * The type of this flow filter.
+     *
+     * <p>
+     *   The flow filter type determines action to be applied to the packet
+     *   when it matches the condition specified by the
+     *   <strong>condition</strong> attribute.
+     *   One of the following elements can be specified.
+     * </p>
+     * <dl style="margin-left: 1em;">
+     *   <dt>pass
+     *   <dd style="margin-left: 1.5em;">
+     *     Let the matched packet through the virtual node.
+     *     The type of this element must be {@link PassFilter}.
+     *
+     *   <dt>drop
+     *   <dd style="margin-left: 1.5em;">
+     *     Discard the matched packet.
+     *     The type of this element must be {@link DropFilter}.
+     *
+     *   <dt>redirect
+     *   <dd style="margin-left: 1.5em;">
+     *     Forward the matched packet to another virtual interface in the
+     *     same VTN.
+     *     The type of this element must be {@link RedirectFilter}.
+     * </dl>
+     *
+     * <ul>
+     *   <li>
+     *     If omitted, it will be treated as if <strong>pass</strong> is
+     *     specified.
+     *   </li>
+     *   <li>
+     *     In JSON notation, this element must be wrapped by a
+     *     <strong>filterType</strong> element.
+     *   </li>
+     * </ul>
+     */
+    @XmlElements({
+        @XmlElement(name = "pass", type = PassFilter.class),
+        @XmlElement(name = "drop", type = DropFilter.class),
+        @XmlElement(name = "redirect", type = RedirectFilter.class)
+    })
+    private FilterType  filterType;
+
+    /**
+     * A list of {@link FlowAction} instances that modifies the packet
+     * when this flow filter is applied to the packet.
+     *
+     * <ul>
+     *   <li>
+     *     If omitted or an empty list is specified, this flow filter is
+     *     applied to the packet without modifying the packet.
+     *   </li>
+     *   <li>
+     *     Note that the VLAN ID of the packet cannot be specified
+     *     because it is always determined by the virtual mapping configuration
+     *     such as port mapping.
+     *   </li>
+     *   <li>
+     *     This element is ignored if a <strong>drop</strong> element is
+     *     configured in this instance.
+     *   </li>
+     * </ul>
+     * <p>
+     *   One or more of the following elements can be specified.
+     * </p>
+     * <dl style="margin-left: 1em;">
+     *   <dt>dlsrc
+     *   <dd style="margin-left: 1.5em;">
+     *     Specifies the action that sets the source MAC address in
+     *     Ethernet header.
+     *     <ul>
+     *       <li>The type of this element must be {@link SetDlSrcAction}.</li>
+     *       <li>
+     *         The source MAC address cannot be changed to the following
+     *         addresses.
+     *         <ul>
+     *           <li>Zero <code>00:00:00:00:00:00</code></li>
+     *           <li>Broadcast address <code>ff:ff:ff:ff:ff:ff</code></li>
+     *           <li>Multicast address</li>
+     *         </ul>
+     *       </li>
+     *     </ul>
+     *
+     *   <dt>dldst
+     *   <dd style="margin-left: 1.5em;">
+     *     Specifies the action that sets the destination MAC address in
+     *     Ethernet header.
+     *     <ul>
+     *       <li>The type of this element must be {@link SetDlDstAction}.</li>
+     *       <li>
+     *         The destination MAC address cannot be changed to the following
+     *         addresses.
+     *         <ul>
+     *           <li>Zero <code>00:00:00:00:00:00</code></li>
+     *           <li>Broadcast address <code>ff:ff:ff:ff:ff:ff</code></li>
+     *           <li>Multicast address</li>
+     *         </ul>
+     *       </li>
+     *     </ul>
+     *
+     *   <dt>vlanpcp
+     *   <dd style="margin-left: 1.5em;">
+     *     Specifies the action that sets the VLAN priority in Ethernet
+     *     header.
+     *     <ul>
+     *       <li>The type of this element must be {@link SetVlanPcpAction}.</li>
+     *       <li>This element does not affect packets without VLAN tag.</li>
+     *     </ul>
+     *
+     *   <dt>inet4src
+     *   <dd style="margin-left: 1.5em;">
+     *     Specifies the action that sets the source IP address in IPv4
+     *     header.
+     *     <ul>
+     *       <li>
+     *         The type of this element must be {@link SetInet4SrcAction}.
+     *       </li>
+     *       <li>This element does not affect packets without IPv4 header.</li>
+     *       <li>
+     *         <strong>This element is not yet supported.</strong>
+     *       </li>
+     *     </ul>
+     *
+     *   <dt>inet4dst
+     *   <dd style="margin-left: 1.5em;">
+     *     Specifies the action that sets the destination IP address in IPv4
+     *     header.
+     *     <ul>
+     *       <li>
+     *         The type of this element must be {@link SetInet4DstAction}.
+     *       </li>
+     *       <li>This element does not affect packets without IPv4 header.</li>
+     *       <li>
+     *         <strong>This element is not yet supported.</strong>
+     *       </li>
+     *     </ul>
+     *
+     *   <dt>dscp
+     *   <dd style="margin-left: 1.5em;">
+     *     Specifies the action that sets the DSCP value in IP header.
+     *     The type of this element must be {@link SetDscpAction}.
+     *     <ul>
+     *       <li>
+     *         The type of this element must be {@link SetDscpAction}.
+     *       </li>
+     *       <li>This element does not affect packets without IP header.</li>
+     *     </ul>
+     *
+     *   <dt>tpsrc
+     *   <dd style="margin-left: 1.5em;">
+     *     Specifies the action that sets the source port number in TCP or
+     *     UDP header.
+     *     <ul>
+     *       <li>
+     *         The type of this element must be {@link SetTpSrcAction}.
+     *       </li>
+     *       <li>
+     *         This element does not affect packets without TCP or UDP header.
+     *       </li>
+     *       <li>
+     *         <strong>This element is not yet supported.</strong>
+     *       </li>
+     *     </ul>
+     *
+     *   <dt>tpdst
+     *   <dd style="margin-left: 1.5em;">
+     *     Specifies the action that sets the destination port number in TCP or
+     *     UDP header.
+     *     <ul>
+     *       <li>
+     *         The type of this element must be {@link SetTpDstAction}.
+     *       </li>
+     *       <li>
+     *         This element does not affect packets without TCP or UDP header.
+     *       </li>
+     *       <li>
+     *         <strong>This element is not yet supported.</strong>
+     *       </li>
+     *     </ul>
+     *
+     *   <dt>icmptype
+     *   <dd style="margin-left: 1.5em;">
+     *     Specifies the action that sets the ICMP type in ICMPv4 header.
+     *     <ul>
+     *       <li>
+     *         The type of this element must be {@link SetIcmpTypeAction}.
+     *       </li>
+     *       <li>
+     *         This element does not affect packets without ICMPv4 header.
+     *       </li>
+     *     </ul>
+     *
+     *   <dt>icmpcode
+     *   <dd style="margin-left: 1.5em;">
+     *     Specifies the action that sets the ICMP code in ICMPv4 header.
+     *     <ul>
+     *       <li>
+     *         The type of this element must be {@link SetIcmpCodeAction}.
+     *       </li>
+     *       <li>
+     *         This element does not affect packets without ICMPv4 header.
+     *       </li>
+     *     </ul>
+     * </dl>
+     */
+    @XmlElementWrapper
+    @XmlElements({
+        @XmlElement(name = "dlsrc", type = SetDlSrcAction.class),
+        @XmlElement(name = "dldst", type = SetDlDstAction.class),
+        @XmlElement(name = "vlanpcp", type = SetVlanPcpAction.class),
+        @XmlElement(name = "inet4src", type = SetInet4SrcAction.class),
+        @XmlElement(name = "inet4dst", type = SetInet4DstAction.class),
+        @XmlElement(name = "dscp", type = SetDscpAction.class),
+        @XmlElement(name = "tpsrc", type = SetTpSrcAction.class),
+        @XmlElement(name = "tpdst", type = SetTpDstAction.class),
+        @XmlElement(name = "icmptype", type = SetIcmpTypeAction.class),
+        @XmlElement(name = "icmpcode", type = SetIcmpCodeAction.class)
+    })
+    private List<FlowAction>  actions;
+
+    /**
+     * Private constructor only for JAXB.
+     */
+    private FlowFilter() {
+    }
+
+    /**
+     * Construct a new flow filter configuration.
+     *
+     * @param idx   An {@link Integer} instance which represents the filter
+     *              index.
+     * @param cond  The name of the flow condition.
+     * @param type  The type of the flow filter.
+     *              {@code null} cannot be specified.
+     * @param acts  A list of {@link FlowAction} instances to be applied to
+     *              packets.
+     */
+    private FlowFilter(Integer idx, String cond, FilterType type,
+                       List<FlowAction> acts) {
+        index = idx;
+        condition = cond;
+        filterType = type;
+        if (acts != null && !acts.isEmpty()) {
+            actions = new ArrayList(acts);
+        }
+    }
+
+    /**
+     * Construct a new flow filter configuration without specifying
+     * filter index.
+     *
+     * @param cond  The name of the flow condition.
+     *              {@code null} cannot be specified.
+     * @param type  The type of the flow filter.
+     *              {@code null} cannot be specified.
+     * @param acts  A list of {@link FlowAction} instances to be applied to
+     *              packets.
+     */
+    public FlowFilter(String cond, FilterType type, List<FlowAction> acts) {
+        this(null, cond, type, acts);
+    }
+
+    /**
+     * Construct a new flow filter configuration.
+     *
+     * @param idx   The index value to be assigned to the flow filter.
+     *              The range of value that can be specified is from
+     *              <strong>1</strong> to <strong>65535</strong>.
+     * @param cond  The name of the flow condition.
+     *              {@code null} cannot be specified.
+     * @param type  The type of the flow filter.
+     *              {@code null} cannot be specified.
+     * @param acts  A list of {@link FlowAction} instances to be applied to
+     *              packets.
+     */
+    public FlowFilter(int idx, String cond, FilterType type,
+                      List<FlowAction> acts) {
+        this(Integer.valueOf(idx), cond, type, acts);
+    }
+
+    /**
+     * Return an index value assigned to this instance.
+     *
+     * @return  An {@link Integer} instance which represents an index value
+     *          assigned to this instance.
+     *          {@code null} is returned if no index is assigned.
+     */
+    public Integer getIndex() {
+        return index;
+    }
+
+    /**
+     * Return the name of the flow condition configured in this instance.
+     *
+     * @return  The name of the flow condition.
+     */
+    public String getFlowConditionName() {
+        return condition;
+    }
+
+    /**
+     * Return a {@link FilterType} instance which represents the type of
+     * this flow filter.
+     *
+     * @return  A {@link FilterType} instance.
+     *          {@code null} is returned if no filter type is configured.
+     */
+    public FilterType getFilterType() {
+        return filterType;
+    }
+
+    /**
+     * Return a list of {@link FlowAction} instances to be applied to the
+     * packet when this flow filter is applied.
+     *
+     * @return  A list of {@link FlowAction} instances.
+     *          {@code null} is returned if no flow action is configured.
+     */
+    public List<FlowAction> getActions() {
+        return (actions == null)
+            ? null
+            : new ArrayList<FlowAction>(actions);
+    }
+
+    /**
+     * Determine whether the given object is identical to this object.
+     *
+     * @param o  An object to be compared.
+     * @return   {@code true} if identical. Otherwise {@code false}.
+     */
+    @Override
+    public boolean equals(Object o) {
+        if (o == this) {
+            return true;
+        }
+        if (o == null || !getClass().equals(o.getClass())) {
+            return false;
+        }
+
+        FlowFilter ff = (FlowFilter)o;
+        return (Objects.equals(index, ff.index) &&
+                Objects.equals(condition, ff.condition) &&
+                Objects.equals(filterType, ff.filterType) &&
+                Objects.equals(actions, ff.actions));
+    }
+
+    /**
+     * Return the hash code of this object.
+     *
+     * @return  The hash code.
+     */
+    @Override
+    public int hashCode() {
+        return Objects.hash(index, condition, filterType, actions);
+    }
+
+    /**
+     * Return a string representation of this object.
+     *
+     * @return  A string representation of this object.
+     */
+    @Override
+    public String toString() {
+        StringBuilder builder = new StringBuilder("FlowFilter[");
+        String sep = "";
+        if (index != null) {
+            builder.append("index=").append(index.toString());
+            sep = ",";
+        }
+        if (condition != null) {
+            builder.append(sep).append("cond=").append(condition);
+            sep = ",";
+        }
+        if (filterType != null) {
+            builder.append(sep).append("type=").append(filterType);
+            sep = ",";
+        }
+        if (actions != null) {
+            builder.append(sep).append("actions=").append(actions);
+        }
+
+        return builder.append(']').toString();
+    }
+}
diff --git a/manager/api/src/main/java/org/opendaylight/vtn/manager/flow/filter/PassFilter.java b/manager/api/src/main/java/org/opendaylight/vtn/manager/flow/filter/PassFilter.java
new file mode 100644 (file)
index 0000000..170cbae
--- /dev/null
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2014 NEC Corporation
+ * 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.vtn.manager.flow.filter;
+
+import javax.xml.bind.annotation.XmlAccessType;
+import javax.xml.bind.annotation.XmlAccessorType;
+import javax.xml.bind.annotation.XmlRootElement;
+
+import com.fasterxml.jackson.databind.annotation.JsonSerialize;
+
+/**
+ * {@code PassFilter} class describes the PASS flow filter which lets the
+ * specified packets through the virtual node in the VTN.
+ *
+ * <h4>Example JSON</h4>
+ * <pre class="prettyprint lang-json">{}</pre>
+ *
+ * @since  Helium
+ */
+@JsonSerialize(include = JsonSerialize.Inclusion.NON_NULL)
+@XmlRootElement(name = "pass")
+@XmlAccessorType(XmlAccessType.NONE)
+public final class PassFilter extends FilterType {
+    /**
+     * Version number for serialization.
+     */
+    private static final long serialVersionUID = 1295310703141555376L;
+
+    /**
+     * Construct a new instance.
+     */
+    public PassFilter() {
+    }
+}
diff --git a/manager/api/src/main/java/org/opendaylight/vtn/manager/flow/filter/RedirectFilter.java b/manager/api/src/main/java/org/opendaylight/vtn/manager/flow/filter/RedirectFilter.java
new file mode 100644 (file)
index 0000000..0544846
--- /dev/null
@@ -0,0 +1,272 @@
+/*
+ * Copyright (c) 2014 NEC Corporation
+ * 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.vtn.manager.flow.filter;
+
+import javax.xml.bind.annotation.XmlAccessType;
+import javax.xml.bind.annotation.XmlAccessorType;
+import javax.xml.bind.annotation.XmlAttribute;
+import javax.xml.bind.annotation.XmlElement;
+import javax.xml.bind.annotation.XmlRootElement;
+import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
+
+import com.fasterxml.jackson.databind.annotation.JsonSerialize;
+
+import org.opendaylight.vtn.manager.VBridgeIfPath;
+import org.opendaylight.vtn.manager.VInterfacePath;
+import org.opendaylight.vtn.manager.VInterfacePathAdapter;
+import org.opendaylight.vtn.manager.VTerminalIfPath;
+
+/**
+ * {@code RedirectFilter} class describes the REDIRECT flow filter which
+ * forwards the specified packets to another virtual interface in the VTN.
+ *
+ * <h4>Example JSON</h4>
+ * <pre class="prettyprint lang-json">
+ * {
+ * &nbsp;&nbsp;"destination": {
+ * &nbsp;&nbsp;&nbsp;&nbsp;"bridge": "vbridge_1",
+ * &nbsp;&nbsp;&nbsp;&nbsp;"interface": "if0",
+ * &nbsp;&nbsp;},
+ * &nbsp;&nbsp;"output": true
+ * }</pre>
+ *
+ * @since  Helium
+ */
+@JsonSerialize(include = JsonSerialize.Inclusion.NON_NULL)
+@XmlRootElement(name = "redirect")
+@XmlAccessorType(XmlAccessType.NONE)
+public final class RedirectFilter extends FilterType {
+    /**
+     * Version number for serialization.
+     */
+    private static final long serialVersionUID = 2299914147430572837L;
+
+    /**
+     * The path to the virtual interface where the specified packets are
+     * forwarded.
+     *
+     * <ul>
+     *   <li>
+     *     The VTN name configured in the <strong>tenant</strong> attribute is
+     *     always ignored. The VTN name is always determined by the location
+     *     of the virtual node that contains the flow filter.
+     *   </li>
+     *   <li>
+     *     The location of the virtual interface that contains this
+     *     flow filter can not be specified.
+     *   </li>
+     *   <li>
+     *     Note that every packet redirected by the flow filter is discarded
+     *     if the virtual interface specified by this element does not exist
+     *     in the VTN.
+     *   </li>
+     * </ul>
+     * <p>
+     *   Packet redirection should be configured not to cause the packet loop.
+     *   The number of virtual node hops per a flow (the number of packet
+     *   redirections) is limited to <strong>100</strong>.
+     *   If the number of virtual node hops exceeds the limit, it is treated
+     *   as the packet loop and then the packet is discarded.
+     * </p>
+     */
+    @XmlJavaTypeAdapter(VInterfacePathAdapter.class)
+    @XmlElement(required = true)
+    private VInterfacePath  destination;
+
+    /**
+     * Determine the direction of the packet redirection.
+     *
+     * <ul>
+     *   <li>
+     *     If <strong>true</strong> is specified, the packet is redirected
+     *     as outgoing packet.
+     *     <ul>
+     *       <li>
+     *         The redirected packet will be treated as if it is transmitted
+     *         from the virtual interface specified by the
+     *         <strong>destination</strong> element.
+     *       </li>
+     *       <li>
+     *         A list of flow filters for outgoing packets configured in the
+     *         virtual interface specified by <strong>destination</strong>
+     *         will be evaluated against the redirected packet.
+     *         If the packet is passed by the flow filter, it is transmitted
+     *         to the physical network mapped to the virtual interface by
+     *         port mapping. The packet will be discarded if port mapping
+     *         is not configured to the virtual interface.
+     *       </li>
+     *     </ul>
+     *   </li>
+     *   <li>
+     *     If <strong>false</strong> is specified, the packet is redirected
+     *     as incoming packet.
+     *     <ul>
+     *       <li>
+     *         The redirected packet will be treated as if it is received from
+     *         the virtual interface specified by the
+     *         <strong>destination</strong> element.
+     *         The packet is redirected even if no physical network is mapped
+     *         to the destination virtual interface by port mapping.
+     *       </li>
+     *       <li>
+     *         A list of flow filters for incoming packets configured in the
+     *         virtual interface specified by <strong>destination</strong>
+     *         will be evaluated against the redirected packet.
+     *         If the packet is passed by the flow filter, it is forwarded to
+     *         the virtual node that contains the virtual interface.
+     *       </li>
+     *     </ul>
+     *   </li>
+     *   <li>
+     *     If omitted, it will be treated as if <strong>false</strong> is
+     *     specified.
+     *   </li>
+     * </ul>
+     */
+    @XmlAttribute
+    private boolean  output;
+
+    /**
+     * Private constructor only for JAXB.
+     */
+    private RedirectFilter() {
+    }
+
+    /**
+     * Construct a new instance that specifies the packet redirection to be
+     * done by a flow filter.
+     *
+     * @param path
+     *   The path to the vBridge interface where the specified packets should
+     *   be redirected.
+     *   <ul>
+     *     <li>{@code null} can not be specified.</li>
+     *     <li>
+     *       The VTN name configured in {@code path} is always ignored.
+     *       The VTN name is always determined by the location of the virtual
+     *       node that contains the flow filter.
+     *     </li>
+     *   </ul>
+     * @param out     {@code true} means the redirected packet should be
+     *                treated as outgoing packet.
+     *                {@code false} means the redirected packet should be
+     *                treated as incoming packet.
+     */
+    public RedirectFilter(VBridgeIfPath path, boolean out) {
+        destination = path;
+        output = out;
+    }
+
+    /**
+     * Construct a new instance that specifies the packet redirection to be
+     * done by a flow filter.
+     *
+     * @param path
+     *   The path to the vTerminal interface where the specified packets should
+     *   be redirected.
+     *   <ul>
+     *     <li>{@code null} can not be specified.</li>
+     *     <li>
+     *       The VTN name configured in {@code path} is always ignored.
+     *       The VTN name is always determined by the location of the virtual
+     *       node that contains the flow filter.
+     *     </li>
+     *   </ul>
+     * @param out     {@code true} means the redirected packet should be
+     *                treated as outgoing packet.
+     *                {@code false} means the redirected packet should be
+     *                treated as incoming packet.
+     */
+    public RedirectFilter(VTerminalIfPath path, boolean out) {
+        destination = path;
+        output = out;
+    }
+
+    /**
+     * Return the location of the virtual interface where the packet should
+     * be redirected.
+     *
+     * @return  A {@link VInterfacePath} instance.
+     */
+    public VInterfacePath getDestination() {
+        return destination;
+    }
+
+    /**
+     * Determine whether the direction of packet redirection.
+     *
+     * @return  {@code true} is returned if the redirected packet should be
+     *          treated as outgoing packet.
+     *          {@code false} is returned if the redirected packet should be
+     *          treated as incoming packet.
+     */
+    public boolean isOutput() {
+        return output;
+    }
+
+    /**
+     * Determine whether the given object is identical to this object.
+     *
+     * @param o  An object to be compared.
+     * @return   {@code true} if identical. Otherwise {@code false}.
+     */
+    @Override
+    public boolean equals(Object o) {
+        if (o == this) {
+            return true;
+        }
+        if (!super.equals(o)) {
+            return false;
+        }
+
+        RedirectFilter rf = (RedirectFilter)o;
+        if (output != rf.output) {
+            return false;
+        }
+
+        if (destination == null) {
+            return (rf.destination == null);
+        }
+
+        return destination.equals(rf.destination);
+    }
+
+    /**
+     * Return the hash code of this object.
+     *
+     * @return  The hash code.
+     */
+    @Override
+    public int hashCode() {
+        int h = super.hashCode();
+        if (output) {
+            h++;
+        }
+        if (destination != null) {
+            h += (destination.hashCode() * 17);
+        }
+
+        return h;
+    }
+
+    /**
+     * Return a string representation of this object.
+     *
+     * @return  A string representation of this object.
+     */
+    @Override
+    public String toString() {
+        StringBuilder builder = new StringBuilder("RedirectFilter[");
+        if (destination != null) {
+            builder.append("destination=").append(destination).append(',');
+        }
+        return builder.append("output=").append(output).append(']').toString();
+    }
+}
diff --git a/manager/api/src/main/java/org/opendaylight/vtn/manager/flow/filter/package-info.java b/manager/api/src/main/java/org/opendaylight/vtn/manager/flow/filter/package-info.java
new file mode 100644 (file)
index 0000000..f2dbec0
--- /dev/null
@@ -0,0 +1,367 @@
+/*
+ * Copyright (c) 2014 NEC Corporation
+ * 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
+ */
+
+/**
+ * This package provides public APIs related to flow filter.
+ *
+ * <h2 id="function-overview" style="border-bottom: 4px double #aaaaaa; padding-top: 0.5em;">
+ *   Function overview
+ * </h2>
+ *
+ * <div style="margin-left: 1em;">
+ *   <p>
+ *     Flow filter provides packet filtering feature for packets forwarded
+ *     in <a href="../../package-summary.html#VTN">VTN</a>.
+ *     Flow filter can not only filter out the specified packets but also
+ *     modify the specified packets.
+ *   </p>
+ *   <p>
+ *     Each flow filter must specify a
+ *     <a href="../cond/package-summary.html">flow condition</a> by name.
+ *     If a packet matches the condition described by the flow condition in a
+ *     flow filter, then actions configured in the same flow filter is applied
+ *     to that packet.
+ *   </p>
+ * </div>
+ *
+ * <h3 id="type" style="border-bottom: 2px solid #aaaaaa;">
+ *   Type of flow filter
+ * </h3>
+ * <div style="margin-left: 1em;">
+ *   <p>
+ *     There are three types of flow filter as follows.
+ *   </p>
+ *   <dl style="margin-left: 1em;">
+ *     <dt id="type.PASS" style="font-weight: bold; margin-top: 0.5em;">PASS
+ *     <dd style="margin-left: 2em;">
+ *       Let the packet through the virtual node if the packet matches the
+ *       flow condition configured in a flow filter.
+ *       This type of flow filter can be used to modify the specified packets.
+ *
+ *     <dt id="type.DROP" style="font-weight: bold; margin-top: 0.5em;">DROP
+ *     <dd style="margin-left: 2em;">
+ *       Discard the packet if the packet matches the flow condition configured
+ *       in a flow filter.
+ *
+ *     <dt id="type.REDIRECT" style="font-weight: bold; margin-top: 0.5em;">REDIRECT
+ *     <dd style="margin-left: 2em;">
+ *       Forward the packet to another virtual interface in the same VTN if the
+ *       packet matches the flow condition configured in a flow filter.
+ *       This type of flow filter also can modify the matched packet.
+ *       See description about <a href="#redirect">packet redirection</a> for
+ *       more details.
+ *   </dl>
+ * </div>
+ *
+ * <h3 id="actions" style="border-bottom: 2px solid #aaaaaa;">
+ *   Flow action list
+ * </h3>
+ * <div style="margin-left: 1em;">
+ *   <p>
+ *     <strong>Flow action list</strong> is a list of rules to modify packet.
+ *   </p>
+ *   <ul>
+ *     <li>
+ *       When a <a href="#type.PASS">PASS</a> or a
+ *       <a href="#type.REDIRECT">REDIRECT</a> flow filter is applied to
+ *       a packet, flow actions configured in the same flow filter are applied
+ *       to the packet in order.
+ *     </li>
+ *     <li>
+ *       Although a <a href="#type.DROP">DROP</a> flow filter can have
+ *       flow actions, they will be always ignored.
+ *     </li>
+ *   </ul>
+ *
+ *   <p>
+ *     Note that modification done by flow actions in a flow filter is visible
+ *     to succeeding evaluation of flow filters.
+ *   </p>
+ * </div>
+ *
+ * <h3 id="place" style="border-bottom: 2px solid #aaaaaa;">
+ *   Place to configure flow filter
+ * </h3>
+ * <div style="margin-left: 1em;">
+ *   <p>
+ *     One or more flow filters can be configured in virtual node in VTN as a
+ *     list, and it is evaluated when a packet is forwarded to the
+ *     virtual node. Each flow filter has a unique index in the list, and
+ *     they are evaluated in ascending order of indices, and only the first
+ *     matched flow filter is applied to the packet.
+ *     If none of flow filter in the list matches the packet, then the
+ *     VTN Manager lets the packet through the virtual node without modifying
+ *     the packet.
+ *   </p>
+ *   <p>
+ *     Flow filter can be configured in the following places.
+ *   </p>
+ *   <dl style="margin-left: 1em;">
+ *     <dt id="place.VTN" style="font-weight: bold; margin-top: 0.5em;">VTN
+ *     <dd style="margin-left: 2em;">
+ *       <p>
+ *         Flow filters in this list are evaluated when an incoming packet is
+ *         mapped to the VTN. Note that the VTN flow filter list is evaluated
+ *         only once before other flow filter lists are evaluated.
+ *       </p>
+ *
+ *     <dt id="place.vBridge.in" style="font-weight: bold; margin-top: 0.5em;">vBridge (input)
+ *     <dd style="margin-left: 2em;">
+ *       <p>
+ *         Flow filters in this list are evaluated when a packet is forwarded
+ *         to the specified
+ *         <a href="../../package-summary.html#vBridge">vBridge</a>.
+ *         This list is evaluated at the following instances.
+ *       </p>
+ *       <ul>
+ *         <li>
+ *           When a packet is forwarded from the virtual interface to the
+ *           vBridge.
+ *         </li>
+ *         <li>
+ *           When an incoming packet is mapped to the vBridge by
+ *           <a href="../../package-summary.html#VLAN-map">VLAN mapping</a> or
+ *           <a href="../../package-summary.html#MAC-map">MAC mapping</a>.
+ *         </li>
+ *       </ul>
+ *
+ *     <dt id="place.vBridge.out" style="font-weight: bold; margin-top: 0.5em;">vBridge (output)
+ *     <dd style="margin-left: 2em;">
+ *       <p>
+ *         Flow filters in this list are evaluated when a packet is going to be
+ *         transmitted to the physical network mapped to the
+ *         <a href="../../package-summary.html#vBridge">vBridge</a> by
+ *         <a href="../../package-summary.html#VLAN-map">VLAN mapping</a> or
+ *         <a href="../../package-summary.html#MAC-map">MAC mapping</a>.
+ *         Note that this list is not evaluated when a packet is forwarded to
+ *         the virtual interface in the same vBridge.
+ *       </p>
+ *
+ *     <dt id="place.vBridge.vif.in" style="font-weight: bold; margin-top: 0.5em;">vBridge interface (input)
+ *     <dd style="margin-left: 2em;">
+ *       <p>
+ *         Flow filters in this list are evaluated when a packet is forwarded to
+ *         the specified
+ *         <a href="../../package-summary.html#vInterface">virtual interface</a>
+ *         in the <a href="../../package-summary.html#vBridge">vBridge</a>.
+ *         This list is evaluated at the following instances.
+ *       </p>
+ *       <ul>
+ *         <li>
+ *           When an incoming packet is mapped to the vBridge interface by
+ *           <a href="../../package-summary.html#port-map">port mapping</a>.
+ *         </li>
+ *         <li>
+ *           When a packet is redirected by another flow filter to the
+ *           vBridge interface as an incoming packet.
+ *         </li>
+ *       </ul>
+ *
+ *     <dt id="place.vBridge.vif.out" style="font-weight: bold; margin-top: 0.5em;">vBridge interface (output)
+ *     <dd style="margin-left: 2em;">
+ *       <p>
+ *         Flow filters in this list are evaluated when a packet is going to be
+ *         transmitted to the physical network mapped to the
+ *         <a href="../../package-summary.html#vInterface">virtual interface</a>
+ *         in the <a href="../../package-summary.html#vBridge">vBridge</a>.
+ *         This list is evaluated at the following instances.
+ *       </p>
+ *       <ul>
+ *         <li>
+ *           When a packet is forwarded from the vBridge to the virtual
+ *           interface.
+ *         </li>
+ *         <li>
+ *           When a packet is redirected by another flow filter to the
+ *           vBridge interface as an outgoing packet.
+ *         </li>
+ *       </ul>
+ *
+ *     <dt id="place.vTerminal.vif.in" style="font-weight: bold; margin-top: 0.5em;">vTerminal interface (input)
+ *     <dd style="margin-left: 2em;">
+ *       <p>
+ *         Flow filters in this list are evaluated when a packet is forwarded
+ *         to the specified
+ *         <a href="../../package-summary.html#vInterface">virtual interface</a>
+ *         in the <a href="../../package-summary.html#vTerminal">vTerminal</a>.
+ *         This list is evaluated at the following instances.
+ *       </p>
+ *       <ul>
+ *         <li>
+ *           When an incoming packet is mapped to the vTerminal interface by
+ *           <a href="../../package-summary.html#port-map">port mapping</a>.
+ *         </li>
+ *         <li>
+ *           When a packet is redirected by another flow filter to the
+ *           vTerminal interface as an incoming packet.
+ *         </li>
+ *       </ul>
+ *       <p>
+ *         vTerminal is an isolated input/output terminal.
+ *         So an incoming packet is always discarded unless it is redirected
+ *         to another virtual interface by the flow filter.
+ *       </p>
+ *
+ *     <dt id="place.vTerminal.vif.out" style="font-weight: bold; margin-top: 0.5em;">vTerminal interface (output)
+ *     <dd style="margin-left: 2em;">
+ *       <p>
+ *         Flow filters in this list are evaluated when a packet is going to
+ *         be transmitted to the physical network mapped to the
+ *         <a href="../../package-summary.html#vInterface">virtual interface</a>
+ *         in the <a href="../../package-summary.html#vTerminal">vTerminal</a>.
+ *       </p>
+ *       <p>
+ *         This list is evaluated only when a packet is redirected by another
+ *         flow filter to the vTerminal interface as an outgoing packet.
+ *       </p>
+ *   </dl>
+ * </div>
+ *
+ * <h3 id="redirect" style="border-bottom: 2px solid #aaaaaa;">
+ *   Packet redirection
+ * </h3>
+ * <div style="margin-left: 1em;">
+ *   <p>
+ *     A <a href="#type.REDIRECT">REDIRECT</a> flow filter forwards the packet
+ *     to another
+ *     <a href="../../package-summary.html#vInterface">virtual interface</a>
+ *     in the same <a href="../../package-summary.html#VTN">VTN</a>.
+ *     A REDIRECT flow filter has the following configurations.
+ *   </p>
+ *   <dl style="margin-left: 1em;">
+ *     <dt id="redirect.destination" style="font-weight: bold; margin-top: 0.5em;">Destination virtual interface
+ *     <dd style="margin-left: 2em;">
+ *       <p>
+ *         The location of the destination virtual interface must be configured
+ *         in every REDIRECT flow filter.
+ *       </p>
+ *       <ul>
+ *         <li>
+ *           Self-redirection (specifying the virtual interface that contains
+ *           the REDIRECT flow filter itself as the destination) is always
+ *           forbidden.
+ *         </li>
+ *         <li>
+ *           If the specified destination node does not exist, every packets
+ *           matched to that REDIRECT flow filter will be discarded.
+ *         </li>
+ *       </ul>
+ *
+ *     <dt id="redirect.destination" style="font-weight: bold; margin-top: 0.5em;">Direction
+ *     <dd style="margin-left: 2em;">
+ *       <p>
+ *         Every REDIRECT flow filter must choose the direction of the packet
+ *         redirection, <strong>input</strong> or <strong>output</strong>.
+ *       </p>
+ *       <ul>
+ *         <li>
+ *           <p>
+ *             <strong>input</strong> means that a redirected packet should be
+ *             treated as an incoming packet as if it is forwarded or mapped
+ *             to the specified virtual interface.
+ *           </p>
+ *           <p>
+ *             A list of flow filters for incoming packets configured in the
+ *             destination virtual interface is evaluated against the
+ *             redirected packet. If the flow filter passes the packet,
+ *             the packet is forwarded to the virtual node which contains the
+ *             destination virtual interface.
+ *             <ul>
+ *               <li>
+ *                 If the destination virtual interface is attached to the
+ *                 <a href="../../package-summary.html#vBridge">vBridge</a>,
+ *                 then the packet is routed according to the vBridge
+ *                 configuration.
+ *                 Note that the source MAC address of the redirected packet
+ *                 is never learned into the
+ *                 <a href="../../package-summary.html#macTable">MAC address table</a>
+ *                 in the vBridge.
+ *               </li>
+ *               <li>
+ *                 If the destination virtual interface is attached to the
+ *                 <a href="../../package-summary.html#vTerminal">vTerminal</a>,
+ *                 then the packet is always discarded. In other words, the
+ *                 packet is always discarded unless the packet is redirected
+ *                 to another interface by the flow filter configured in the
+ *                 destination virtual interface.
+ *               </li>
+ *             </ul>
+ *           <p>
+ *         </li>
+ *         <li>
+ *           <p>
+ *             <strong>output</strong> means that a redirected packet should be
+ *             treated as an outgoing packet as if it is going to be
+ *             transmitted to the physical network mapped to the specified
+ *             virtual interface.
+ *           </p>
+ *           <p>
+ *             A list of flow filters for outgoing packets configured in the
+ *             destination virtual interface is evaluated against the
+ *             redirected packet. If the flow filter passes the packet,
+ *             the packet is transmitted to the physical network mapped to the
+ *             virtual interface by
+ *             <a href="../../package-summary.html#port-map">port mapping</a>.
+ *             Note that the packet is discarded if the port mapping is not
+ *             configured in the virtual interface.
+ *           </p>
+ *         </li>
+ *       </ul>
+ *   </dl>
+ *
+ *   <h4 id="redirect.loop" style="border-bottom: 1px dashed #aaaaaa;">
+ *     Packet loop detection
+ *   </h4>
+ *   <div style="margin-left: 1em;">
+ *     <p>
+ *       <a href="#type.REDIRECT">REDIRECT</a> flow filter should be configured
+ *       not to cause the packet loop. The number of virtual node hops per a
+ *       flow (the number of packet redirections per a flow) is limited to
+ *       <strong>100</strong>. If the number of virtual node hops exceeds the
+ *       limit, it is treated as the packet loop and then the packet is
+ *       discarded.
+ *     </p>
+ *   </div>
+ * </div>
+ *
+ * <h3 style="border-bottom: 2px solid #aaaaaa;">
+ *   Limitations
+ * </h3>
+ * <div style="margin-left: 1em;">
+ *   <h4 id="multicast" style="border-bottom: 1px dashed #aaaaaa;">
+ *     Broadcast/Multicast packet
+ *   </h4>
+ *   <div style="margin-left: 1em;">
+ *     <p>
+ *       Basically, flow filter can be applied to unicast packets.
+ *       So flow filter ignores broadcast and multicast packets except for
+ *       <a href="#type.DROP">DROP</a> flow filter.
+ *     </p>
+ *     <p>
+ *       For example, a broadcast packet is mapped to the VTN, flow filters
+ *       in the <a href="#place.VTN">VTN flow filter</a> are evaluated as
+ *       follows.
+ *     </p>
+ *     <ul>
+ *       <li>
+ *         A flow filter is ignored if its type is <a href="#type.PASS">PASS</a>
+ *         or <a href="#type.REDIRECT">REDIRECT</a>.
+ *       </li>
+ *       <li>
+ *         A flow filter is evaluated if its type is
+ *         <a href="#type.DROP">DROP</a>. It the broadcast packet matches that
+ *         flow filter, then the packet is discarded.
+ *       </li>
+ *     </ul>
+ *   </div>
+ * </div>
+ *
+ * @since  Helium
+ */
+package org.opendaylight.vtn.manager.flow.filter;
index 6b6468c5fff89d5e4d0a0c73f6e28f9a2984e747..6f892a7565995587f178e5c952aa60275ebc2ac4 100644 (file)
@@ -42,6 +42,10 @@ import org.opendaylight.vtn.manager.flow.action.SetTpDstAction;
 import org.opendaylight.vtn.manager.flow.action.SetTpSrcAction;
 import org.opendaylight.vtn.manager.flow.action.SetVlanIdAction;
 import org.opendaylight.vtn.manager.flow.action.SetVlanPcpAction;
+import org.opendaylight.vtn.manager.flow.filter.DropFilter;
+import org.opendaylight.vtn.manager.flow.filter.FilterType;
+import org.opendaylight.vtn.manager.flow.filter.PassFilter;
+import org.opendaylight.vtn.manager.flow.filter.RedirectFilter;
 
 import org.opendaylight.controller.sal.core.Node;
 import org.opendaylight.controller.sal.core.NodeConnector;
@@ -296,6 +300,45 @@ public abstract class TestBase extends Assert {
         return null;
     }
 
+    /**
+     * Create a copy of the specified {@link FilterType} instance.
+     *
+     * @param type  A {@link FilterType} instance to be copied.
+     * @return      A copied {@link FilterType} instance.
+     */
+    protected static FilterType copy(FilterType type) {
+        if (type == null) {
+            return null;
+        }
+        if (type instanceof PassFilter) {
+            return new PassFilter();
+        }
+        if (type instanceof DropFilter) {
+            return new DropFilter();
+        }
+        if (type instanceof RedirectFilter) {
+            RedirectFilter rf = (RedirectFilter)type;
+            VInterfacePath path = rf.getDestination();
+            boolean output = rf.isOutput();
+            if (path instanceof VBridgeIfPath) {
+                VBridgeIfPath ipath = new VBridgeIfPath(
+                    copy(path.getTenantName()),
+                    copy(path.getTenantNodeName()),
+                    copy(path.getInterfaceName()));
+                return new RedirectFilter(ipath, output);
+            } else {
+                VTerminalIfPath ipath = new VTerminalIfPath(
+                    copy(path.getTenantName()),
+                    copy(path.getTenantNodeName()),
+                    copy(path.getInterfaceName()));
+                return new RedirectFilter(ipath, output);
+            }
+        }
+
+        fail("Unexpected filter type: " + type);
+        return null;
+    }
+
     /**
      * Create a deep copy of the specified set.
      *
@@ -347,6 +390,8 @@ public abstract class TestBase extends Assert {
                 dst.add(copy((MacAddressEntry)elem));
             } else if (elem instanceof FlowAction) {
                 dst.add(copy((FlowAction)elem));
+            } else if (elem instanceof FilterType) {
+                dst.add(copy((FilterType)elem));
             } else {
                 fail("Unexpected instanse in the collection: " + elem);
             }
index eb4379ec4a8c45546f4a8021e5b8709aa3f25e2c..56c5493c6a0b75c16f494617b5d54ec322cf7e7d 100644 (file)
@@ -39,6 +39,19 @@ public class VBridgeIfPathTest extends TestBase {
                     assertEquals(bname, path.getBridgeName());
                     assertEquals(iname, path.getInterfaceName());
                     assertEquals("vBridge-IF", path.getNodeType());
+
+                    VTenantPath clone = path.clone();
+                    assertNotSame(clone, path);
+                    assertEquals(clone, path);
+
+                    String name = tname + "_new";
+                    VBridgeIfPath path1 =
+                        (VBridgeIfPath)path.replaceTenantName(name);
+                    assertEquals(name, path1.getTenantName());
+                    assertEquals(bname, path1.getBridgeName());
+                    assertEquals(iname, path1.getInterfaceName());
+                    assertEquals("vBridge-IF", path1.getNodeType());
+                    assertEquals(VBridgeIfPath.class, path1.getClass());
                 }
             }
         }
index c94c583bb5be6cd543beb5e6415b76a4c1b7c999..4e5caa8b2ba3f8a1eac824d4afb81b0bb7ead933 100644 (file)
@@ -35,6 +35,17 @@ public class VBridgePathTest extends TestBase {
                 assertEquals(tname, path.getTenantName());
                 assertEquals(bname, path.getBridgeName());
                 assertEquals("vBridge", path.getNodeType());
+
+                VTenantPath clone = path.clone();
+                assertNotSame(clone, path);
+                assertEquals(clone, path);
+
+                String name = tname + "_new";
+                VBridgePath path1 = (VBridgePath)path.replaceTenantName(name);
+                assertEquals(name, path1.getTenantName());
+                assertEquals(bname, path1.getBridgeName());
+                assertEquals("vBridge", path1.getNodeType());
+                assertEquals(VBridgePath.class, path1.getClass());
             }
         }
     }
diff --git a/manager/api/src/test/java/org/opendaylight/vtn/manager/VInterfacePathAdapterTest.java b/manager/api/src/test/java/org/opendaylight/vtn/manager/VInterfacePathAdapterTest.java
new file mode 100644 (file)
index 0000000..abe5f2a
--- /dev/null
@@ -0,0 +1,62 @@
+/*
+ * Copyright (c) 2014 NEC Corporation
+ * 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.vtn.manager;
+
+import org.junit.Test;
+
+/**
+ * JUnit test for {@link VInterfacePathAdapter}.
+ */
+public class VInterfacePathAdapterTest extends TestBase {
+    /**
+     * Test case for {@link VInterfacePathAdapter#marshal(VInterfacePath)} and
+     * {@link VInterfacePathAdapter#unmarshal(VNodeLocation)}.
+     */
+    @Test
+    public void testMarshal() {
+        VInterfacePathAdapter adapter = new VInterfacePathAdapter();
+        assertEquals(null, adapter.marshal(null));
+        assertEquals(null, adapter.unmarshal(null));
+
+        for (String tname: createStrings("tenant")) {
+            for (String bname: createStrings("bridge", false)) {
+                VBridgePath bpath = new VBridgePath(tname, bname);
+                VTerminalPath vtpath = new VTerminalPath(tname, bname);
+                for (String iname: createStrings("ifname", false)) {
+                    VBridgeIfPath bipath = new VBridgeIfPath(bpath, iname);
+                    VNodeLocation loc = new VNodeLocation(bipath);
+                    assertEquals(bipath, adapter.unmarshal(loc));
+                    assertEquals(loc, adapter.marshal(bipath));
+
+                    VTerminalIfPath vipath =
+                        new VTerminalIfPath(vtpath, iname);
+                    loc = new VNodeLocation(vipath);
+                    assertEquals(vipath, adapter.unmarshal(loc));
+                    assertEquals(loc, adapter.marshal(vipath));
+                }
+            }
+        }
+
+        // Specifying invalid path.
+        VInterfacePath path = new VBridgeIfPath("vtn", null, "if_1");
+        VNodeLocation loc = path.toVNodeLocation();
+        VInterfacePath invalid = adapter.unmarshal(loc);
+        assertEquals(ErrorVNodePath.class, invalid.getClass());
+        assertEquals("Unexpected interface location: " + loc,
+                     ((ErrorVNodePath)invalid).getError());
+
+        path = new VBridgeIfPath("vtn", "vbridge", null);
+        loc = path.toVNodeLocation();
+        invalid = adapter.unmarshal(loc);
+        assertEquals(ErrorVNodePath.class, invalid.getClass());
+        assertEquals("Interface name is not specified: " + loc,
+                     ((ErrorVNodePath)invalid).getError());
+    }
+}
diff --git a/manager/api/src/test/java/org/opendaylight/vtn/manager/VNodePathAdapterTest.java b/manager/api/src/test/java/org/opendaylight/vtn/manager/VNodePathAdapterTest.java
new file mode 100644 (file)
index 0000000..4490ad9
--- /dev/null
@@ -0,0 +1,70 @@
+/*
+ * Copyright (c) 2014 NEC Corporation
+ * 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.vtn.manager;
+
+import org.junit.Test;
+
+/**
+ * JUnit test for {@link VNodePathAdapter}.
+ */
+public class VNodePathAdapterTest extends TestBase {
+    /**
+     * Test case for {@link VNodePathAdapter#marshal(VNodePath)} and
+     * {@link VNodePathAdapter#unmarshal(VNodeLocation)}.
+     */
+    @Test
+    public void testMarshal() {
+        VNodePathAdapter adapter = new VNodePathAdapter();
+        assertEquals(null, adapter.marshal(null));
+        assertEquals(null, adapter.unmarshal(null));
+
+        for (String tname: createStrings("tenant")) {
+            for (String bname: createStrings("bridge", false)) {
+                VBridgePath bpath = new VBridgePath(tname, bname);
+                VNodeLocation loc = new VNodeLocation(bpath);
+                assertEquals(bpath, adapter.unmarshal(loc));
+                assertEquals(loc, adapter.marshal(bpath));
+
+                VTerminalPath vtpath = new VTerminalPath(tname, bname);
+                loc = new VNodeLocation(vtpath);
+                assertEquals(vtpath, adapter.unmarshal(loc));
+                assertEquals(loc, adapter.marshal(vtpath));
+
+                for (String iname: createStrings("ifname", false)) {
+                    VBridgeIfPath bipath = new VBridgeIfPath(bpath, iname);
+                    loc = new VNodeLocation(bipath);
+                    assertEquals(bipath, adapter.unmarshal(loc));
+                    assertEquals(loc, adapter.marshal(bipath));
+
+                    VTerminalIfPath vipath =
+                        new VTerminalIfPath(vtpath, iname);
+                    loc = new VNodeLocation(vipath);
+                    assertEquals(vipath, adapter.unmarshal(loc));
+                    assertEquals(loc, adapter.marshal(vipath));
+                }
+            }
+        }
+
+        // Specifying invalid path.
+        VNodePath path = new VBridgePath("vtn", null);
+        VNodeLocation loc = path.toVNodeLocation();
+        VNodePath invalid = adapter.unmarshal(loc);
+        assertEquals(ErrorVNodePath.class, invalid.getClass());
+        assertEquals("Unexpected node location: " + loc,
+                     ((ErrorVNodePath)invalid).getError());
+
+        path = new VBridgeIfPath("vtn", null, "if_1");
+        loc = path.toVNodeLocation();
+        invalid = adapter.unmarshal(loc);
+        assertEquals(ErrorVNodePath.class, invalid.getClass());
+        assertEquals("Unexpected node location: " + loc,
+                     ((ErrorVNodePath)invalid).getError());
+    }
+}
index 904a6f6bb8563ead5fc745b1db4f79c5a90426cd..1ef0fd4f1d142828399a24c40d828e597d09e633 100644 (file)
@@ -46,6 +46,16 @@ public class VTenantPathTest extends TestBase {
             VTenantPath path = new VTenantPath(tname);
             assertEquals(tname, path.getTenantName());
             assertEquals("VTN", path.getNodeType());
+
+            VTenantPath clone = path.clone();
+            assertNotSame(clone, path);
+            assertEquals(clone, path);
+
+            String name = tname + "_new";
+            VTenantPath path1 = path.replaceTenantName(name);
+            assertEquals(name, path1.getTenantName());
+            assertEquals("VTN", path1.getNodeType());
+            assertEquals(VTenantPath.class, path1.getClass());
         }
     }
 
index 160c55e3eb41e2094cce251704708d019b7b4b6f..39eead535fd46befd06dd2ad59521997059c8a4c 100644 (file)
@@ -39,6 +39,19 @@ public class VTerminalIfPathTest extends TestBase {
                     assertEquals(vtname, path.getTerminalName());
                     assertEquals(iname, path.getInterfaceName());
                     assertEquals("vTerminal-IF", path.getNodeType());
+
+                    VTenantPath clone = path.clone();
+                    assertNotSame(clone, path);
+                    assertEquals(clone, path);
+
+                    String name = tname + "_new";
+                    VTerminalIfPath path1 =
+                        (VTerminalIfPath)path.replaceTenantName(name);
+                    assertEquals(name, path1.getTenantName());
+                    assertEquals(vtname, path1.getTerminalName());
+                    assertEquals(iname, path1.getInterfaceName());
+                    assertEquals("vTerminal-IF", path1.getNodeType());
+                    assertEquals(VTerminalIfPath.class, path1.getClass());
                 }
             }
         }
index c49856dff2af2352c182fe03514b771ed61d9a58..fb117e776a1d951769e55df2b9ff6c0c43fe4f88 100644 (file)
@@ -35,6 +35,18 @@ public class VTerminalPathTest extends TestBase {
                 assertEquals(tname, path.getTenantName());
                 assertEquals(mname, path.getTerminalName());
                 assertEquals("vTerminal", path.getNodeType());
+
+                VTenantPath clone = path.clone();
+                assertNotSame(clone, path);
+                assertEquals(clone, path);
+
+                String name = tname + "_new";
+                VTerminalPath path1 =
+                    (VTerminalPath)path.replaceTenantName(name);
+                assertEquals(name, path1.getTenantName());
+                assertEquals(mname, path1.getTerminalName());
+                assertEquals("vTerminal", path1.getNodeType());
+                assertEquals(VTerminalPath.class, path1.getClass());
             }
         }
     }
diff --git a/manager/api/src/test/java/org/opendaylight/vtn/manager/flow/filter/DropFilterTest.java b/manager/api/src/test/java/org/opendaylight/vtn/manager/flow/filter/DropFilterTest.java
new file mode 100644 (file)
index 0000000..5c29e85
--- /dev/null
@@ -0,0 +1,84 @@
+/*
+ * Copyright (c) 2014 NEC Corporation
+ * 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.vtn.manager.flow.filter;
+
+import java.util.HashSet;
+
+import org.junit.Test;
+
+import org.opendaylight.vtn.manager.VBridgeIfPath;
+
+import org.opendaylight.vtn.manager.TestBase;
+
+/**
+ * JUnit test for {@link DropFilter}.
+ */
+public class DropFilterTest extends TestBase {
+    /**
+     * Test case for {@link DropFilter#equals(Object)} and
+     * {@link DropFilter#hashCode()}.
+     */
+    @Test
+    public void testEquals() {
+        HashSet<Object> set = new HashSet<Object>();
+        testEquals(set, new DropFilter(), new DropFilter());
+        for (int i = 0; i < 10; i++) {
+            assertFalse(set.add(new DropFilter()));
+        }
+
+        assertEquals(1, set.size());
+        assertTrue(set.add(new PassFilter()));
+
+        VBridgeIfPath path = new VBridgeIfPath("vtn", "vbr", "if");
+        assertTrue(set.add(new RedirectFilter(path, true)));
+        assertEquals(3, set.size());
+    }
+
+    /**
+     * Test case for {@link DropFilter#toString()}.
+     */
+    @Test
+    public void testToString() {
+        for (int i = 0; i < 10; i++) {
+            DropFilter pf = new DropFilter();
+            assertEquals("DropFilter", pf.toString());
+        }
+    }
+
+    /**
+     * Ensure that {@link DropFilter} is serializable.
+     */
+    @Test
+    public void testSerialize() {
+        for (int i = 0; i < 10; i++) {
+            serializeTest(new DropFilter());
+        }
+    }
+
+    /**
+     * Ensure that {@link DropFilter} is mapped to XML root element.
+     */
+    @Test
+    public void testJAXB() {
+        for (int i = 0; i < 10; i++) {
+            jaxbTest(new DropFilter(), "drop");
+        }
+    }
+
+    /**
+     * Ensure that {@link DropFilter} is mapped to JSON object.
+     */
+    @Test
+    public void testJSON() {
+        for (int i = 0; i < 10; i++) {
+            jsonTest(new DropFilter());
+        }
+    }
+}
diff --git a/manager/api/src/test/java/org/opendaylight/vtn/manager/flow/filter/FlowFilterTest.java b/manager/api/src/test/java/org/opendaylight/vtn/manager/flow/filter/FlowFilterTest.java
new file mode 100644 (file)
index 0000000..fbe9486
--- /dev/null
@@ -0,0 +1,322 @@
+/*
+ * Copyright (c) 2014 NEC Corporation
+ * 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.vtn.manager.flow.filter;
+
+import java.net.InetAddress;
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+
+import org.junit.Test;
+
+import org.opendaylight.vtn.manager.VBridgeIfPath;
+import org.opendaylight.vtn.manager.VTerminalIfPath;
+import org.opendaylight.vtn.manager.flow.action.FlowAction;
+import org.opendaylight.vtn.manager.flow.action.SetDlDstAction;
+import org.opendaylight.vtn.manager.flow.action.SetDlSrcAction;
+import org.opendaylight.vtn.manager.flow.action.SetDscpAction;
+import org.opendaylight.vtn.manager.flow.action.SetIcmpCodeAction;
+import org.opendaylight.vtn.manager.flow.action.SetIcmpTypeAction;
+import org.opendaylight.vtn.manager.flow.action.SetInet4DstAction;
+import org.opendaylight.vtn.manager.flow.action.SetInet4SrcAction;
+import org.opendaylight.vtn.manager.flow.action.SetTpDstAction;
+import org.opendaylight.vtn.manager.flow.action.SetTpSrcAction;
+import org.opendaylight.vtn.manager.flow.action.SetVlanPcpAction;
+
+import org.opendaylight.vtn.manager.TestBase;
+
+/**
+ * JUnit test for {@link FlowFilter}.
+ */
+public class FlowFilterTest extends TestBase {
+    /**
+     * A list of {@link FilterType} instances for test.
+     */
+    private List<FilterType>  filterTypes;
+
+    /**
+     * A list of {@link FlowAction} instance lists for test.
+     */
+    private List<List<FlowAction>>  actionLists;
+
+    /**
+     * Test case for getter methods.
+     */
+    @Test
+    public void testGetter() {
+        int[] indices = {1, 100, 1000, 65535};
+        for (int index: indices) {
+            for (String cond: createStrings("cnd")) {
+                for (FilterType type: createFilterTypes()) {
+                    for (List<FlowAction> actions: createActionLists()) {
+                        FlowFilter ff = new FlowFilter(cond, type, actions);
+                        List<FlowAction> exacts = actions;
+                        if (exacts != null && exacts.isEmpty()) {
+                            exacts = null;
+                        }
+                        assertEquals(null, ff.getIndex());
+                        assertEquals(cond, ff.getFlowConditionName());
+                        assertEquals(type, ff.getFilterType());
+                        assertEquals(exacts, ff.getActions());
+
+                        ff = new FlowFilter(index, cond, type, actions);
+                        assertEquals(Integer.valueOf(index), ff.getIndex());
+                        assertEquals(cond, ff.getFlowConditionName());
+                        assertEquals(type, ff.getFilterType());
+
+                        List<FlowAction> list = ff.getActions();
+                        assertEquals(exacts, list);
+
+                        // Ensure that a copy of action list is returned.
+                        if (list != null) {
+                            list.clear();
+                            assertEquals(exacts, ff.getActions());
+                        }
+                    }
+                }
+            }
+        }
+    }
+
+    /**
+     * Test case for {@link FlowFilter#equals(Object)} and
+     * {@link FlowFilter#hashCode()}.
+     */
+    @Test
+    public void testEquals() {
+        HashSet<Object> set = new HashSet<Object>();
+        int[] indices = {0, 1, 65535};
+        List<String> conditions = createStrings("cnd");
+        List<FilterType> types = createFilterTypes();
+        List<List<FlowAction>> actLists = createActionLists();
+        for (Iterator<List<FlowAction>> it = actLists.iterator();
+             it.hasNext();) {
+            List<FlowAction> act = it.next();
+            if (act == null) {
+                it.remove();
+                break;
+            }
+        }
+
+        for (int index: indices) {
+            for (String cond: conditions) {
+                for (FilterType type: types) {
+                    for (List<FlowAction> actions: actLists) {
+                        FlowFilter ff1, ff2;
+
+                        if (index == 0) {
+                            ff1 = new FlowFilter(cond, type, actions);
+                            ff2 = new FlowFilter(copy(cond), copy(type),
+                                                 copy(actions));
+                        } else {
+                            ff1 = new FlowFilter(index, cond, type, actions);
+                            ff2 = new FlowFilter(index, copy(cond), copy(type),
+                                                 copy(actions));
+                        }
+                        testEquals(set, ff1, ff2);
+                    }
+                }
+            }
+        }
+
+        int required = indices.length * conditions.size() * types.size() *
+            actLists.size();
+        assertEquals(required, set.size());
+    }
+
+    /**
+     * Test case for {@link FlowFilter#toString()}.
+     */
+    @Test
+    public void testToString() {
+        String prefix = "FlowFilter[";
+        String suffix = "]";
+        int[] indices = {0, 1, 1000, 65535};
+        for (int index: indices) {
+            for (String cond: createStrings("cnd")) {
+                for (FilterType type: createFilterTypes()) {
+                    for (List<FlowAction> actions: createActionLists()) {
+                        FlowFilter ff = (index == 0)
+                            ? new FlowFilter(cond, type, actions)
+                            : new FlowFilter(index, cond, type, actions);
+                        String i = (index == 0)
+                            ? null : "index=" + index;
+                        String c = (cond == null) ? null : "cond=" + cond;
+                        String t = (type == null) ? null : "type=" + type;
+                        String a = (actions == null || actions.isEmpty())
+                            ? null : "actions=" + actions;
+                        String required = joinStrings(prefix, suffix, ",",
+                                                      i, c, t, a);
+                        assertEquals(required, ff.toString());
+                    }
+                }
+            }
+        }
+    }
+
+    /**
+     * Ensure that {@link FlowFilter} is serializable.
+     */
+    @Test
+    public void testSerialize() {
+        int[] indices = {0, 100, 65535};
+        for (int index: indices) {
+            for (String cond: createStrings("cnd")) {
+                for (FilterType type: createFilterTypes()) {
+                    for (List<FlowAction> actions: createActionLists()) {
+                        FlowFilter ff = (index == 0)
+                            ? new FlowFilter(cond, type, actions)
+                            : new FlowFilter(index, cond, type, actions);
+                        serializeTest(ff);
+                    }
+                }
+            }
+        }
+    }
+
+    /**
+     * Ensure that {@link FlowFilter} is mapped to XML root element.
+     */
+    @Test
+    public void testJAXB() {
+        int[] indices = {0, 100, 65535};
+        for (int index: indices) {
+            for (String cond: createStrings("cnd")) {
+                for (FilterType type: createFilterTypes()) {
+                    for (List<FlowAction> actions: createActionLists()) {
+                        FlowFilter ff = (index == 0)
+                            ? new FlowFilter(cond, type, actions)
+                            : new FlowFilter(index, cond, type, actions);
+                        jaxbTest(ff, "flowfilter");
+                    }
+                }
+            }
+        }
+    }
+
+    /**
+     * Ensure that {@link FlowFilter} is mapped to JSON object.
+     */
+    @Test
+    public void testJSON() {
+        int[] indices = {0, 100, 65535};
+        for (int index: indices) {
+            for (String cond: createStrings("cnd")) {
+                for (FilterType type: createFilterTypes()) {
+                    for (List<FlowAction> actions: createActionLists()) {
+                        FlowFilter ff = (index == 0)
+                            ? new FlowFilter(cond, type, actions)
+                            : new FlowFilter(index, cond, type, actions);
+                        jsonTest(ff);
+                    }
+                }
+            }
+        }
+    }
+
+    /**
+     * Create a list of {@link FilterType} instances.
+     *
+     * @return  A list of {@link FilterType} instances.
+     */
+    private List<FilterType> createFilterTypes() {
+        List<FilterType> list = filterTypes;
+        if (list == null) {
+            list = new ArrayList<FilterType>();
+            list.add(null);
+            list.add(new PassFilter());
+            list.add(new DropFilter());
+            list.add(new RedirectFilter(
+                         new VBridgeIfPath(null, "bname", "if1"), true));
+            list.add(new RedirectFilter(
+                         new VTerminalIfPath("vtn", "vtname", "if2"), false));
+            filterTypes = list;
+        }
+
+        return list;
+    }
+
+    /**
+     * Create lists of {@link FlowAction} instances.
+     *
+     * @return  A list of {@link FlowAction} instance lists.
+     */
+    private List<List<FlowAction>> createActionLists() {
+        List<List<FlowAction>> list = actionLists;
+        if (list == null) {
+            list = new ArrayList<List<FlowAction>>();
+            byte[] mac1 = {
+                (byte)0x00, (byte)0x01, (byte)0x02,
+                (byte)0x03, (byte)0x04, (byte)0x05,
+            };
+            byte[] mac2 = {
+                (byte)0x10, (byte)0x20, (byte)0x30,
+                (byte)0x40, (byte)0x50, (byte)0x60,
+            };
+            byte[] mac3 = {
+                (byte)0xa0, (byte)0xbb, (byte)0xcc,
+                (byte)0xdd, (byte)0xee, (byte)0xff,
+            };
+            byte[] mac4 = {
+                (byte)0xfa, (byte)0xf0, (byte)0xf1,
+                (byte)0xfa, (byte)0xfb, (byte)0xfc,
+            };
+
+            InetAddress addr1, addr2;
+            try {
+                addr1 = InetAddress.getByName("192.168.100.1");
+                addr2 = InetAddress.getByName("192.168.200.123");
+            } catch (Exception e) {
+                unexpected(e);
+                return null;
+            }
+
+            list.add(null);
+            list.add(new ArrayList<FlowAction>());
+            list.add(createActions(new SetDscpAction((byte)63)));
+            list.add(createActions(new SetDlSrcAction(mac1),
+                                   new SetDlDstAction(mac2),
+                                   new SetVlanPcpAction((byte)3)));
+
+            // Create all-in-one list.
+            list.add(createActions(new SetVlanPcpAction((byte)7),
+                                   new SetDscpAction((byte)4),
+                                   new SetTpSrcAction(65535),
+                                   new SetTpDstAction(0),
+                                   new SetDlSrcAction(mac3),
+                                   new SetDlDstAction(mac4),
+                                   new SetIcmpTypeAction((short)255),
+                                   new SetIcmpCodeAction((short)128),
+                                   new SetInet4SrcAction(addr1),
+                                   new SetInet4DstAction(addr2)));
+
+            actionLists = list;
+        }
+
+        return list;
+    }
+
+    /**
+     * Create a list of {@link FlowAction} instances.
+     *
+     * @param actions  An array of {@link FlowAction} instances to be added
+     *                 to the list.
+     * @return  A list of {@link FlowAction} instances.
+     */
+    private List<FlowAction> createActions(FlowAction ... actions) {
+        List<FlowAction> list = new ArrayList<FlowAction>();
+        for (FlowAction act: actions) {
+            list.add(act);
+        }
+
+        return list;
+    }
+}
diff --git a/manager/api/src/test/java/org/opendaylight/vtn/manager/flow/filter/PassFilterTest.java b/manager/api/src/test/java/org/opendaylight/vtn/manager/flow/filter/PassFilterTest.java
new file mode 100644 (file)
index 0000000..71b23c5
--- /dev/null
@@ -0,0 +1,84 @@
+/*
+ * Copyright (c) 2014 NEC Corporation
+ * 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.vtn.manager.flow.filter;
+
+import java.util.HashSet;
+
+import org.junit.Test;
+
+import org.opendaylight.vtn.manager.VTerminalIfPath;
+
+import org.opendaylight.vtn.manager.TestBase;
+
+/**
+ * JUnit test for {@link PassFilter}.
+ */
+public class PassFilterTest extends TestBase {
+    /**
+     * Test case for {@link PassFilter#equals(Object)} and
+     * {@link PassFilter#hashCode()}.
+     */
+    @Test
+    public void testEquals() {
+        HashSet<Object> set = new HashSet<Object>();
+        testEquals(set, new PassFilter(), new PassFilter());
+        for (int i = 0; i < 10; i++) {
+            assertFalse(set.add(new PassFilter()));
+        }
+
+        assertEquals(1, set.size());
+        assertTrue(set.add(new DropFilter()));
+
+        VTerminalIfPath path = new VTerminalIfPath("vtn", "vtm", "if");
+        assertTrue(set.add(new RedirectFilter(path, false)));
+        assertEquals(3, set.size());
+    }
+
+    /**
+     * Test case for {@link PassFilter#toString()}.
+     */
+    @Test
+    public void testToString() {
+        for (int i = 0; i < 10; i++) {
+            PassFilter pf = new PassFilter();
+            assertEquals("PassFilter", pf.toString());
+        }
+    }
+
+    /**
+     * Ensure that {@link PassFilter} is serializable.
+     */
+    @Test
+    public void testSerialize() {
+        for (int i = 0; i < 10; i++) {
+            serializeTest(new PassFilter());
+        }
+    }
+
+    /**
+     * Ensure that {@link PassFilter} is mapped to XML root element.
+     */
+    @Test
+    public void testJAXB() {
+        for (int i = 0; i < 10; i++) {
+            jaxbTest(new PassFilter(), "pass");
+        }
+    }
+
+    /**
+     * Ensure that {@link PassFilter} is mapped to JSON object.
+     */
+    @Test
+    public void testJSON() {
+        for (int i = 0; i < 10; i++) {
+            jsonTest(new PassFilter());
+        }
+    }
+}
diff --git a/manager/api/src/test/java/org/opendaylight/vtn/manager/flow/filter/RedirectFilterTest.java b/manager/api/src/test/java/org/opendaylight/vtn/manager/flow/filter/RedirectFilterTest.java
new file mode 100644 (file)
index 0000000..f240c1c
--- /dev/null
@@ -0,0 +1,229 @@
+/*
+ * Copyright (c) 2014 NEC Corporation
+ * 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.vtn.manager.flow.filter;
+
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+
+import org.junit.Test;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+
+import org.codehaus.jettison.json.JSONObject;
+
+import org.opendaylight.vtn.manager.ErrorVNodePath;
+import org.opendaylight.vtn.manager.VBridgeIfPath;
+import org.opendaylight.vtn.manager.VBridgePath;
+import org.opendaylight.vtn.manager.VInterfacePath;
+import org.opendaylight.vtn.manager.VNodeLocation;
+import org.opendaylight.vtn.manager.VNodePath;
+import org.opendaylight.vtn.manager.VTenantPath;
+import org.opendaylight.vtn.manager.VTerminalIfPath;
+import org.opendaylight.vtn.manager.VTerminalPath;
+
+import org.opendaylight.vtn.manager.TestBase;
+
+/**
+ * JUnit test for {@link RedirectFilter}.
+ */
+public class RedirectFilterTest extends TestBase {
+    /**
+     * A list of {@link VInterfacePath} instances for test.
+     */
+    private List<VInterfacePath>  interfaces;
+
+    /**
+     * Test case for getter methods.
+     */
+    @Test
+    public void testGetter() {
+        boolean[] bools = {true, false};
+        for (VInterfacePath path: createPaths()) {
+            for (boolean output: bools) {
+                RedirectFilter rf = (path instanceof VBridgeIfPath)
+                    ? new RedirectFilter((VBridgeIfPath)path, output)
+                    : new RedirectFilter((VTerminalIfPath)path, output);
+                assertEquals(path, rf.getDestination());
+                assertEquals(output, rf.isOutput());
+            }
+        }
+    }
+
+    /**
+     * Test case for {@link RedirectFilter#equals(Object)} and
+     * {@link RedirectFilter#hashCode()}.
+     */
+    @Test
+    public void testEquals() {
+        HashSet<Object> set = new HashSet<Object>();
+        List<VInterfacePath> paths = createPaths();
+        boolean[] bools = {true, false};
+        for (VInterfacePath path: createPaths()) {
+            for (boolean output: bools) {
+                RedirectFilter rf1 = (path instanceof VBridgeIfPath)
+                    ? new RedirectFilter((VBridgeIfPath)path, output)
+                    : new RedirectFilter((VTerminalIfPath)path, output);
+                VTenantPath dest = (path == null)
+                    ? null
+                    : ((VNodePath)path).clone();
+                RedirectFilter rf2 = (dest instanceof VBridgeIfPath)
+                    ? new RedirectFilter((VBridgeIfPath)dest, output)
+                    : new RedirectFilter((VTerminalIfPath)dest, output);
+                testEquals(set, rf1, rf2);
+            }
+        }
+
+        assertEquals(paths.size() * bools.length, set.size());
+    }
+
+    /**
+     * Test case for {@link RedirectFilter#toString()}.
+     */
+    @Test
+    public void testToString() {
+        String prefix = "RedirectFilter[";
+        String suffix = "]";
+        boolean[] bools = {true, false};
+        for (VInterfacePath path: createPaths()) {
+            for (boolean output: bools) {
+                RedirectFilter rf = (path instanceof VBridgeIfPath)
+                    ? new RedirectFilter((VBridgeIfPath)path, output)
+                    : new RedirectFilter((VTerminalIfPath)path, output);
+                String p = (path == null) ? null : "destination=" + path;
+                String o = "output=" + output;
+                String required = joinStrings(prefix, suffix, ",", p, o);
+                assertEquals(required, rf.toString());
+            }
+        }
+    }
+
+    /**
+     * Ensure that {@link RedirectFilter} is serializable.
+     */
+    @Test
+    public void testSerialize() {
+        boolean[] bools = {true, false};
+        for (VInterfacePath path: createPaths()) {
+            for (boolean output: bools) {
+                RedirectFilter rf = (path instanceof VBridgeIfPath)
+                    ? new RedirectFilter((VBridgeIfPath)path, output)
+                    : new RedirectFilter((VTerminalIfPath)path, output);
+                serializeTest(rf);
+            }
+        }
+    }
+
+    /**
+     * Ensure that {@link RedirectFilter} is mapped to XML root element.
+     */
+    @Test
+    public void testJAXB() {
+        boolean[] bools = {true, false};
+        for (VInterfacePath path: createPaths()) {
+            for (boolean output: bools) {
+                RedirectFilter rf = (path instanceof VBridgeIfPath)
+                    ? new RedirectFilter((VBridgeIfPath)path, output)
+                    : new RedirectFilter((VTerminalIfPath)path, output);
+                jaxbTest(rf, "redirect");
+            }
+        }
+    }
+
+    /**
+     * Ensure that {@link RedirectFilter} is mapped to JSON object.
+     */
+    @Test
+    public void testJSON() {
+        boolean[] bools = {true, false};
+        for (VInterfacePath path: createPaths()) {
+            for (boolean output: bools) {
+                RedirectFilter rf = (path instanceof VBridgeIfPath)
+                    ? new RedirectFilter((VBridgeIfPath)path, output)
+                    : new RedirectFilter((VTerminalIfPath)path, output);
+                jsonTest(rf);
+            }
+        }
+
+        // Error detection test: Virtual node name is not specified.
+        ObjectMapper mapper = getJsonObjectMapper();
+        try {
+            JSONObject json = new JSONObject();
+            JSONObject dest = new JSONObject();
+            dest.put("interface", "if_1");
+            json.put("destination", dest);
+
+            RedirectFilter rf =
+                mapper.readValue(json.toString(), RedirectFilter.class);
+            VInterfacePath path = rf.getDestination();
+            assertEquals(ErrorVNodePath.class, path.getClass());
+            ErrorVNodePath err = (ErrorVNodePath)path;
+            VNodeLocation loc =
+                new VNodeLocation(new VBridgeIfPath(null, null, "if_1"));
+            assertEquals("Unexpected interface location: " + loc,
+                         err.getError());
+        } catch (Exception e) {
+            unexpected(e);
+        }
+
+        // Error detection test: Interface name is not specified.
+        VNodePath[] paths = {
+            new VBridgePath((String)null, "node1"),
+            new VTerminalPath((String)null, "node2"),
+        };
+        for (VNodePath vnpath: paths) {
+            try {
+                JSONObject json = new JSONObject();
+                JSONObject dest = new JSONObject();
+                String key = (vnpath instanceof VBridgePath)
+                    ? "bridge" : "terminal";
+                dest.put(key, vnpath.getTenantNodeName());
+                json.put("destination", dest);
+
+                RedirectFilter rf =
+                    mapper.readValue(json.toString(), RedirectFilter.class);
+                VInterfacePath path = rf.getDestination();
+                assertEquals(ErrorVNodePath.class, path.getClass());
+                ErrorVNodePath err = (ErrorVNodePath)path;
+                VNodeLocation loc = (vnpath instanceof VBridgePath)
+                    ? new VNodeLocation((VBridgePath)vnpath)
+                    : new VNodeLocation((VTerminalPath)vnpath);
+                assertEquals("Interface name is not specified: " + loc,
+                             err.getError());
+            } catch (Exception e) {
+                unexpected(e);
+            }
+        }
+    }
+
+    /**
+     * Create a list of {@link VInterfacePath} instances.
+     *
+     * @return  A list of {@link VInterfacePath} instances.
+     */
+    private List<VInterfacePath> createPaths() {
+        List<VInterfacePath> list = interfaces;
+        if (list == null) {
+            list = new ArrayList<VInterfacePath>();
+            list.add(null);
+            for (String tname: createStrings("tenant")) {
+                for (String bname: createStrings("vbr", false)) {
+                    for (String iname: createStrings("iface", false)) {
+                        list.add(new VBridgeIfPath(tname, bname, iname));
+                        list.add(new VTerminalIfPath(tname, bname, iname));
+                    }
+                }
+            }
+            interfaces = list;
+        }
+
+        return list;
+    }
+}
index 793ec16a7bf0af7da93cc3a634062266eb766010..48eb1cdd8b5488036d10895f56f3b08e2b93ee25 100644 (file)
               org.opendaylight.vtn.manager.flow,
               org.opendaylight.vtn.manager.flow.action,
               org.opendaylight.vtn.manager.flow.cond,
+              org.opendaylight.vtn.manager.flow.filter,
               org.apache.felix.dm,
               org.slf4j,
               org.osgi.framework
index f5977d04d86dcba9d0e05cf527758593cf524a8b..aca609cc50e311adc12986cda9166e89cd6c979b 100644 (file)
@@ -95,6 +95,7 @@
               org.opendaylight.vtn.manager.flow,
               org.opendaylight.vtn.manager.flow.action,
               org.opendaylight.vtn.manager.flow.cond,
+              org.opendaylight.vtn.manager.flow.filter,
               com.fasterxml.jackson.databind.annotation,
               com.sun.jersey.spi.container.servlet,
               javax.ws.rs,
diff --git a/manager/northbound/src/main/java/org/opendaylight/vtn/manager/northbound/FlowFilterList.java b/manager/northbound/src/main/java/org/opendaylight/vtn/manager/northbound/FlowFilterList.java
new file mode 100644 (file)
index 0000000..2e09e21
--- /dev/null
@@ -0,0 +1,142 @@
+/*
+ * Copyright (c) 2014 NEC Corporation
+ * 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.vtn.manager.northbound;
+
+import java.util.List;
+
+import javax.xml.bind.annotation.XmlAccessType;
+import javax.xml.bind.annotation.XmlAccessorType;
+import javax.xml.bind.annotation.XmlElement;
+import javax.xml.bind.annotation.XmlRootElement;
+
+import com.fasterxml.jackson.databind.annotation.JsonSerialize;
+
+import org.opendaylight.vtn.manager.flow.filter.FlowFilter;
+
+/**
+ * {@code FlowFilterList} class describes a list of flow filter information.
+ *
+ * <p>
+ *   This class is used to return a list of flow filter information to
+ *   REST client.
+ * </p>
+ *
+ * <h4>Example JSON</h4>
+ * <pre class="prettyprint lang-json">
+ * {
+ * &nbsp;&nbsp;"flowfilter": [
+ * &nbsp;&nbsp;&nbsp;&nbsp;{
+ * &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"index": 10,
+ * &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"condition": "flowcond_1",
+ * &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"filterType": {
+ * &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"redirect": {
+ * &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"destination": {
+ * &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"bridge": "vbridge_1",
+ * &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"interface": "if_2"
+ * &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;},
+ * &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"output": false
+ * &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}
+ * &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;},
+ * &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"actions": [
+ * &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{"dlsrc": {"address": "f0:f1:f2:f3:f4:f5"}},
+ * &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{"vlanpcp": {"priority": 7}}
+ * &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;]
+ * &nbsp;&nbsp;&nbsp;&nbsp;},
+ * &nbsp;&nbsp;&nbsp;&nbsp;{
+ * &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"index": 20,
+ * &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"condition": "flowcond_2",
+ * &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"filterType": {
+ * &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"pass": {}
+ * &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;},
+ * &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"actions": [
+ * &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{"dscp": {"dscp": 10}}
+ * &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;]
+ * &nbsp;&nbsp;&nbsp;&nbsp;}
+ * &nbsp;&nbsp;]
+ * }</pre>
+ *
+ * @since  Helium
+ */
+@JsonSerialize(include = JsonSerialize.Inclusion.NON_NULL)
+@XmlRootElement(name = "flowfilters")
+@XmlAccessorType(XmlAccessType.NONE)
+public class FlowFilterList {
+    /**
+     * A list of {@link FlowFilter} instances.
+     *
+     * <ul>
+     *   <li>
+     *     This element contains 0 or more {@link FlowFilter} instances
+     *     which represent flow filter information.
+     *   </li>
+     * </ul>
+     */
+    @XmlElement(name = "flowfilter")
+    private List<FlowFilter>  filters;
+
+    /**
+     * Default constructor.
+     */
+    public FlowFilterList() {
+    }
+
+    /**
+     * Construct a list of flow filters.
+     *
+     * @param list  A list of flow filter information.
+     */
+    public FlowFilterList(List<FlowFilter> list) {
+        filters = list;
+    }
+
+    /**
+     * Return a list of flow filter information.
+     *
+     * @return A list of flow filter information.
+     */
+    List<FlowFilter> getFilters() {
+        return filters;
+    }
+
+    /**
+     * Determine whether the given object is identical to this object.
+     *
+     * @param o  An object to be compared.
+     * @return   {@code true} if identical. Otherwise {@code false}.
+     */
+    @Override
+    public boolean equals(Object o) {
+        if (!(o instanceof FlowFilterList)) {
+            return false;
+        }
+
+        List<FlowFilter> list = ((FlowFilterList)o).filters;
+        if (filters == null || filters.isEmpty()) {
+            return (list == null || list.isEmpty());
+        }
+
+        return filters.equals(list);
+    }
+
+    /**
+     * Return the hash code of this object.
+     *
+     * @return  The hash code.
+     */
+    @Override
+    public int hashCode() {
+        int h = 0;
+        if (filters != null && !filters.isEmpty()) {
+            h ^= filters.hashCode();
+        }
+
+        return h;
+    }
+}
diff --git a/manager/northbound/src/main/java/org/opendaylight/vtn/manager/northbound/VBridgeFlowFilterNorthbound.java b/manager/northbound/src/main/java/org/opendaylight/vtn/manager/northbound/VBridgeFlowFilterNorthbound.java
new file mode 100644 (file)
index 0000000..adde70e
--- /dev/null
@@ -0,0 +1,508 @@
+/*
+ * Copyright (c) 2014 NEC Corporation
+ * 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.vtn.manager.northbound;
+
+import static java.net.HttpURLConnection.HTTP_BAD_REQUEST;
+import static java.net.HttpURLConnection.HTTP_CREATED;
+import static java.net.HttpURLConnection.HTTP_INTERNAL_ERROR;
+import static java.net.HttpURLConnection.HTTP_NOT_ACCEPTABLE;
+import static java.net.HttpURLConnection.HTTP_NOT_FOUND;
+import static java.net.HttpURLConnection.HTTP_NO_CONTENT;
+import static java.net.HttpURLConnection.HTTP_OK;
+import static java.net.HttpURLConnection.HTTP_UNAUTHORIZED;
+import static java.net.HttpURLConnection.HTTP_UNAVAILABLE;
+import static java.net.HttpURLConnection.HTTP_UNSUPPORTED_TYPE;
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.DELETE;
+import javax.ws.rs.GET;
+import javax.ws.rs.PUT;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.UriInfo;
+
+import org.codehaus.enunciate.jaxrs.ResponseCode;
+import org.codehaus.enunciate.jaxrs.ResponseHeader;
+import org.codehaus.enunciate.jaxrs.ResponseHeaders;
+import org.codehaus.enunciate.jaxrs.StatusCodes;
+import org.codehaus.enunciate.jaxrs.TypeHint;
+
+import org.opendaylight.vtn.manager.flow.filter.FlowFilter;
+
+import org.opendaylight.controller.sal.authorization.Privilege;
+
+/**
+ * This class provides Northbound REST APIs to handle flow filter in the
+ * vBridge.
+ *
+ * <p>
+ *   Each vBridge has two flow filter lists.
+ * </p>
+ * <dl style="margin-left: 1em;">
+ *   <dt>Input flow filter list
+ *   <dd style="margin-left: 1.5em">
+ *     <p>
+ *       Flow filters in this list are evaluated when a packet is forwarded
+ *       to the vBridge. This list is evaluated at the following instances.
+ *     </p>
+ *     <ul>
+ *       <li>
+ *         When a packet is forwarded from the virtual interface to the
+ *         vBridge.
+ *       </li>
+ *       <li>
+ *         When an incoming packet is mapped to the vBridge by VLAN mapping or
+ *         MAC mapping.
+ *       </li>
+ *     </ul>
+ *
+ *   <dt>Output flow filter list
+ *   <dd style="margin-left: 1.5em">
+ *     <p>
+ *       Flow filters in this list are evaluated when a packet is going to be
+ *       transmitted to the physical network mapped to the vBridge by
+ *       VLAN mapping or MAC mapping.
+ *       Note that this list is not evaluated when a packet is forwarded to
+ *       the virtual interface in the same vBridge.
+ *     </p>
+ * </dl>
+ *
+ * @since Helium
+ */
+@Path("/{containerName}/vtns/{tenantName}/vbridges/{bridgeName}/flowfilters")
+public class VBridgeFlowFilterNorthbound extends VTNNorthBoundBase {
+    /**
+     * Return information about flow filters configured in the specified
+     * vBridge flow filter list.
+     *
+     * @param containerName  The name of the container.
+     * @param tenantName     The name of the VTN.
+     * @param bridgeName     The name of the vBridge.
+     * @param listType
+     *   The type of the flow filter list (case insensitive).
+     *   <dl style="margin-left: 1em;">
+     *     <dt>in
+     *     <dd style="margin-left: 1.5em">
+     *       Indicates the input flow filter list, which is evaluated when a
+     *       packet is forwarded to the vBridge.
+     *
+     *     <dt>out
+     *     <dd style="margin-left: 1.5em">
+     *       Indicates the output flow filter list, which is evaluated when a
+     *       packet is going to be transmitted to the physical network mapped
+     *       to the vBridge.
+     *   </dl>
+     * @return
+     *   <strong>flowfilters</strong> element contains information about the
+     *   vBridge flow filter list specified by the requested URI.
+     */
+    @Path("{listType}")
+    @GET
+    @Produces({MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML})
+    @TypeHint(FlowFilterList.class)
+    @StatusCodes({
+        @ResponseCode(code = HTTP_OK,
+                      condition = "Operation completed successfully."),
+        @ResponseCode(code = HTTP_BAD_REQUEST,
+                      condition = "Invalid value is passed to " +
+                      "<u>{listType}</u>."),
+        @ResponseCode(code = HTTP_UNAUTHORIZED,
+                      condition = "User is not authorized to perform this " +
+                      "operation."),
+        @ResponseCode(code = HTTP_NOT_FOUND,
+                      condition = "<ul>" +
+                      "<li>The specified container does not exist.</li>" +
+                      "<li>The specified VTN does not exist.</li>" +
+                      "<li>The specified vBridge does not exist.</li>" +
+                      "</ul>"),
+        @ResponseCode(code = HTTP_INTERNAL_ERROR,
+                      condition = "Fatal internal error occurred in the " +
+                      "VTN Manager."),
+        @ResponseCode(code = HTTP_UNAVAILABLE,
+                      condition = "One or more of mandatory controller " +
+                      "services, such as the VTN Manager, are unavailable.")})
+    public FlowFilterList getFlowFilters(
+            @PathParam("containerName") String containerName,
+            @PathParam("tenantName") String tenantName,
+            @PathParam("bridgeName") String bridgeName,
+            @PathParam("listType") String listType) {
+        checkPrivilege(containerName, Privilege.READ);
+
+        // REVISIT: Not yet.
+        return null;
+    }
+
+    /**
+     * Delete all flow filters in the specified vBridge flow filter list.
+     *
+     * @param containerName  The name of the container.
+     * @param tenantName     The name of the VTN.
+     * @param bridgeName     The name of the vBridge.
+     * @param listType
+     *   The type of the flow filter list (case insensitive).
+     *   <dl style="margin-left: 1em;">
+     *     <dt>in
+     *     <dd style="margin-left: 1.5em">
+     *       Indicates the input flow filter list, which is evaluated when a
+     *       packet is forwarded to the vBridge.
+     *
+     *     <dt>out
+     *     <dd style="margin-left: 1.5em">
+     *       Indicates the output flow filter list, which is evaluated when a
+     *       packet is going to be transmitted to the physical network mapped
+     *       to the vBridge.
+     *   </dl>
+     * @return Response as dictated by the HTTP Response Status code.
+     */
+    @Path("{listType}")
+    @DELETE
+    @TypeHint(TypeHint.NO_CONTENT.class)
+    @StatusCodes({
+        @ResponseCode(code = HTTP_OK,
+                      condition = "Flow filters were deleted successfully."),
+        @ResponseCode(code = HTTP_NO_CONTENT,
+                      condition = "No flow filter was configured in the " +
+                      "specified vBridge flow filter list."),
+        @ResponseCode(code = HTTP_BAD_REQUEST,
+                      condition = "Invalid value is passed to " +
+                      "<u>{listType}</u>."),
+        @ResponseCode(code = HTTP_UNAUTHORIZED,
+                      condition = "User is not authorized to perform this " +
+                      "operation."),
+        @ResponseCode(code = HTTP_NOT_FOUND,
+                      condition = "<ul>" +
+                      "<li>The specified container does not exist.</li>" +
+                      "<li>The specified VTN does not exist.</li>" +
+                      "<li>The specified vBridge does not exist.</li>" +
+                      "</ul>"),
+        @ResponseCode(code = HTTP_NOT_ACCEPTABLE,
+                      condition = "\"default\" is specified to " +
+                      "<u>{containerName}</u> and a container other than " +
+                      "the default container is present."),
+        @ResponseCode(code = HTTP_INTERNAL_ERROR,
+                      condition = "Fatal internal error occurred in the " +
+                      "VTN Manager."),
+        @ResponseCode(code = HTTP_UNAVAILABLE,
+                      condition = "One or more of mandatory controller " +
+                      "services, such as the VTN Manager, are unavailable.")})
+    public Response deleteFlowFilters(
+            @PathParam("containerName") String containerName,
+            @PathParam("tenantName") String tenantName,
+            @PathParam("bridgeName") String bridgeName,
+            @PathParam("listType") String listType) {
+        checkPrivilege(containerName, Privilege.WRITE);
+
+        // REVISIT: Not yet.
+        return null;
+    }
+
+    /**
+     * Return information about the flow filter specified by the flow filter
+     * index in the vBridge flow filter list.
+     *
+     * @param containerName  The name of the container.
+     * @param tenantName     The name of the VTN.
+     * @param bridgeName     The name of the vBridge.
+     * @param listType
+     *   The type of the flow filter list (case insensitive).
+     *   <dl style="margin-left: 1em;">
+     *     <dt>in
+     *     <dd style="margin-left: 1.5em">
+     *       Indicates the input flow filter list, which is evaluated when a
+     *       packet is forwarded to the vBridge.
+     *
+     *     <dt>out
+     *     <dd style="margin-left: 1.5em">
+     *       Indicates the output flow filter list, which is evaluated when a
+     *       packet is going to be transmitted to the physical network mapped
+     *       to the vBridge.
+     *   </dl>
+     * @param index
+     *   The index value which specifies the flow filter in the vBridge
+     *   flow filter list. A string representation of an integer value must be
+     *   specified.
+     * @return  <strong>flowfilter</strong> element contains information
+     *          about the flow filter specified by the requested URI.
+     */
+    @Path("{listType}/{index}")
+    @GET
+    @Produces({MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML})
+    @TypeHint(FlowFilter.class)
+    @StatusCodes({
+        @ResponseCode(code = HTTP_OK,
+                      condition = "Operation completed successfully."),
+        @ResponseCode(code = HTTP_NO_CONTENT,
+                      condition = "The specified flow filter does not exist " +
+                      "in the specified vBridge."),
+        @ResponseCode(code = HTTP_BAD_REQUEST,
+                      condition = "Invalid value is passed to " +
+                      "<u>{listType}</u>."),
+        @ResponseCode(code = HTTP_UNAUTHORIZED,
+                      condition = "User is not authorized to perform this " +
+                      "operation."),
+        @ResponseCode(code = HTTP_NOT_FOUND,
+                      condition = "<ul>" +
+                      "<li>The specified container does not exist.</li>" +
+                      "<li>The specified VTN does not exist.</li>" +
+                      "<li>The specified vBridge does not exist.</li>" +
+                      "<li>A string passed to <u>{index}</u> can not be " +
+                      "converted into an integer.</li>" +
+                      "</ul>"),
+        @ResponseCode(code = HTTP_INTERNAL_ERROR,
+                      condition = "Fatal internal error occurred in the " +
+                      "VTN Manager."),
+        @ResponseCode(code = HTTP_UNAVAILABLE,
+                      condition = "One or more of mandatory controller " +
+                      "services, such as the VTN Manager, are unavailable.")})
+    public FlowFilter getFlowFilter(
+            @PathParam("containerName") String containerName,
+            @PathParam("tenantName") String tenantName,
+            @PathParam("bridgeName") String bridgeName,
+            @PathParam("listType") String listType,
+            @PathParam("index") int index) {
+        checkPrivilege(containerName, Privilege.READ);
+
+        // REVISIT: Not yet.
+        return null;
+    }
+
+    /**
+     * Create or modify the flow filter specified by the index number in the
+     * vBridge flow filter list.
+     *
+     * <ul>
+     *   <li>
+     *     If the flow filter specified by
+     *     <span style="text-decoration: underline;">{index}</span> does not
+     *     exist in the vBridge flow filter list specified by
+     *     <span style="text-decoration: underline;">{listType}</span>,
+     *     a new flow filter will be associated with
+     *     <span style="text-decoration: underline;">{index}</span> in the
+     *     specified vBridge flow filter list.
+     *   </li>
+     *   <li>
+     *     If the flow filter specified by
+     *     <span style="text-decoration: underline;">{index}</span> already
+     *     exists in the vBridge flow filter list specified by
+     *     <span style="text-decoration: underline;">{listType}</span>,
+     *     it will be modified as specified by <strong>flowfilter</strong>
+     *     element.
+     *   </li>
+     * </ul>
+     *
+     * @param uriInfo        Requested URI information.
+     * @param containerName  The name of the container.
+     * @param tenantName     The name of the VTN.
+     * @param bridgeName     The name of the vBridge.
+     * @param listType
+     *   The type of the flow filter list (case insensitive).
+     *   <dl style="margin-left: 1em;">
+     *     <dt>in
+     *     <dd style="margin-left: 1.5em">
+     *       Indicates the input flow filter list, which is evaluated when a
+     *       packet is forwarded to the vBridge.
+     *
+     *     <dt>out
+     *     <dd style="margin-left: 1.5em">
+     *       Indicates the output flow filter list, which is evaluated when a
+     *       packet is going to be transmitted to the physical network mapped
+     *       to the vBridge.
+     *   </dl>
+     * @param index
+     *   The index value which specifies the flow filter in the vBridge
+     *   flow filter list.
+     *   <ul>
+     *     <li>
+     *       A string representation of an integer value must be specified.
+     *     </li>
+     *     <li>
+     *       The range of value that can be specified is from
+     *       <strong>1</strong> to <strong>65535</strong>.
+     *     </li>
+     *     <li>
+     *       This value is also used to determine the order of flow filter
+     *       evaluation. Flow filters in the list are evaluated in ascending
+     *       order of indices, and only the first matched flow filter is
+     *       applied to the packet.
+     *     </li>
+     *   </ul>
+     * @param ff
+     *   <strong>flowfilter</strong> element specifies the configuration of the
+     *   flow filter.
+     *   <ul>
+     *     <li>
+     *       The <strong>index</strong> attribute in the
+     *       <strong>flowfilter</strong> element is always ignored.
+     *       The index number is determined by the
+     *       <span style="text-decoration: underline;">{index}</span>
+     *       parameter.
+     *     </li>
+     *     <li>
+     *       Note that this API does not check whether the flow condition
+     *       specified by the <strong>condition</strong> attribute in
+     *       the <strong>flowfilter</strong> element actually exists or not.
+     *       The flow filter will be invalidated if the specified
+     *       flow condition does not exist.
+     *     </li>
+     *     <li>
+     *       Note that this API does not check whether the destination
+     *       virtual interface of the packet redirection, which is configured
+     *       in the <strong>redirect</strong> element in the
+     *       <strong>flowfilter</strong> element, actually exists or not.
+     *       The packet will be discarded if the destination virtual interface
+     *       is not found when the packet is going to be redirected by the
+     *       flow filter.
+     *     </li>
+     *   </ul>
+     * @return Response as dictated by the HTTP Response Status code.
+     */
+    @Path("{listType}/{index}")
+    @PUT
+    @Consumes({MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML})
+    @TypeHint(TypeHint.NO_CONTENT.class)
+    @ResponseHeaders({
+        @ResponseHeader(name = "Location",
+                        description = "URI corresponding to the newly " +
+                        "created flow filter, which is the same URI " +
+                        "specified in request. This header is set only if " +
+                        "CREATED(201) is returned as response code.")})
+    @StatusCodes({
+        @ResponseCode(code = HTTP_OK,
+                      condition = "Existing flow filter was modified " +
+                      "successfully."),
+        @ResponseCode(code = HTTP_CREATED,
+                      condition = "Flow filter was newly created " +
+                      "successfully."),
+        @ResponseCode(code = HTTP_NO_CONTENT,
+                      condition = "Flow filter was not changed."),
+        @ResponseCode(code = HTTP_BAD_REQUEST,
+                      condition = "<ul>" +
+                      "<li>Incorrect XML or JSON data is specified " +
+                      "in Request body.</li>" +
+                      "<li>Invalid value is passed to <u>{listType}</u>." +
+                      "<li>Index number specified by <u>{index}</u> " +
+                      "parameter is out of valid range.</li>" +
+                      "<li>Incorrect value is configured in " +
+                      "<strong>flowfilter</strong>.</li>" +
+                      "</ul>"),
+        @ResponseCode(code = HTTP_UNAUTHORIZED,
+                      condition = "User is not authorized to perform this " +
+                      "operation."),
+        @ResponseCode(code = HTTP_NOT_FOUND,
+                      condition = "<ul>" +
+                      "<li>The specified container does not exist.</li>" +
+                      "<li>The specified VTN does not exist.</li>" +
+                      "<li>The specified vBridge does not exist.</li>" +
+                      "<li>A string passed to <u>{index}</u> can not be " +
+                      "converted into an integer.</li>" +
+                      "</ul>"),
+        @ResponseCode(code = HTTP_NOT_ACCEPTABLE,
+                      condition = "\"default\" is specified to " +
+                      "<u>{containerName}</u> and a container other than " +
+                      "the default container is present."),
+        @ResponseCode(code = HTTP_UNSUPPORTED_TYPE,
+                      condition = "Unsupported data type is specified in " +
+                      "<strong>Content-Type</strong> header."),
+        @ResponseCode(code = HTTP_INTERNAL_ERROR,
+                      condition = "Fatal internal error occurred in the " +
+                      "VTN Manager."),
+        @ResponseCode(code = HTTP_UNAVAILABLE,
+                      condition = "One or more of mandatory controller " +
+                      "services, such as the VTN Manager, are unavailable.")})
+    public Response putFlowFilter(
+            @Context UriInfo uriInfo,
+            @PathParam("containerName") String containerName,
+            @PathParam("tenantName") String tenantName,
+            @PathParam("bridgeName") String bridgeName,
+            @PathParam("listType") String listType,
+            @PathParam("index") int index,
+            @TypeHint(FlowFilter.class) FlowFilter ff) {
+        checkPrivilege(containerName, Privilege.WRITE);
+
+        // REVISIT: Not yet.
+        return null;
+    }
+
+    /**
+     * Delete the flow filter specified by the index number in the
+     * vBridge flow filter list.
+     *
+     * @param containerName  The name of the container.
+     * @param tenantName     The name of the VTN.
+     * @param bridgeName     The name of the vBridge.
+     * @param listType
+     *   The type of the flow filter list (case insensitive).
+     *   <dl style="margin-left: 1em;">
+     *     <dt>in
+     *     <dd style="margin-left: 1.5em">
+     *       Indicates the input flow filter list, which is evaluated when a
+     *       packet is forwarded to the vBridge.
+     *
+     *     <dt>out
+     *     <dd style="margin-left: 1.5em">
+     *       Indicates the output flow filter list, which is evaluated when a
+     *       packet is going to be transmitted to the physical network mapped
+     *       to the vBridge.
+     *   </dl>
+     * @param index
+     *   The index value which specifies the flow filter in the vBridge
+     *   flow filter list. A string representation of an integer value must be
+     *   specified.
+     * @return Response as dictated by the HTTP Response Status code.
+     */
+    @Path("{listType}/{index}")
+    @DELETE
+    @TypeHint(TypeHint.NO_CONTENT.class)
+    @StatusCodes({
+        @ResponseCode(code = HTTP_OK,
+                      condition = "Flow filter was deleted successfully."),
+        @ResponseCode(code = HTTP_NO_CONTENT,
+                      condition = "The specified flow filter does not exist " +
+                      "in the specified vBridge."),
+        @ResponseCode(code = HTTP_BAD_REQUEST,
+                      condition = "Invalid value is passed to " +
+                      "<u>{listType}</u>."),
+        @ResponseCode(code = HTTP_UNAUTHORIZED,
+                      condition = "User is not authorized to perform this " +
+                      "operation."),
+        @ResponseCode(code = HTTP_NOT_FOUND,
+                      condition = "<ul>" +
+                      "<li>The specified container does not exist.</li>" +
+                      "<li>The specified VTN does not exist.</li>" +
+                      "<li>The specified vBridge does not exist.</li>" +
+                      "<li>A string passed to <u>{index}</u> can not be " +
+                      "converted into an integer.</li>" +
+                      "</ul>"),
+        @ResponseCode(code = HTTP_NOT_ACCEPTABLE,
+                      condition = "\"default\" is specified to " +
+                      "<u>{containerName}</u> and a container other than " +
+                      "the default container is present."),
+        @ResponseCode(code = HTTP_INTERNAL_ERROR,
+                      condition = "Fatal internal error occurred in the " +
+                      "VTN Manager."),
+        @ResponseCode(code = HTTP_UNAVAILABLE,
+                      condition = "One or more of mandatory controller " +
+                      "services, such as the VTN Manager, are unavailable.")})
+    public Response deleteFlowFilter(
+            @PathParam("containerName") String containerName,
+            @PathParam("tenantName") String tenantName,
+            @PathParam("bridgeName") String bridgeName,
+            @PathParam("listType") String listType,
+            @PathParam("index") int index) {
+        checkPrivilege(containerName, Privilege.WRITE);
+
+        // REVISIT: Not yet.
+        return null;
+    }
+}
diff --git a/manager/northbound/src/main/java/org/opendaylight/vtn/manager/northbound/VBridgeIfFlowFilterNorthbound.java b/manager/northbound/src/main/java/org/opendaylight/vtn/manager/northbound/VBridgeIfFlowFilterNorthbound.java
new file mode 100644 (file)
index 0000000..e6afe80
--- /dev/null
@@ -0,0 +1,538 @@
+/*
+ * Copyright (c) 2014 NEC Corporation
+ * 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.vtn.manager.northbound;
+
+import static java.net.HttpURLConnection.HTTP_BAD_REQUEST;
+import static java.net.HttpURLConnection.HTTP_CREATED;
+import static java.net.HttpURLConnection.HTTP_INTERNAL_ERROR;
+import static java.net.HttpURLConnection.HTTP_NOT_ACCEPTABLE;
+import static java.net.HttpURLConnection.HTTP_NOT_FOUND;
+import static java.net.HttpURLConnection.HTTP_NO_CONTENT;
+import static java.net.HttpURLConnection.HTTP_OK;
+import static java.net.HttpURLConnection.HTTP_UNAUTHORIZED;
+import static java.net.HttpURLConnection.HTTP_UNAVAILABLE;
+import static java.net.HttpURLConnection.HTTP_UNSUPPORTED_TYPE;
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.DELETE;
+import javax.ws.rs.GET;
+import javax.ws.rs.PUT;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.UriInfo;
+
+import org.codehaus.enunciate.jaxrs.ResponseCode;
+import org.codehaus.enunciate.jaxrs.ResponseHeader;
+import org.codehaus.enunciate.jaxrs.ResponseHeaders;
+import org.codehaus.enunciate.jaxrs.StatusCodes;
+import org.codehaus.enunciate.jaxrs.TypeHint;
+
+import org.opendaylight.vtn.manager.flow.filter.FlowFilter;
+
+import org.opendaylight.controller.sal.authorization.Privilege;
+
+/**
+ * This class provides Northbound REST APIs to handle flow filter in the
+ * vBridge interface.
+ *
+ * <p>
+ *   Each vBridge interface has two flow filter lists.
+ * </p>
+ * <dl style="margin-left: 1em;">
+ *   <dt>Input flow filter list
+ *   <dd style="margin-left: 1.5em">
+ *     <p>
+ *       Flow filters in this list are evaluated when a packet is forwarded to
+ *       the vBridge interface. This list is evaluated at the following
+ *       instances.
+ *     </p>
+ *     <ul>
+ *       <li>
+ *         When an incoming packet is mapped to the vBridge interface by
+ *         port mapping.
+ *       </li>
+ *       <li>
+ *         When a packet is redirected by another flow filter to the
+ *         vBridge interface as an incoming packet.
+ *       </li>
+ *     </ul>
+ *
+ *   <dt>Output flow filter list
+ *   <dd style="margin-left: 1.5em">
+ *     <p>
+ *       Flow filters in this list are evaluated when a packet is going to be
+ *       transmitted to the physical network mapped to the vBridge interface.
+ *       This list is evaluated at the following instances.
+ *     </p>
+ *     <ul>
+ *       <li>
+ *         When a packet is forwarded from the vBridge to the virtual
+ *         interface.
+ *       </li>
+ *       <li>
+ *         When a packet is redirected by another flow filter to the
+ *         vBridge interface as an outgoing packet.
+ *       </li>
+ *     </ul>
+ * </dl>
+ *
+ * @since Helium
+ */
+@Path("/{containerName}/vtns/{tenantName}/vbridges/{bridgeName}/interfaces/{ifName}/flowfilters")
+public class VBridgeIfFlowFilterNorthbound extends VTNNorthBoundBase {
+    /**
+     * Return information about flow filters configured in the specified
+     * vBridge interface.
+     *
+     * @param containerName  The name of the container.
+     * @param tenantName     The name of the VTN.
+     * @param bridgeName     The name of the vBridge.
+     * @param ifName         The name of the vBridge interface.
+     * @param listType
+     *   The type of the flow filter list (case insensitive).
+     *   <dl style="margin-left: 1em;">
+     *     <dt>in
+     *     <dd style="margin-left: 1.5em">
+     *       Indicates the input flow filter list, which is evaluated when a
+     *       packet is forwarded to the vBridge interface.
+     *
+     *     <dt>out
+     *     <dd style="margin-left: 1.5em">
+     *       Indicates the output flow filter list, which is evaluated when a
+     *       packet is going to be transmitted to the physical network mapped
+     *       to the vBridge interface.
+     *   </dl>
+     * @return
+     *   <strong>flowfilters</strong> element contains information about the
+     *   vBridge interface flow filter list specified by the requested URI.
+     */
+    @Path("{listType}")
+    @GET
+    @Produces({MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML})
+    @TypeHint(FlowFilterList.class)
+    @StatusCodes({
+        @ResponseCode(code = HTTP_OK,
+                      condition = "Operation completed successfully."),
+        @ResponseCode(code = HTTP_BAD_REQUEST,
+                      condition = "Invalid value is passed to " +
+                      "<u>{listType}</u>."),
+        @ResponseCode(code = HTTP_UNAUTHORIZED,
+                      condition = "User is not authorized to perform this " +
+                      "operation."),
+        @ResponseCode(code = HTTP_NOT_FOUND,
+                      condition = "<ul>" +
+                      "<li>The specified container does not exist.</li>" +
+                      "<li>The specified VTN does not exist.</li>" +
+                      "<li>The specified vBridge does not exist.</li>" +
+                      "<li>The specified vBridge interface does not " +
+                      "exist.</li>" +
+                      "</ul>"),
+        @ResponseCode(code = HTTP_INTERNAL_ERROR,
+                      condition = "Fatal internal error occurred in the " +
+                      "VTN Manager."),
+        @ResponseCode(code = HTTP_UNAVAILABLE,
+                      condition = "One or more of mandatory controller " +
+                      "services, such as the VTN Manager, are unavailable.")})
+    public FlowFilterList getFlowFilters(
+            @PathParam("containerName") String containerName,
+            @PathParam("tenantName") String tenantName,
+            @PathParam("bridgeName") String bridgeName,
+            @PathParam("ifName") String ifName,
+            @PathParam("listType") String listType) {
+        checkPrivilege(containerName, Privilege.READ);
+
+        // REVISIT: Not yet.
+        return null;
+    }
+
+    /**
+     * Delete all flow filters in the specified vBridge interface flow filter
+     * list.
+     *
+     * @param containerName  The name of the container.
+     * @param tenantName     The name of the VTN.
+     * @param bridgeName     The name of the vBridge.
+     * @param ifName         The name of the vBridge interface.
+     * @param listType
+     *   The type of the flow filter list (case insensitive).
+     *   <dl style="margin-left: 1em;">
+     *     <dt>in
+     *     <dd style="margin-left: 1.5em">
+     *       Indicates the input flow filter list, which is evaluated when a
+     *       packet is forwarded to the vBridge interface.
+     *
+     *     <dt>out
+     *     <dd style="margin-left: 1.5em">
+     *       Indicates the output flow filter list, which is evaluated when a
+     *       packet is going to be transmitted to the physical network mapped
+     *       to the vBridge interface.
+     *   </dl>
+     * @return Response as dictated by the HTTP Response Status code.
+     */
+    @Path("{listType}")
+    @DELETE
+    @TypeHint(TypeHint.NO_CONTENT.class)
+    @StatusCodes({
+        @ResponseCode(code = HTTP_OK,
+                      condition = "Flow filters were deleted successfully."),
+        @ResponseCode(code = HTTP_NO_CONTENT,
+                      condition = "No flow filter was configured in the " +
+                      "specified vBridge interface flow filter list."),
+        @ResponseCode(code = HTTP_BAD_REQUEST,
+                      condition = "Invalid value is passed to " +
+                      "<u>{listType}</u>."),
+        @ResponseCode(code = HTTP_UNAUTHORIZED,
+                      condition = "User is not authorized to perform this " +
+                      "operation."),
+        @ResponseCode(code = HTTP_NOT_FOUND,
+                      condition = "<ul>" +
+                      "<li>The specified container does not exist.</li>" +
+                      "<li>The specified VTN does not exist.</li>" +
+                      "<li>The specified vBridge does not exist.</li>" +
+                      "<li>The specified vBridge interface does not " +
+                      "exist.</li>" +
+                      "</ul>"),
+        @ResponseCode(code = HTTP_NOT_ACCEPTABLE,
+                      condition = "\"default\" is specified to " +
+                      "<u>{containerName}</u> and a container other than " +
+                      "the default container is present."),
+        @ResponseCode(code = HTTP_INTERNAL_ERROR,
+                      condition = "Fatal internal error occurred in the " +
+                      "VTN Manager."),
+        @ResponseCode(code = HTTP_UNAVAILABLE,
+                      condition = "One or more of mandatory controller " +
+                      "services, such as the VTN Manager, are unavailable.")})
+    public Response deleteFlowFilters(
+            @PathParam("containerName") String containerName,
+            @PathParam("tenantName") String tenantName,
+            @PathParam("bridgeName") String bridgeName,
+            @PathParam("ifName") String ifName,
+            @PathParam("listType") String listType) {
+        checkPrivilege(containerName, Privilege.WRITE);
+
+        // REVISIT: Not yet.
+        return null;
+    }
+
+    /**
+     * Return information about the flow filter specified by
+     * the flow filter index in the vBridge interface flow filter list.
+     *
+     * @param containerName  The name of the container.
+     * @param tenantName     The name of the VTN.
+     * @param bridgeName     The name of the vBridge.
+     * @param ifName         The name of the vBridge interface.
+     * @param listType
+     *   The type of the flow filter list (case insensitive).
+     *   <dl style="margin-left: 1em;">
+     *     <dt>in
+     *     <dd style="margin-left: 1.5em">
+     *       Indicates the input flow filter list, which is evaluated when a
+     *       packet is forwarded to the vBridge interface.
+     *
+     *     <dt>out
+     *     <dd style="margin-left: 1.5em">
+     *       Indicates the output flow filter list, which is evaluated when a
+     *       packet is going to be transmitted to the physical network mapped
+     *       to the vBridge interface.
+     *   </dl>
+     * @param index
+     *   The index value which specifies the flow filter in the vBridge
+     *   interface flow filter list. A string representation of an integer
+     *   value must be specified.
+     * @return  <strong>flowfilter</strong> element contains information
+     *          about the flow filter specified by the requested URI.
+     */
+    @Path("{listType}/{index}")
+    @GET
+    @Produces({MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML})
+    @TypeHint(FlowFilter.class)
+    @StatusCodes({
+        @ResponseCode(code = HTTP_OK,
+                      condition = "Operation completed successfully."),
+        @ResponseCode(code = HTTP_NO_CONTENT,
+                      condition = "The specified flow filter does not exist " +
+                      "in the specified vBridge."),
+        @ResponseCode(code = HTTP_BAD_REQUEST,
+                      condition = "Invalid value is passed to " +
+                      "<u>{listType}</u>."),
+        @ResponseCode(code = HTTP_UNAUTHORIZED,
+                      condition = "User is not authorized to perform this " +
+                      "operation."),
+        @ResponseCode(code = HTTP_NOT_FOUND,
+                      condition = "<ul>" +
+                      "<li>The specified container does not exist.</li>" +
+                      "<li>The specified VTN does not exist.</li>" +
+                      "<li>The specified vBridge does not exist.</li>" +
+                      "<li>The specified vBridge interface does not " +
+                      "exist.</li>" +
+                      "<li>A string passed to <u>{index}</u> can not be " +
+                      "converted into an integer.</li>" +
+                      "</ul>"),
+        @ResponseCode(code = HTTP_INTERNAL_ERROR,
+                      condition = "Fatal internal error occurred in the " +
+                      "VTN Manager."),
+        @ResponseCode(code = HTTP_UNAVAILABLE,
+                      condition = "One or more of mandatory controller " +
+                      "services, such as the VTN Manager, are unavailable.")})
+    public FlowFilter getFlowFilter(
+            @PathParam("containerName") String containerName,
+            @PathParam("tenantName") String tenantName,
+            @PathParam("bridgeName") String bridgeName,
+            @PathParam("ifName") String ifName,
+            @PathParam("listType") String listType,
+            @PathParam("index") int index) {
+        checkPrivilege(containerName, Privilege.READ);
+
+        // REVISIT: Not yet.
+        return null;
+    }
+
+    /**
+     * Create or modify the flow filter specified by the index number in the
+     * vBridge interface flow filter list.
+     *
+     * <ul>
+     *   <li>
+     *     If the flow filter specified by
+     *     <span style="text-decoration: underline;">{index}</span> does not
+     *     exist in the vBridge interface flow filter list specified by
+     *     <span style="text-decoration: underline;">{listType}</span>,
+     *     a new flow filter will be associated with
+     *     <span style="text-decoration: underline;">{index}</span> in the
+     *     specified vBridge interface flow filter list.
+     *   </li>
+     *   <li>
+     *     If the flow filter specified by
+     *     <span style="text-decoration: underline;">{index}</span> already
+     *     exists in the vBridge interface flow filter list specified by
+     *     <span style="text-decoration: underline;">{listType}</span>,
+     *     it will be modified as specified by <strong>flowfilter</strong>
+     *     element.
+     *   </li>
+     * </ul>
+     *
+     * @param uriInfo        Requested URI information.
+     * @param containerName  The name of the container.
+     * @param tenantName     The name of the VTN.
+     * @param bridgeName     The name of the vBridge.
+     * @param ifName         The name of the vBridge interface.
+     * @param listType
+     *   The type of the flow filter list (case insensitive).
+     *   <dl style="margin-left: 1em;">
+     *     <dt>in
+     *     <dd style="margin-left: 1.5em">
+     *       Indicates the input flow filter list, which is evaluated when a
+     *       packet is forwarded to the vBridge interface.
+     *
+     *     <dt>out
+     *     <dd style="margin-left: 1.5em">
+     *       Indicates the output flow filter list, which is evaluated when a
+     *       packet is going to be transmitted to the physical network mapped
+     *       to the vBridge interface.
+     *   </dl>
+     * @param index
+     *   The index value which specifies the flow filter in the vBridge
+     *   interface flow filter list.
+     *   <ul>
+     *     <li>
+     *       A string representation of an integer value must be specified.
+     *     </li>
+     *     <li>
+     *       The range of value that can be specified is from
+     *       <strong>1</strong> to <strong>65535</strong>.
+     *     </li>
+     *     <li>
+     *       This value is also used to determine the order of flow filter
+     *       evaluation. Flow filters in the list are evaluated in ascending
+     *       order of indices, and only the first matched flow filter is
+     *       applied to the packet.
+     *     </li>
+     *   </ul>
+     * @param ff
+     *   <strong>flowfilter</strong> element specifies the configuration of the
+     *   flow filter.
+     *   <ul>
+     *     <li>
+     *       The <strong>index</strong> attribute in the
+     *       <strong>flowfilter</strong> element is always ignored.
+     *       The index number is determined by the
+     *       <span style="text-decoration: underline;">{index}</span>
+     *       parameter.
+     *     </li>
+     *     <li>
+     *       Note that this API does not check whether the flow condition
+     *       specified by the <strong>condition</strong> attribute in
+     *       the <strong>flowfilter</strong> element actually exists or not.
+     *       The flow filter will be invalidated if the specified
+     *       flow condition does not exist.
+     *     </li>
+     *     <li>
+     *       Note that this API does not check whether the destination
+     *       virtual interface of the packet redirection, which is configured
+     *       in the <strong>redirect</strong> element in the
+     *       <strong>flowfilter</strong> element, actually exists or not.
+     *       The packet will be discarded if the destination virtual interface
+     *       is not found when the packet is going to be redirected by the
+     *       flow filter.
+     *     </li>
+     *   </ul>
+     * @return Response as dictated by the HTTP Response Status code.
+     */
+    @Path("{listType}/{index}")
+    @PUT
+    @Consumes({MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML})
+    @TypeHint(TypeHint.NO_CONTENT.class)
+    @ResponseHeaders({
+        @ResponseHeader(name = "Location",
+                        description = "URI corresponding to the newly " +
+                        "created flow filter, which is the same URI " +
+                        "specified in request. This header is set only if " +
+                        "CREATED(201) is returned as response code.")})
+    @StatusCodes({
+        @ResponseCode(code = HTTP_OK,
+                      condition = "Existing flow filter was modified " +
+                      "successfully."),
+        @ResponseCode(code = HTTP_CREATED,
+                      condition = "Flow filter was newly created " +
+                      "successfully."),
+        @ResponseCode(code = HTTP_NO_CONTENT,
+                      condition = "Flow filter was not changed."),
+        @ResponseCode(code = HTTP_BAD_REQUEST,
+                      condition = "<ul>" +
+                      "<li>Incorrect XML or JSON data is specified " +
+                      "in Request body.</li>" +
+                      "<li>Invalid value is passed to <u>{listType}</u>." +
+                      "<li>Index number specified by <u>{index}</u> " +
+                      "parameter is out of valid range.</li>" +
+                      "<li>Incorrect value is configured in " +
+                      "<strong>flowfilter</strong>.</li>" +
+                      "</ul>"),
+        @ResponseCode(code = HTTP_UNAUTHORIZED,
+                      condition = "User is not authorized to perform this " +
+                      "operation."),
+        @ResponseCode(code = HTTP_NOT_FOUND,
+                      condition = "<ul>" +
+                      "<li>The specified container does not exist.</li>" +
+                      "<li>The specified VTN does not exist.</li>" +
+                      "<li>The specified vBridge does not exist.</li>" +
+                      "<li>The specified vBridge interface does not " +
+                      "exist.</li>" +
+                      "<li>A string passed to <u>{index}</u> can not be " +
+                      "converted into an integer.</li>" +
+                      "</ul>"),
+        @ResponseCode(code = HTTP_NOT_ACCEPTABLE,
+                      condition = "\"default\" is specified to " +
+                      "<u>{containerName}</u> and a container other than " +
+                      "the default container is present."),
+        @ResponseCode(code = HTTP_UNSUPPORTED_TYPE,
+                      condition = "Unsupported data type is specified in " +
+                      "<strong>Content-Type</strong> header."),
+        @ResponseCode(code = HTTP_INTERNAL_ERROR,
+                      condition = "Fatal internal error occurred in the " +
+                      "VTN Manager."),
+        @ResponseCode(code = HTTP_UNAVAILABLE,
+                      condition = "One or more of mandatory controller " +
+                      "services, such as the VTN Manager, are unavailable.")})
+    public Response putFlowFilter(
+            @Context UriInfo uriInfo,
+            @PathParam("containerName") String containerName,
+            @PathParam("tenantName") String tenantName,
+            @PathParam("bridgeName") String bridgeName,
+            @PathParam("ifName") String ifName,
+            @PathParam("listType") String listType,
+            @PathParam("index") int index,
+            @TypeHint(FlowFilter.class) FlowFilter ff) {
+        checkPrivilege(containerName, Privilege.WRITE);
+
+        // REVISIT: Not yet.
+        return null;
+    }
+
+    /**
+     * Delete the flow filter specified by the index number in the
+     * specified vBridge interface flow filter list.
+     *
+     * @param containerName  The name of the container.
+     * @param tenantName     The name of the VTN.
+     * @param bridgeName     The name of the vBridge.
+     * @param ifName         The name of the vBridge interface.
+     * @param listType
+     *   The type of the flow filter list (case insensitive).
+     *   <dl style="margin-left: 1em;">
+     *     <dt>in
+     *     <dd style="margin-left: 1.5em">
+     *       Indicates the input flow filter list, which is evaluated when a
+     *       packet is forwarded to the vBridge interface.
+     *
+     *     <dt>out
+     *     <dd style="margin-left: 1.5em">
+     *       Indicates the output flow filter list, which is evaluated when a
+     *       packet is going to be transmitted to the physical network mapped
+     *       to the vBridge interface.
+     *   </dl>
+     * @param index
+     *   The index value which specifies the flow filter in the vBridge
+     *   interface flow filter list. A string representation of an integer
+     *   value must be specified.
+     * @return Response as dictated by the HTTP Response Status code.
+     */
+    @Path("{listType}/{index}")
+    @DELETE
+    @TypeHint(TypeHint.NO_CONTENT.class)
+    @StatusCodes({
+        @ResponseCode(code = HTTP_OK,
+                      condition = "Flow filter was deleted successfully."),
+        @ResponseCode(code = HTTP_NO_CONTENT,
+                      condition = "The specified flow filter does not exist " +
+                      "in the specified vBridge."),
+        @ResponseCode(code = HTTP_BAD_REQUEST,
+                      condition = "Invalid value is passed to " +
+                      "<u>{listType}</u>."),
+        @ResponseCode(code = HTTP_UNAUTHORIZED,
+                      condition = "User is not authorized to perform this " +
+                      "operation."),
+        @ResponseCode(code = HTTP_NOT_FOUND,
+                      condition = "<ul>" +
+                      "<li>The specified container does not exist.</li>" +
+                      "<li>The specified VTN does not exist.</li>" +
+                      "<li>The specified vBridge does not exist.</li>" +
+                      "<li>The specified vBridge interface does not " +
+                      "exist.</li>" +
+                      "<li>A string passed to <u>{index}</u> can not be " +
+                      "converted into an integer.</li>" +
+                      "</ul>"),
+        @ResponseCode(code = HTTP_NOT_ACCEPTABLE,
+                      condition = "\"default\" is specified to " +
+                      "<u>{containerName}</u> and a container other than " +
+                      "the default container is present."),
+        @ResponseCode(code = HTTP_INTERNAL_ERROR,
+                      condition = "Fatal internal error occurred in the " +
+                      "VTN Manager."),
+        @ResponseCode(code = HTTP_UNAVAILABLE,
+                      condition = "One or more of mandatory controller " +
+                      "services, such as the VTN Manager, are unavailable.")})
+    public Response deleteFlowFilter(
+            @PathParam("containerName") String containerName,
+            @PathParam("tenantName") String tenantName,
+            @PathParam("bridgeName") String bridgeName,
+            @PathParam("ifName") String ifName,
+            @PathParam("listType") String listType,
+            @PathParam("index") int index) {
+        checkPrivilege(containerName, Privilege.WRITE);
+
+        // REVISIT: Not yet.
+        return null;
+    }
+}
diff --git a/manager/northbound/src/main/java/org/opendaylight/vtn/manager/northbound/VTenantFlowFilterNorthbound.java b/manager/northbound/src/main/java/org/opendaylight/vtn/manager/northbound/VTenantFlowFilterNorthbound.java
new file mode 100644 (file)
index 0000000..67b63e4
--- /dev/null
@@ -0,0 +1,373 @@
+/*
+ * Copyright (c) 2014 NEC Corporation
+ * 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.vtn.manager.northbound;
+
+import static java.net.HttpURLConnection.HTTP_BAD_REQUEST;
+import static java.net.HttpURLConnection.HTTP_CREATED;
+import static java.net.HttpURLConnection.HTTP_INTERNAL_ERROR;
+import static java.net.HttpURLConnection.HTTP_NOT_ACCEPTABLE;
+import static java.net.HttpURLConnection.HTTP_NOT_FOUND;
+import static java.net.HttpURLConnection.HTTP_NO_CONTENT;
+import static java.net.HttpURLConnection.HTTP_OK;
+import static java.net.HttpURLConnection.HTTP_UNAUTHORIZED;
+import static java.net.HttpURLConnection.HTTP_UNAVAILABLE;
+import static java.net.HttpURLConnection.HTTP_UNSUPPORTED_TYPE;
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.DELETE;
+import javax.ws.rs.GET;
+import javax.ws.rs.PUT;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.UriInfo;
+
+import org.codehaus.enunciate.jaxrs.ResponseCode;
+import org.codehaus.enunciate.jaxrs.ResponseHeader;
+import org.codehaus.enunciate.jaxrs.ResponseHeaders;
+import org.codehaus.enunciate.jaxrs.StatusCodes;
+import org.codehaus.enunciate.jaxrs.TypeHint;
+
+import org.opendaylight.vtn.manager.flow.filter.FlowFilter;
+
+import org.opendaylight.controller.sal.authorization.Privilege;
+
+/**
+ * This class provides Northbound REST APIs to handle flow filter in the
+ * VTN.
+ *
+ * <p>
+ *   Each VTN has a list of flow filters. Flow filters in the VTN flow filter
+ *   list are evaluated when an incoming packet is mapped to the VTN.
+ *   Note that the VTN flow filter list is evaluated only once before other
+ *   flow filter lists are evaluated.
+ * </p>
+ *
+ * @since Helium
+ */
+@Path("/{containerName}/vtns/{tenantName}/flowfilters")
+public class VTenantFlowFilterNorthbound extends VTNNorthBoundBase {
+    /**
+     * Return information about flow filters configured in the specified VTN.
+     *
+     * @param containerName  The name of the container.
+     * @param tenantName     The name of the VTN.
+     * @return
+     *   <strong>flowfilters</strong> element contains information about the
+     *   VTN flow filter list specified by the requested URI.
+     */
+    @GET
+    @Produces({MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML})
+    @TypeHint(FlowFilterList.class)
+    @StatusCodes({
+        @ResponseCode(code = HTTP_OK,
+                      condition = "Operation completed successfully."),
+        @ResponseCode(code = HTTP_UNAUTHORIZED,
+                      condition = "User is not authorized to perform this " +
+                      "operation."),
+        @ResponseCode(code = HTTP_NOT_FOUND,
+                      condition = "<ul>" +
+                      "<li>The specified container does not exist.</li>" +
+                      "<li>The specified VTN does not exist.</li>" +
+                      "</ul>"),
+        @ResponseCode(code = HTTP_INTERNAL_ERROR,
+                      condition = "Fatal internal error occurred in the " +
+                      "VTN Manager."),
+        @ResponseCode(code = HTTP_UNAVAILABLE,
+                      condition = "One or more of mandatory controller " +
+                      "services, such as the VTN Manager, are unavailable.")})
+    public FlowFilterList getFlowFilters(
+            @PathParam("containerName") String containerName,
+            @PathParam("tenantName") String tenantName) {
+        checkPrivilege(containerName, Privilege.READ);
+
+        // REVISIT: Not yet.
+        return null;
+    }
+
+
+    /**
+     * Delete all flow filters in the VTN flow filter list.
+     *
+     * @param containerName  The name of the container.
+     * @param tenantName     The name of the VTN.
+     * @return Response as dictated by the HTTP Response Status code.
+     */
+    @DELETE
+    @TypeHint(TypeHint.NO_CONTENT.class)
+    @StatusCodes({
+        @ResponseCode(code = HTTP_OK,
+                      condition = "Flow filters were deleted successfully."),
+        @ResponseCode(code = HTTP_NO_CONTENT,
+                      condition = "No flow filter was configured in the " +
+                      "specified VTN."),
+        @ResponseCode(code = HTTP_UNAUTHORIZED,
+                      condition = "User is not authorized to perform this " +
+                      "operation."),
+        @ResponseCode(code = HTTP_NOT_FOUND,
+                      condition = "<ul>" +
+                      "<li>The specified container does not exist.</li>" +
+                      "<li>The specified VTN does not exist.</li>" +
+                      "</ul>"),
+        @ResponseCode(code = HTTP_NOT_ACCEPTABLE,
+                      condition = "\"default\" is specified to " +
+                      "<u>{containerName}</u> and a container other than " +
+                      "the default container is present."),
+        @ResponseCode(code = HTTP_INTERNAL_ERROR,
+                      condition = "Fatal internal error occurred in the " +
+                      "VTN Manager."),
+        @ResponseCode(code = HTTP_UNAVAILABLE,
+                      condition = "One or more of mandatory controller " +
+                      "services, such as the VTN Manager, are unavailable.")})
+    public Response deleteFlowFilters(
+            @PathParam("containerName") String containerName,
+            @PathParam("tenantName") String tenantName) {
+        checkPrivilege(containerName, Privilege.WRITE);
+
+        // REVISIT: Not yet.
+        return null;
+    }
+
+    /**
+     * Return information about the flow filter specified by the
+     * flow filter index in the VTN flow filter list.
+     *
+     * @param containerName  The name of the container.
+     * @param tenantName     The name of the VTN.
+     * @param index
+     *   The index value which specifies the flow filter in the VTN flow filter
+     *   list. A string representation of an integer value must be specified.
+     * @return
+     *   <strong>flowfilter</strong> element contains information about the
+     *   flow filter specified by the requested URI.
+     */
+    @Path("{index}")
+    @GET
+    @Produces({MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML})
+    @TypeHint(FlowFilter.class)
+    @StatusCodes({
+        @ResponseCode(code = HTTP_OK,
+                      condition = "Operation completed successfully."),
+        @ResponseCode(code = HTTP_NO_CONTENT,
+                      condition = "The specified flow filter does not exist " +
+                      "in the specified VTN."),
+        @ResponseCode(code = HTTP_UNAUTHORIZED,
+                      condition = "User is not authorized to perform this " +
+                      "operation."),
+        @ResponseCode(code = HTTP_NOT_FOUND,
+                      condition = "<ul>" +
+                      "<li>The specified container does not exist.</li>" +
+                      "<li>The specified VTN does not exist.</li>" +
+                      "<li>A string passed to <u>{index}</u> can not be " +
+                      "converted into an integer.</li>" +
+                      "</ul>"),
+        @ResponseCode(code = HTTP_INTERNAL_ERROR,
+                      condition = "Fatal internal error occurred in the " +
+                      "VTN Manager."),
+        @ResponseCode(code = HTTP_UNAVAILABLE,
+                      condition = "One or more of mandatory controller " +
+                      "services, such as the VTN Manager, are unavailable.")})
+    public FlowFilter getFlowFilter(
+            @PathParam("containerName") String containerName,
+            @PathParam("tenantName") String tenantName,
+            @PathParam("index") int index) {
+        checkPrivilege(containerName, Privilege.READ);
+
+        // REVISIT: Not yet.
+        return null;
+    }
+
+    /**
+     * Create or modify the flow filter specified by the index number in the
+     * VTN flow filter list.
+     *
+     * <ul>
+     *   <li>
+     *     If the flow filter specified by
+     *     <span style="text-decoration: underline;">{index}</span> does not
+     *     exist, a new flow filter will be associated with
+     *     <span style="text-decoration: underline;">{index}</span> in the
+     *     VTN flow filter list.
+     *   </li>
+     *   <li>
+     *     If the flow filter specified by
+     *     <span style="text-decoration: underline;">{index}</span> already
+     *     exists, it will be modified as specified by
+     *     <strong>flowfilter</strong> element.
+     *   </li>
+     * </ul>
+     *
+     * @param uriInfo        Requested URI information.
+     * @param containerName  The name of the container.
+     * @param tenantName     The name of the VTN.
+     * @param index
+     *   The index value which specifies the flow filter in the VTN flow filter
+     *   list.
+     *   <ul>
+     *     <li>
+     *       A string representation of an integer value must be specified.
+     *     </li>
+     *     <li>
+     *       The range of value that can be specified is from
+     *       <strong>1</strong> to <strong>65535</strong>.
+     *     </li>
+     *     <li>
+     *       This value is also used to determine the order of flow filter
+     *       evaluation. Flow filters in the list are evaluated in ascending
+     *       order of indices, and only the first matched flow filter is
+     *       applied to the packet.
+     *     </li>
+     *   </ul>
+     * @param ff
+     *   <strong>flowfilter</strong> element specifies the configuration of the
+     *   flow filter.
+     *   <ul>
+     *     <li>
+     *       The <strong>index</strong> attribute in the
+     *       <strong>flowfilter</strong> element is always ignored.
+     *       The index number is determined by the
+     *       <span style="text-decoration: underline;">{index}</span>
+     *       parameter.
+     *     </li>
+     *     <li>
+     *       Note that this API does not check whether the flow condition
+     *       specified by the <strong>condition</strong> attribute in
+     *       the <strong>flowfilter</strong> element actually exists or not.
+     *       The flow filter will be invalidated if the specified
+     *       flow condition does not exist.
+     *     </li>
+     *     <li>
+     *       Note that this API does not check whether the destination
+     *       virtual interface of the packet redirection, which is configured
+     *       in the <strong>redirect</strong> element in the
+     *       <strong>flowfilter</strong> element, actually exists or not.
+     *       The packet will be discarded if the destination virtual interface
+     *       is not found when the packet is going to be redirected by the
+     *       flow filter.
+     *     </li>
+     *   </ul>
+     * @return Response as dictated by the HTTP Response Status code.
+     */
+    @Path("{index}")
+    @PUT
+    @Consumes({MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML})
+    @TypeHint(TypeHint.NO_CONTENT.class)
+    @ResponseHeaders({
+        @ResponseHeader(name = "Location",
+                        description = "URI corresponding to the newly " +
+                        "created flow filter, which is the same URI " +
+                        "specified in request. This header is set only if " +
+                        "CREATED(201) is returned as response code.")})
+    @StatusCodes({
+        @ResponseCode(code = HTTP_OK,
+                      condition = "Existing flow filter was modified " +
+                      "successfully."),
+        @ResponseCode(code = HTTP_CREATED,
+                      condition = "Flow filter was newly created " +
+                      "successfully."),
+        @ResponseCode(code = HTTP_NO_CONTENT,
+                      condition = "Flow filter was not changed."),
+        @ResponseCode(code = HTTP_BAD_REQUEST,
+                      condition = "<ul>" +
+                      "<li>Incorrect XML or JSON data is specified " +
+                      "in Request body.</li>" +
+                      "<li>Index number specified by <u>{index}</u> " +
+                      "parameter is out of valid range.</li>" +
+                      "<li>Incorrect value is configured in " +
+                      "<strong>flowfilter</strong>.</li>" +
+                      "</ul>"),
+        @ResponseCode(code = HTTP_UNAUTHORIZED,
+                      condition = "User is not authorized to perform this " +
+                      "operation."),
+        @ResponseCode(code = HTTP_NOT_FOUND,
+                      condition = "<ul>" +
+                      "<li>The specified container does not exist.</li>" +
+                      "<li>The specified VTN does not exist.</li>" +
+                      "<li>A string passed to <u>{index}</u> can not be " +
+                      "converted into an integer.</li>" +
+                      "</ul>"),
+        @ResponseCode(code = HTTP_NOT_ACCEPTABLE,
+                      condition = "\"default\" is specified to " +
+                      "<u>{containerName}</u> and a container other than " +
+                      "the default container is present."),
+        @ResponseCode(code = HTTP_UNSUPPORTED_TYPE,
+                      condition = "Unsupported data type is specified in " +
+                      "<strong>Content-Type</strong> header."),
+        @ResponseCode(code = HTTP_INTERNAL_ERROR,
+                      condition = "Fatal internal error occurred in the " +
+                      "VTN Manager."),
+        @ResponseCode(code = HTTP_UNAVAILABLE,
+                      condition = "One or more of mandatory controller " +
+                      "services, such as the VTN Manager, are unavailable.")})
+    public Response putFlowFilter(
+            @Context UriInfo uriInfo,
+            @PathParam("containerName") String containerName,
+            @PathParam("tenantName") String tenantName,
+            @PathParam("index") int index,
+            @TypeHint(FlowFilter.class) FlowFilter ff) {
+        checkPrivilege(containerName, Privilege.WRITE);
+
+        // REVISIT: Not yet.
+        return null;
+    }
+
+    /**
+     * Delete the flow filter specified by the index number in the VTN
+     * flow filter list.
+     *
+     * @param containerName  The name of the container.
+     * @param tenantName     The name of the VTN.
+     * @param index
+     *   The index value which specifies the flow filter in the VTN flow filter
+     *   list. A string representation of an integer value must be specified.
+     * @return Response as dictated by the HTTP Response Status code.
+     */
+    @Path("{index}")
+    @DELETE
+    @TypeHint(TypeHint.NO_CONTENT.class)
+    @StatusCodes({
+        @ResponseCode(code = HTTP_OK,
+                      condition = "Flow filter was deleted successfully."),
+        @ResponseCode(code = HTTP_NO_CONTENT,
+                      condition = "The specified flow filter does not exist " +
+                      "in the specified VTN."),
+        @ResponseCode(code = HTTP_UNAUTHORIZED,
+                      condition = "User is not authorized to perform this " +
+                      "operation."),
+        @ResponseCode(code = HTTP_NOT_FOUND,
+                      condition = "<ul>" +
+                      "<li>The specified container does not exist.</li>" +
+                      "<li>The specified VTN does not exist.</li>" +
+                      "<li>A string passed to <u>{index}</u> can not be " +
+                      "converted into an integer.</li>" +
+                      "</ul>"),
+        @ResponseCode(code = HTTP_NOT_ACCEPTABLE,
+                      condition = "\"default\" is specified to " +
+                      "<u>{containerName}</u> and a container other than " +
+                      "the default container is present."),
+        @ResponseCode(code = HTTP_INTERNAL_ERROR,
+                      condition = "Fatal internal error occurred in the " +
+                      "VTN Manager."),
+        @ResponseCode(code = HTTP_UNAVAILABLE,
+                      condition = "One or more of mandatory controller " +
+                      "services, such as the VTN Manager, are unavailable.")})
+    public Response deleteFlowFilter(
+            @PathParam("containerName") String containerName,
+            @PathParam("tenantName") String tenantName,
+            @PathParam("index") int index) {
+        checkPrivilege(containerName, Privilege.WRITE);
+
+        // REVISIT: Not yet.
+        return null;
+    }
+}
diff --git a/manager/northbound/src/main/java/org/opendaylight/vtn/manager/northbound/VTerminalIfFlowFilterNorthbound.java b/manager/northbound/src/main/java/org/opendaylight/vtn/manager/northbound/VTerminalIfFlowFilterNorthbound.java
new file mode 100644 (file)
index 0000000..737913c
--- /dev/null
@@ -0,0 +1,537 @@
+/*
+ * Copyright (c) 2014 NEC Corporation
+ * 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.vtn.manager.northbound;
+
+import static java.net.HttpURLConnection.HTTP_BAD_REQUEST;
+import static java.net.HttpURLConnection.HTTP_CREATED;
+import static java.net.HttpURLConnection.HTTP_INTERNAL_ERROR;
+import static java.net.HttpURLConnection.HTTP_NOT_ACCEPTABLE;
+import static java.net.HttpURLConnection.HTTP_NOT_FOUND;
+import static java.net.HttpURLConnection.HTTP_NO_CONTENT;
+import static java.net.HttpURLConnection.HTTP_OK;
+import static java.net.HttpURLConnection.HTTP_UNAUTHORIZED;
+import static java.net.HttpURLConnection.HTTP_UNAVAILABLE;
+import static java.net.HttpURLConnection.HTTP_UNSUPPORTED_TYPE;
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.DELETE;
+import javax.ws.rs.GET;
+import javax.ws.rs.PUT;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.UriInfo;
+
+import org.codehaus.enunciate.jaxrs.ResponseCode;
+import org.codehaus.enunciate.jaxrs.ResponseHeader;
+import org.codehaus.enunciate.jaxrs.ResponseHeaders;
+import org.codehaus.enunciate.jaxrs.StatusCodes;
+import org.codehaus.enunciate.jaxrs.TypeHint;
+
+import org.opendaylight.vtn.manager.flow.filter.FlowFilter;
+
+import org.opendaylight.controller.sal.authorization.Privilege;
+
+/**
+ * This class provides Northbound REST APIs to handle flow filter in the
+ * vTerminal interface.
+ *
+ * <p>
+ *   Each vTerminal interface has two flow filter lists.
+ * </p>
+ * <dl style="margin-left: 1em;">
+ *   <dt>Input flow filter list
+ *   <dd style="margin-left: 1.5em">
+ *     <p>
+ *       Flow filters in this list are evaluated when a packet is forwarded
+ *       to the vTerminal interface.
+ *       This list is evaluated at the following instances.
+ *     </p>
+ *     <ul>
+ *       <li>
+ *         When an incoming packet is mapped to the vTerminal interface by
+ *         port mapping.
+ *       </li>
+ *       <li>
+ *         When a packet is redirected by another flow filter to the
+ *         vTerminal interface as an incoming packet.
+ *       </li>
+ *     </ul>
+ *     <p>
+ *       vTerminal is an isolated input/output terminal.
+ *       So an incoming packet is always discarded unless it is redirected
+ *       to another virtual interface by the flow filter.
+ *     </p>
+ *
+ *   <dt>Output flow filter list
+ *   <dd style="margin-left: 1.5em">
+ *     <p>
+ *       Flow filters in this list are evaluated when a packet is going to be
+ *       transmitted to the physical network mapped to the vTerminal interface.
+ *       This list is evaluated at the following instances.
+ *     </p>
+ *     <p>
+ *       This list is evaluated only when a packet is redirected by another
+ *       flow filter to the vTerminal interface as an outgoing packet.
+ *     </p>
+ * </dl>
+ *
+ * @since Helium
+ */
+@Path("/{containerName}/vtns/{tenantName}/vterminals/{termName}/interfaces/{ifName}/flowfilters")
+public class VTerminalIfFlowFilterNorthbound extends VTNNorthBoundBase {
+    /**
+     * Return information about flow filters configured in the specified
+     * vTerminal interface.
+     *
+     * @param containerName  The name of the container.
+     * @param tenantName     The name of the VTN.
+     * @param termName       The name of the vTerminal.
+     * @param ifName         The name of the vTerminal interface.
+     * @param listType
+     *   The type of the flow filter list (case insensitive).
+     *   <dl style="margin-left: 1em;">
+     *     <dt>in
+     *     <dd style="margin-left: 1.5em">
+     *       Indicates the input flow filter list, which is evaluated when a
+     *       packet is forwarded to the vTerminal interface.
+     *
+     *     <dt>out
+     *     <dd style="margin-left: 1.5em">
+     *       Indicates the output flow filter list, which is evaluated when a
+     *       packet is going to be transmitted to the physical network mapped
+     *       to the vTerminal interface.
+     *   </dl>
+     * @return
+     *   <strong>flowfilters</strong> element contains information about the
+     *   vTerminal interface flow filter list specified by the requested URI.
+     */
+    @Path("{listType}")
+    @GET
+    @Produces({MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML})
+    @TypeHint(FlowFilterList.class)
+    @StatusCodes({
+        @ResponseCode(code = HTTP_OK,
+                      condition = "Operation completed successfully."),
+        @ResponseCode(code = HTTP_BAD_REQUEST,
+                      condition = "Invalid value is passed to " +
+                      "<u>{listType}</u>."),
+        @ResponseCode(code = HTTP_UNAUTHORIZED,
+                      condition = "User is not authorized to perform this " +
+                      "operation."),
+        @ResponseCode(code = HTTP_NOT_FOUND,
+                      condition = "<ul>" +
+                      "<li>The specified container does not exist.</li>" +
+                      "<li>The specified VTN does not exist.</li>" +
+                      "<li>The specified vTerminal does not exist.</li>" +
+                      "<li>The specified vTerminal interface does not " +
+                      "exist.</li>" +
+                      "</ul>"),
+        @ResponseCode(code = HTTP_INTERNAL_ERROR,
+                      condition = "Fatal internal error occurred in the " +
+                      "VTN Manager."),
+        @ResponseCode(code = HTTP_UNAVAILABLE,
+                      condition = "One or more of mandatory controller " +
+                      "services, such as the VTN Manager, are unavailable.")})
+    public FlowFilterList getFlowFilters(
+            @PathParam("containerName") String containerName,
+            @PathParam("tenantName") String tenantName,
+            @PathParam("termName") String termName,
+            @PathParam("ifName") String ifName,
+            @PathParam("listType") String listType) {
+        checkPrivilege(containerName, Privilege.READ);
+
+        // REVISIT: Not yet.
+        return null;
+    }
+
+    /**
+     * Delete all flow filters in the specified vTerminal interface flow filter
+     * list.
+     *
+     * @param containerName  The name of the container.
+     * @param tenantName     The name of the VTN.
+     * @param termName       The name of the vTerminal.
+     * @param ifName         The name of the vTerminal interface.
+     * @param listType
+     *   The type of the flow filter list (case insensitive).
+     *   <dl style="margin-left: 1em;">
+     *     <dt>in
+     *     <dd style="margin-left: 1.5em">
+     *       Indicates the input flow filter list, which is evaluated when a
+     *       packet is forwarded to the vTerminal interface.
+     *
+     *     <dt>out
+     *     <dd style="margin-left: 1.5em">
+     *       Indicates the output flow filter list, which is evaluated when a
+     *       packet is going to be transmitted to the physical network mapped
+     *       to the vTerminal interface.
+     *   </dl>
+     * @return Response as dictated by the HTTP Response Status code.
+     */
+    @Path("{listType}")
+    @DELETE
+    @TypeHint(TypeHint.NO_CONTENT.class)
+    @StatusCodes({
+        @ResponseCode(code = HTTP_OK,
+                      condition = "Flow filters were deleted successfully."),
+        @ResponseCode(code = HTTP_NO_CONTENT,
+                      condition = "No flow filter was configured in the " +
+                      "specified vTerminal interface flow filter list."),
+        @ResponseCode(code = HTTP_BAD_REQUEST,
+                      condition = "Invalid value is passed to " +
+                      "<u>{listType}</u>."),
+        @ResponseCode(code = HTTP_UNAUTHORIZED,
+                      condition = "User is not authorized to perform this " +
+                      "operation."),
+        @ResponseCode(code = HTTP_NOT_FOUND,
+                      condition = "<ul>" +
+                      "<li>The specified container does not exist.</li>" +
+                      "<li>The specified VTN does not exist.</li>" +
+                      "<li>The specified vTerminal does not exist.</li>" +
+                      "<li>The specified vTerminal interface does not " +
+                      "exist.</li>" +
+                      "</ul>"),
+        @ResponseCode(code = HTTP_NOT_ACCEPTABLE,
+                      condition = "\"default\" is specified to " +
+                      "<u>{containerName}</u> and a container other than " +
+                      "the default container is present."),
+        @ResponseCode(code = HTTP_INTERNAL_ERROR,
+                      condition = "Fatal internal error occurred in the " +
+                      "VTN Manager."),
+        @ResponseCode(code = HTTP_UNAVAILABLE,
+                      condition = "One or more of mandatory controller " +
+                      "services, such as the VTN Manager, are unavailable.")})
+    public Response deleteFlowFilters(
+            @PathParam("containerName") String containerName,
+            @PathParam("tenantName") String tenantName,
+            @PathParam("termame") String termName,
+            @PathParam("ifName") String ifName,
+            @PathParam("listType") String listType) {
+        checkPrivilege(containerName, Privilege.WRITE);
+
+        // REVISIT: Not yet.
+        return null;
+    }
+
+    /**
+     * Return information about the flow filter specified by
+     * the flow filter index in the vTerminal interface flow filter list.
+     *
+     * @param containerName  The name of the container.
+     * @param tenantName     The name of the VTN.
+     * @param termName       The name of the vTerminal.
+     * @param ifName         The name of the vTerminal interface.
+     * @param listType
+     *   The type of the flow filter list (case insensitive).
+     *   <dl style="margin-left: 1em;">
+     *     <dt>in
+     *     <dd style="margin-left: 1.5em">
+     *       Indicates the input flow filter list, which is evaluated when a
+     *       packet is forwarded to the vTerminal interface.
+     *
+     *     <dt>out
+     *     <dd style="margin-left: 1.5em">
+     *       Indicates the output flow filter list, which is evaluated when a
+     *       packet is going to be transmitted to the physical network mapped
+     *       to the vTerminal interface.
+     *   </dl>
+     * @param index
+     *   The index value which specifies the flow filter in the vTerminal
+     *   interface flow filter list. A string representation of an integer
+     *   value must be specified.
+     * @return  <strong>flowfilter</strong> element contains information
+     *          about the flow filter specified by the requested URI.
+     */
+    @Path("{listType}/{index}")
+    @GET
+    @Produces({MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML})
+    @TypeHint(FlowFilter.class)
+    @StatusCodes({
+        @ResponseCode(code = HTTP_OK,
+                      condition = "Operation completed successfully."),
+        @ResponseCode(code = HTTP_NO_CONTENT,
+                      condition = "The specified flow filter does not exist " +
+                      "in the specified vTerminal."),
+        @ResponseCode(code = HTTP_BAD_REQUEST,
+                      condition = "Invalid value is passed to " +
+                      "<u>{listType}</u>."),
+        @ResponseCode(code = HTTP_UNAUTHORIZED,
+                      condition = "User is not authorized to perform this " +
+                      "operation."),
+        @ResponseCode(code = HTTP_NOT_FOUND,
+                      condition = "<ul>" +
+                      "<li>The specified container does not exist.</li>" +
+                      "<li>The specified VTN does not exist.</li>" +
+                      "<li>The specified vTerminal does not exist.</li>" +
+                      "<li>The specified vTerminal interface does not " +
+                      "exist.</li>" +
+                      "<li>A string passed to <u>{index}</u> can not be " +
+                      "converted into an integer.</li>" +
+                      "</ul>"),
+        @ResponseCode(code = HTTP_INTERNAL_ERROR,
+                      condition = "Fatal internal error occurred in the " +
+                      "VTN Manager."),
+        @ResponseCode(code = HTTP_UNAVAILABLE,
+                      condition = "One or more of mandatory controller " +
+                      "services, such as the VTN Manager, are unavailable.")})
+    public FlowFilter getFlowFilter(
+            @PathParam("containerName") String containerName,
+            @PathParam("tenantName") String tenantName,
+            @PathParam("termName") String termName,
+            @PathParam("ifName") String ifName,
+            @PathParam("listType") String listType,
+            @PathParam("index") int index) {
+        checkPrivilege(containerName, Privilege.READ);
+
+        // REVISIT: Not yet.
+        return null;
+    }
+
+    /**
+     * Create or modify the flow filter specified by the index number in the
+     * vTerminal interface flow filter list.
+     *
+     * <ul>
+     *   <li>
+     *     If the flow filter specified by
+     *     <span style="text-decoration: underline;">{index}</span> does not
+     *     exist in the vTerminal interface flow filter list specified by
+     *     <span style="text-decoration: underline;">{listType}</span>,
+     *     a new flow filter will be associated with
+     *     <span style="text-decoration: underline;">{index}</span> in the
+     *     specified vTerminal interface flow filter list.
+     *   </li>
+     *   <li>
+     *     If the flow filter specified by
+     *     <span style="text-decoration: underline;">{index}</span> already
+     *     exists in the vTerminal interface flow filter list specified by
+     *     <span style="text-decoration: underline;">{listType}</span>,
+     *     it will be modified as specified by <strong>flowfilter</strong>
+     *     element.
+     *   </li>
+     * </ul>
+     *
+     * @param uriInfo        Requested URI information.
+     * @param containerName  The name of the container.
+     * @param tenantName     The name of the VTN.
+     * @param termName       The name of the vTerminal.
+     * @param ifName         The name of the vTerminal interface.
+     * @param listType
+     *   The type of the flow filter list (case insensitive).
+     *   <dl style="margin-left: 1em;">
+     *     <dt>in
+     *     <dd style="margin-left: 1.5em">
+     *       Indicates the input flow filter list, which is evaluated when a
+     *       packet is forwarded to the vTerminal interface.
+     *
+     *     <dt>out
+     *     <dd style="margin-left: 1.5em">
+     *       Indicates the output flow filter list, which is evaluated when a
+     *       packet is going to be transmitted to the physical network mapped
+     *       to the vTerminal interface.
+     *   </dl>
+     * @param index
+     *   The index value which specifies the flow filter in the vTerminal
+     *   interface flow filter list.
+     *   <ul>
+     *     <li>
+     *       A string representation of an integer value must be specified.
+     *     </li>
+     *     <li>
+     *       The range of value that can be specified is from
+     *       <strong>1</strong> to <strong>65535</strong>.
+     *     </li>
+     *     <li>
+     *       This value is also used to determine the order of flow filter
+     *       evaluation. Flow filters in the list are evaluated in ascending
+     *       order of indices, and only the first matched flow filter is
+     *       applied to the packet.
+     *     </li>
+     *   </ul>
+     * @param ff
+     *   <strong>flowfilter</strong> element specifies the configuration of the
+     *   flow filter.
+     *   <ul>
+     *     <li>
+     *       The <strong>index</strong> attribute in the
+     *       <strong>flowfilter</strong> element is always ignored.
+     *       The index number is determined by the
+     *       <span style="text-decoration: underline;">{index}</span>
+     *       parameter.
+     *     </li>
+     *     <li>
+     *       Note that this API does not check whether the flow condition
+     *       specified by the <strong>condition</strong> attribute in
+     *       the <strong>flowfilter</strong> element actually exists or not.
+     *       The flow filter will be invalidated if the specified
+     *       flow condition does not exist.
+     *     </li>
+     *     <li>
+     *       Note that this API does not check whether the destination
+     *       virtual interface of the packet redirection, which is configured
+     *       in the <strong>redirect</strong> element in the
+     *       <strong>flowfilter</strong> element, actually exists or not.
+     *       The packet will be discarded if the destination virtual interface
+     *       is not found when the packet is going to be redirected by the
+     *       flow filter.
+     *     </li>
+     *   </ul>
+     * @return Response as dictated by the HTTP Response Status code.
+     */
+    @Path("{listType}/{index}")
+    @PUT
+    @Consumes({MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML})
+    @TypeHint(TypeHint.NO_CONTENT.class)
+    @ResponseHeaders({
+        @ResponseHeader(name = "Location",
+                        description = "URI corresponding to the newly " +
+                        "created flow filter, which is the same URI " +
+                        "specified in request. This header is set only if " +
+                        "CREATED(201) is returned as response code.")})
+    @StatusCodes({
+        @ResponseCode(code = HTTP_OK,
+                      condition = "Existing flow filter was modified " +
+                      "successfully."),
+        @ResponseCode(code = HTTP_CREATED,
+                      condition = "Flow filter was newly created " +
+                      "successfully."),
+        @ResponseCode(code = HTTP_NO_CONTENT,
+                      condition = "Flow filter was not changed."),
+        @ResponseCode(code = HTTP_BAD_REQUEST,
+                      condition = "<ul>" +
+                      "<li>Incorrect XML or JSON data is specified " +
+                      "in Request body.</li>" +
+                      "<li>Invalid value is passed to <u>{listType}</u>." +
+                      "<li>Index number specified by <u>{index}</u> " +
+                      "parameter is out of valid range.</li>" +
+                      "<li>Incorrect value is configured in " +
+                      "<strong>flowfilter</strong>.</li>" +
+                      "</ul>"),
+        @ResponseCode(code = HTTP_UNAUTHORIZED,
+                      condition = "User is not authorized to perform this " +
+                      "operation."),
+        @ResponseCode(code = HTTP_NOT_FOUND,
+                      condition = "<ul>" +
+                      "<li>The specified container does not exist.</li>" +
+                      "<li>The specified VTN does not exist.</li>" +
+                      "<li>The specified vTerminal does not exist.</li>" +
+                      "<li>The specified vTerminal interface does not " +
+                      "exist.</li>" +
+                      "<li>A string passed to <u>{index}</u> can not be " +
+                      "converted into an integer.</li>" +
+                      "</ul>"),
+        @ResponseCode(code = HTTP_NOT_ACCEPTABLE,
+                      condition = "\"default\" is specified to " +
+                      "<u>{containerName}</u> and a container other than " +
+                      "the default container is present."),
+        @ResponseCode(code = HTTP_UNSUPPORTED_TYPE,
+                      condition = "Unsupported data type is specified in " +
+                      "<strong>Content-Type</strong> header."),
+        @ResponseCode(code = HTTP_INTERNAL_ERROR,
+                      condition = "Fatal internal error occurred in the " +
+                      "VTN Manager."),
+        @ResponseCode(code = HTTP_UNAVAILABLE,
+                      condition = "One or more of mandatory controller " +
+                      "services, such as the VTN Manager, are unavailable.")})
+    public Response putFlowFilter(
+            @Context UriInfo uriInfo,
+            @PathParam("containerName") String containerName,
+            @PathParam("tenantName") String tenantName,
+            @PathParam("termName") String termName,
+            @PathParam("ifName") String ifName,
+            @PathParam("listType") String listType,
+            @PathParam("index") int index,
+            @TypeHint(FlowFilter.class) FlowFilter ff) {
+        checkPrivilege(containerName, Privilege.WRITE);
+
+        // REVISIT: Not yet.
+        return null;
+    }
+
+    /**
+     * Delete the flow filter specified by the index number in the
+     * specified vTerminal interface flow filter list.
+     *
+     * @param containerName  The name of the container.
+     * @param tenantName     The name of the VTN.
+     * @param termName       The name of the vTerminal.
+     * @param ifName         The name of the vTerminal interface.
+     * @param listType
+     *   The type of the flow filter list (case insensitive).
+     *   <dl style="margin-left: 1em;">
+     *     <dt>in
+     *     <dd style="margin-left: 1.5em">
+     *       Indicates the input flow filter list, which is evaluated when a
+     *       packet is forwarded to the vTerminal interface.
+     *
+     *     <dt>out
+     *     <dd style="margin-left: 1.5em">
+     *       Indicates the output flow filter list, which is evaluated when a
+     *       packet is going to be transmitted to the physical network mapped
+     *       to the vTerminal interface.
+     *   </dl>
+     * @param index
+     *   The index value which specifies the flow filter in the vTerminal
+     *   interface flow filter list. A string representation of an integer
+     *   value must be specified.
+     * @return Response as dictated by the HTTP Response Status code.
+     */
+    @Path("{listType}/{index}")
+    @DELETE
+    @TypeHint(TypeHint.NO_CONTENT.class)
+    @StatusCodes({
+        @ResponseCode(code = HTTP_OK,
+                      condition = "Flow filter was deleted successfully."),
+        @ResponseCode(code = HTTP_NO_CONTENT,
+                      condition = "The specified flow filter does not exist " +
+                      "in the specified vTerminal."),
+        @ResponseCode(code = HTTP_BAD_REQUEST,
+                      condition = "Invalid value is passed to " +
+                      "<u>{listType}</u>."),
+        @ResponseCode(code = HTTP_UNAUTHORIZED,
+                      condition = "User is not authorized to perform this " +
+                      "operation."),
+        @ResponseCode(code = HTTP_NOT_FOUND,
+                      condition = "<ul>" +
+                      "<li>The specified container does not exist.</li>" +
+                      "<li>The specified VTN does not exist.</li>" +
+                      "<li>The specified vTerminal does not exist.</li>" +
+                      "<li>The specified vTerminal interface does not " +
+                      "exist.</li>" +
+                      "<li>A string passed to <u>{index}</u> can not be " +
+                      "converted into an integer.</li>" +
+                      "</ul>"),
+        @ResponseCode(code = HTTP_NOT_ACCEPTABLE,
+                      condition = "\"default\" is specified to " +
+                      "<u>{containerName}</u> and a container other than " +
+                      "the default container is present."),
+        @ResponseCode(code = HTTP_INTERNAL_ERROR,
+                      condition = "Fatal internal error occurred in the " +
+                      "VTN Manager."),
+        @ResponseCode(code = HTTP_UNAVAILABLE,
+                      condition = "One or more of mandatory controller " +
+                      "services, such as the VTN Manager, are unavailable.")})
+    public Response deleteFlowFilter(
+            @PathParam("containerName") String containerName,
+            @PathParam("tenantName") String tenantName,
+            @PathParam("termName") String termName,
+            @PathParam("ifName") String ifName,
+            @PathParam("listType") String listType,
+            @PathParam("index") int index) {
+        checkPrivilege(containerName, Privilege.WRITE);
+
+        // REVISIT: Not yet.
+        return null;
+    }
+}
diff --git a/manager/northbound/src/test/java/org/opendaylight/vtn/manager/northbound/FlowFilterListTest.java b/manager/northbound/src/test/java/org/opendaylight/vtn/manager/northbound/FlowFilterListTest.java
new file mode 100644 (file)
index 0000000..160389b
--- /dev/null
@@ -0,0 +1,324 @@
+/*
+ * Copyright (c) 2014 NEC Corporation
+ * 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.vtn.manager.northbound;
+
+import java.net.InetAddress;
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+
+import org.junit.Test;
+
+import org.opendaylight.vtn.manager.VBridgeIfPath;
+import org.opendaylight.vtn.manager.VTerminalIfPath;
+import org.opendaylight.vtn.manager.flow.action.FlowAction;
+import org.opendaylight.vtn.manager.flow.action.SetDlDstAction;
+import org.opendaylight.vtn.manager.flow.action.SetDlSrcAction;
+import org.opendaylight.vtn.manager.flow.action.SetDscpAction;
+import org.opendaylight.vtn.manager.flow.action.SetIcmpCodeAction;
+import org.opendaylight.vtn.manager.flow.action.SetIcmpTypeAction;
+import org.opendaylight.vtn.manager.flow.action.SetInet4DstAction;
+import org.opendaylight.vtn.manager.flow.action.SetInet4SrcAction;
+import org.opendaylight.vtn.manager.flow.action.SetTpDstAction;
+import org.opendaylight.vtn.manager.flow.action.SetTpSrcAction;
+import org.opendaylight.vtn.manager.flow.action.SetVlanPcpAction;
+import org.opendaylight.vtn.manager.flow.filter.DropFilter;
+import org.opendaylight.vtn.manager.flow.filter.FilterType;
+import org.opendaylight.vtn.manager.flow.filter.FlowFilter;
+import org.opendaylight.vtn.manager.flow.filter.PassFilter;
+import org.opendaylight.vtn.manager.flow.filter.RedirectFilter;
+
+/**
+ * JUnit test for {@link FlowFilterList}.
+ */
+public class FlowFilterListTest extends TestBase {
+    /**
+     * A list of {@link FilterType} instances for test.
+     */
+    private List<FilterType>  filterTypes;
+
+    /**
+     * A list of {@link FlowAction} instance lists for test.
+     */
+    private List<List<FlowAction>>  actionLists;
+
+    /**
+     * Test case for getter methods.
+     */
+    @Test
+    public void testGetter() {
+        // Null list.
+        FlowFilterList nullList = new FlowFilterList(null);
+        assertNull(nullList.getFilters());
+
+        // Empty list.
+        List<FlowFilter> filters = new ArrayList<FlowFilter>();
+        FlowFilterList emptyList = new FlowFilterList(filters);
+        assertEquals(filters, emptyList.getFilters());
+
+        int[] indices = {0, 1, 65535};
+        for (int index: indices) {
+            for (String cond: createStrings("cnd")) {
+                for (FilterType type: createFilterTypes()) {
+                    for (List<FlowAction> actions: createActionLists()) {
+                        FlowFilter ff = (index == 0)
+                            ? new FlowFilter(cond, type, actions)
+                            : new FlowFilter(index, cond, type, actions);
+                        filters.add(ff);
+
+                        List<FlowFilter> list =
+                            new ArrayList<FlowFilter>(filters);
+                        FlowFilterList fl = new FlowFilterList(list);
+                        assertEquals(list, fl.getFilters());
+                    }
+                }
+            }
+        }
+    }
+
+    /**
+     * Test case for {@link FlowFilterList#equals(Object)} and
+     * {@link FlowFilterList#hashCode()}.
+     */
+    @Test
+    public void testEquals() {
+        HashSet<Object> set = new HashSet<Object>();
+
+        // Null list.
+        FlowFilterList nullList = new FlowFilterList(null);
+        testEquals(set, nullList, new FlowFilterList(null));
+
+        // Empty list should be treated as null list.
+        List<FlowFilter> filters1 = new ArrayList<FlowFilter>();
+        List<FlowFilter> filters2 = new ArrayList<FlowFilter>();
+        FlowFilterList emptyList = new FlowFilterList(filters1);
+        assertEquals(nullList, emptyList);
+        assertEquals(nullList.hashCode(), emptyList.hashCode());
+        assertFalse(set.add(emptyList));
+
+        int[] indices = {0, 1, 65535};
+        String[] conditions = {null, "condition"};
+        List<FilterType> types = createFilterTypes();
+        List<List<FlowAction>> actLists = createActionLists();
+        for (Iterator<List<FlowAction>> it = actLists.iterator();
+             it.hasNext();) {
+            List<FlowAction> act = it.next();
+            if (act == null) {
+                it.remove();
+                break;
+            }
+        }
+
+        for (int index: indices) {
+            for (String cond: conditions) {
+                for (FilterType type: types) {
+                    for (List<FlowAction> actions: actLists) {
+                        FlowFilter ff1, ff2;
+                        List<FlowAction> alist =
+                            new ArrayList<FlowAction>(actions);
+                        if (index == 0) {
+                            ff1 = new FlowFilter(cond, type, actions);
+                            ff2 = new FlowFilter(copy(cond), type, alist);
+                        } else {
+                            ff1 = new FlowFilter(index, cond, type, actions);
+                            ff2 = new FlowFilter(index, copy(cond), type,
+                                                 alist);
+                        }
+
+                        filters1.add(ff1);
+                        filters2.add(ff2);
+                        List<FlowFilter> list1 =
+                            new ArrayList<FlowFilter>(filters1);
+                        List<FlowFilter> list2 =
+                            new ArrayList<FlowFilter>(filters2);
+                        FlowFilterList fl1 = new FlowFilterList(list1);
+                        FlowFilterList fl2 = new FlowFilterList(list2);
+                        testEquals(set, fl1, fl2);
+                    }
+                }
+            }
+        }
+
+        int required = indices.length * conditions.length * types.size() *
+            actLists.size() + 1;
+        assertEquals(required, set.size());
+    }
+
+    /**
+     * Ensure that {@link FlowFilterList} is mapped to XML root element.
+     */
+    @Test
+    public void testJAXB() {
+        // Null list.
+        FlowFilterList fl = new FlowFilterList(null);
+        String rootName = "flowfilters";
+        jaxbTest(fl, rootName);
+
+        // Empty list.
+        List<FlowFilter> filters = new ArrayList<FlowFilter>();
+        fl = new FlowFilterList(filters);
+        jaxbTest(fl, rootName);
+
+        int[] indices = {0, 1, 65535};
+        String[] conditions = {null, "condition"};
+        for (int index: indices) {
+            for (String cond: conditions) {
+                for (FilterType type: createFilterTypes()) {
+                    for (List<FlowAction> actions: createActionLists()) {
+                        FlowFilter ff = (index == 0)
+                            ? new FlowFilter(cond, type, actions)
+                            : new FlowFilter(index, cond, type, actions);
+                        filters.add(ff);
+
+                        List<FlowFilter> list =
+                            new ArrayList<FlowFilter>(filters);
+                        fl = new FlowFilterList(list);
+                        jaxbTest(fl, rootName);
+                    }
+                }
+            }
+        }
+    }
+
+    /**
+     * Ensure that {@link FlowFilterList} is mapped to JSON object.
+     */
+    @Test
+    public void testJSON() {
+        // Null list.
+        FlowFilterList fl = new FlowFilterList(null);
+        String rootName = "flowfilters";
+        jaxbTest(fl, rootName);
+
+        // Empty list.
+        List<FlowFilter> filters = new ArrayList<FlowFilter>();
+        fl = new FlowFilterList(filters);
+        jaxbTest(fl, rootName);
+
+        int[] indices = {0, 1, 65535};
+        String[] conditions = {null, "condition"};
+        for (int index: indices) {
+            for (String cond: conditions) {
+                for (FilterType type: createFilterTypes()) {
+                    for (List<FlowAction> actions: createActionLists()) {
+                        FlowFilter ff = (index == 0)
+                            ? new FlowFilter(cond, type, actions)
+                            : new FlowFilter(index, cond, type, actions);
+                        filters.add(ff);
+
+                        List<FlowFilter> list =
+                            new ArrayList<FlowFilter>(filters);
+                        fl = new FlowFilterList(list);
+                        jsonTest(fl);
+                    }
+                }
+            }
+        }
+    }
+
+    /**
+     * Create a list of {@link FilterType} instances.
+     *
+     * @return  A list of {@link FilterType} instances.
+     */
+    private List<FilterType> createFilterTypes() {
+        List<FilterType> list = filterTypes;
+        if (list == null) {
+            list = new ArrayList<FilterType>();
+            list.add(null);
+            list.add(new PassFilter());
+            list.add(new DropFilter());
+            list.add(new RedirectFilter(
+                         new VBridgeIfPath(null, "bname", "if1"), true));
+            list.add(new RedirectFilter(
+                         new VTerminalIfPath("vtn", "vtname", "if2"), false));
+            filterTypes = list;
+        }
+
+        return list;
+    }
+
+    /**
+     * Create lists of {@link FlowAction} instances.
+     *
+     * @return  A list of {@link FlowAction} instance lists.
+     */
+    private List<List<FlowAction>> createActionLists() {
+        List<List<FlowAction>> list = actionLists;
+        if (list == null) {
+            list = new ArrayList<List<FlowAction>>();
+            byte[] mac1 = {
+                (byte)0x00, (byte)0x01, (byte)0x02,
+                (byte)0x03, (byte)0x04, (byte)0x05,
+            };
+            byte[] mac2 = {
+                (byte)0x10, (byte)0x20, (byte)0x30,
+                (byte)0x40, (byte)0x50, (byte)0x60,
+            };
+            byte[] mac3 = {
+                (byte)0xa0, (byte)0xbb, (byte)0xcc,
+                (byte)0xdd, (byte)0xee, (byte)0xff,
+            };
+            byte[] mac4 = {
+                (byte)0xfa, (byte)0xf0, (byte)0xf1,
+                (byte)0xfa, (byte)0xfb, (byte)0xfc,
+            };
+
+            InetAddress addr1, addr2;
+            try {
+                addr1 = InetAddress.getByName("192.168.100.1");
+                addr2 = InetAddress.getByName("192.168.200.123");
+            } catch (Exception e) {
+                unexpected(e);
+                return null;
+            }
+
+            list.add(null);
+            list.add(new ArrayList<FlowAction>());
+            list.add(createActions(new SetDscpAction((byte)63),
+                                   new SetDlSrcAction(mac1),
+                                   new SetDlDstAction(mac2),
+                                   new SetVlanPcpAction((byte)3)));
+
+            // Create all-in-one list.
+            list.add(createActions(new SetVlanPcpAction((byte)7),
+                                   new SetDscpAction((byte)4),
+                                   new SetTpSrcAction(65535),
+                                   new SetTpDstAction(0),
+                                   new SetDlSrcAction(mac3),
+                                   new SetDlDstAction(mac4),
+                                   new SetIcmpTypeAction((short)255),
+                                   new SetIcmpCodeAction((short)128),
+                                   new SetInet4SrcAction(addr1),
+                                   new SetInet4DstAction(addr2)));
+
+            actionLists = list;
+        }
+
+        return list;
+    }
+
+    /**
+     * Create a list of {@link FlowAction} instances.
+     *
+     * @param actions  An array of {@link FlowAction} instances to be added
+     *                 to the list.
+     * @return  A list of {@link FlowAction} instances.
+     */
+    private List<FlowAction> createActions(FlowAction ... actions) {
+        List<FlowAction> list = new ArrayList<FlowAction>();
+        for (FlowAction act: actions) {
+            list.add(act);
+        }
+
+        return list;
+    }
+}
index a8664eb99f8f6dba98627026c78df70c8aee4db3..da4f710067d22c8e651bf4a548f435bbae91d4c1 100644 (file)
@@ -23,6 +23,11 @@ import java.util.Set;
 import javax.xml.bind.JAXB;
 import javax.xml.bind.annotation.XmlRootElement;
 
+import com.fasterxml.jackson.databind.AnnotationIntrospector;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.introspect.JacksonAnnotationIntrospector;
+import com.fasterxml.jackson.module.jaxb.JaxbAnnotationIntrospector;
+
 import org.opendaylight.vtn.manager.EthernetHost;
 import org.opendaylight.vtn.manager.MacAddressEntry;
 import org.opendaylight.vtn.manager.SwitchPort;
@@ -755,8 +760,9 @@ public abstract class TestBase extends Assert {
      *
      * @param o         An object to be tested.
      * @param rootName  The name of expected root element.
+     * @return  Deserialized object.
      */
-    protected static void jaxbTest(Object o, String rootName) {
+    protected static Object jaxbTest(Object o, String rootName) {
         // Ensure that the class of the given class has XmlRootElement
         // annotation.
         Class<?> cl = o.getClass();
@@ -786,5 +792,51 @@ public abstract class TestBase extends Assert {
 
         assertNotSame(o, newobj);
         assertEquals(o, newobj);
+
+        return newobj;
+    }
+
+    /**
+     * Ensure that the given object is mapped to JSON object.
+     *
+     * @param o    An object to be tested.
+     * @param <T>  The type of the given object.
+     * @return  Deserialized object.
+     */
+    protected static <T> T jsonTest(T o) {
+        ObjectMapper mapper = getJsonObjectMapper();
+
+        try {
+            // Marshal the given object into JSON.
+            String json = mapper.writeValueAsString(o);
+            assertNotNull(json);
+            assertTrue(json.length() != 0);
+
+            // Unmarshal the JSON notation.
+            T newobj = mapper.readValue(json, (Class<T>)o.getClass());
+            assertNotSame(o, newobj);
+            assertEquals(o, newobj);
+            return newobj;
+        } catch (Exception e) {
+            unexpected(e);
+        }
+
+        return null;
+    }
+
+    /**
+     * Create Jackson's {@code ObjectMapper} instance.
+     *
+     * @return  A {@code ObjectMapper} instance.
+     */
+    protected static ObjectMapper getJsonObjectMapper() {
+        // Create Jackson object mapper with enabling JAXB annotations.
+        ObjectMapper mapper = new ObjectMapper();
+        AnnotationIntrospector introspector =
+            AnnotationIntrospector.pair(new JacksonAnnotationIntrospector(),
+                                        new JaxbAnnotationIntrospector());
+        mapper.setAnnotationIntrospector(introspector);
+
+        return mapper;
     }
 }