Added support for multiple Actions. 20/16520/2
authorThomas Bachman <tbachman@yahoo.com>
Mon, 9 Mar 2015 14:13:37 +0000 (10:13 -0400)
committerThomas Bachman <tbachman@yahoo.com>
Mon, 23 Mar 2015 15:44:03 +0000 (11:44 -0400)
This patch adds support for using multiple actions
in the OpenFlow Overlay renderer.

Change-Id: I07e8e80a9012db378e765478751d175cae29a82b
Signed-off-by: Thomas Bachman <tbachman@yahoo.com>
renderers/ofoverlay/src/main/java/org/opendaylight/groupbasedpolicy/renderer/ofoverlay/flow/FlowUtils.java
renderers/ofoverlay/src/main/java/org/opendaylight/groupbasedpolicy/renderer/ofoverlay/flow/PolicyEnforcer.java
renderers/ofoverlay/src/main/java/org/opendaylight/groupbasedpolicy/renderer/ofoverlay/sf/Action.java [new file with mode: 0644]
renderers/ofoverlay/src/main/java/org/opendaylight/groupbasedpolicy/renderer/ofoverlay/sf/AllowAction.java [new file with mode: 0644]
renderers/ofoverlay/src/main/java/org/opendaylight/groupbasedpolicy/renderer/ofoverlay/sf/SubjectFeatures.java
renderers/ofoverlay/src/test/java/org/opendaylight/groupbasedpolicy/renderer/ofoverlay/flow/OfTableTest.java

index a3d047e83f28aac89d64326067f899f92d4e06c0..9635caff0395298a8c874f6ea334568524ea40e4 100644 (file)
@@ -10,6 +10,7 @@ package org.opendaylight.groupbasedpolicy.renderer.ofoverlay.flow;
 
 import java.math.BigInteger;
 import java.util.ArrayList;
+import java.util.List;
 
 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.Uri;
 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev100924.MacAddress;
@@ -258,6 +259,17 @@ public final class FlowUtils {
         }
         return alist;
     }
+    public static ArrayList<org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.list.Action> actionList(List<ActionBuilder> actions) {
+        ArrayList<org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.list.Action> alist
+            = new ArrayList<>();
+        int count = 0;
+        for (ActionBuilder action : actions) {
+            alist.add(action
+            .setOrder(Integer.valueOf(count++))
+            .build());
+        }
+        return alist;
+    }
     public static Instruction applyActionIns(Action... actions) {
         return new ApplyActionsCaseBuilder()
             .setApplyActions(new ApplyActionsBuilder()
@@ -265,6 +277,13 @@ public final class FlowUtils {
                 .build())
             .build();
     }
+    public static Instruction applyActionIns(List<ActionBuilder> actions) {
+        return new ApplyActionsCaseBuilder()
+            .setApplyActions(new ApplyActionsBuilder()
+                .setAction(actionList(actions))
+                .build())
+            .build();
+    }
     public static Instructions instructions(Instruction... instructions) {
         ArrayList<org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.list.Instruction> ins
             = new ArrayList<>();
index b5b6844c9651eaca5d8d8429d60a7e6bf169ce5e..7c10e2e0459caebe3ef472ea76331614f4275e32 100644 (file)
@@ -8,7 +8,9 @@
 
 package org.opendaylight.groupbasedpolicy.renderer.ofoverlay.flow;
 
+import java.util.ArrayList;
 import java.util.Collections;
+import java.util.Comparator;
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.List;
@@ -20,7 +22,9 @@ import javax.annotation.concurrent.Immutable;
 import org.opendaylight.controller.md.sal.binding.api.ReadWriteTransaction;
 import org.opendaylight.groupbasedpolicy.renderer.ofoverlay.PolicyManager.Dirty;
 import org.opendaylight.groupbasedpolicy.renderer.ofoverlay.flow.FlowUtils.RegMatch;
+import org.opendaylight.groupbasedpolicy.renderer.ofoverlay.sf.AllowAction;
 import org.opendaylight.groupbasedpolicy.renderer.ofoverlay.sf.Classifier;
+import org.opendaylight.groupbasedpolicy.renderer.ofoverlay.sf.Action;
 import org.opendaylight.groupbasedpolicy.renderer.ofoverlay.sf.SubjectFeatures;
 import org.opendaylight.groupbasedpolicy.resolver.ConditionGroup;
 import org.opendaylight.groupbasedpolicy.resolver.EgKey;
@@ -28,6 +32,7 @@ import org.opendaylight.groupbasedpolicy.resolver.IndexedTenant;
 import org.opendaylight.groupbasedpolicy.resolver.Policy;
 import org.opendaylight.groupbasedpolicy.resolver.PolicyInfo;
 import org.opendaylight.groupbasedpolicy.resolver.RuleGroup;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.list.ActionBuilder;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowId;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.Table;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.FlowBuilder;
@@ -37,11 +42,13 @@ import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.common.rev
 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.common.rev140421.TenantId;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.endpoint.rev140421.endpoints.Endpoint;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.HasDirection.Direction;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.has.action.refs.ActionRef;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.has.classifier.refs.ClassifierRef;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.subject.feature.instance.ParameterValue;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.EndpointGroup;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.EndpointGroup.IntraGroupPolicy;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.contract.subject.Rule;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.subject.feature.instances.ActionInstance;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.subject.feature.instances.ClassifierInstance;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeConnectorId;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeId;
@@ -54,6 +61,9 @@ import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import com.google.common.collect.ComparisonChain;
+import com.google.common.collect.Ordering;
+
 import static org.opendaylight.groupbasedpolicy.renderer.ofoverlay.flow.FlowUtils.*;
 
 /**
@@ -212,12 +222,92 @@ public class PolicyEnforcer extends FlowTable {
             }
         }
     }
-    
+
+    /**
+     * Private internal class for ordering Actions in Rules. The
+     * order is determined first by the value of the order parameter,
+     * with the lower order actions being applied first; for Actions
+     * with either the same order or no order, ordering is lexicographical
+     * by name.
+     *
+     * @author tbachman
+     *
+     */
+    private static class ActionRefComparator implements Comparator<ActionRef> {
+        public static final ActionRefComparator INSTANCE = new ActionRefComparator();
+
+        @Override
+        public int compare(ActionRef arg0, ActionRef arg1) {
+            return ComparisonChain.start()
+                    .compare(arg0.getOrder(), arg1.getOrder(),
+                             Ordering.natural().nullsLast())
+                    .compare(arg0.getName().getValue(), arg1.getName().getValue(),
+                             Ordering.natural().nullsLast())
+                    .result();
+        }
+
+    }
+
     private void syncDirection(ReadWriteTransaction t, 
                                InstanceIdentifier<Table> tiid,
                                Map<String, FlowCtx> flowMap, NodeId nodeId,
                                IndexedTenant contractTenant,
                                CgPair p, Rule r, Direction d, int priority) {
+        /*
+         * Create the ordered action list. The implicit action is
+         * "allow", and is therefore always in the list
+         *
+         * TODO: revisit implicit vs. default for "allow"
+         * TODO: look into incorporating operational policy for actions
+         */
+        List<ActionBuilder> abl = new ArrayList<ActionBuilder>();
+        if (r.getActionRef() != null) {
+            /*
+             * Pre-sort by references using order, then name
+             */
+            List<ActionRef> arl = new ArrayList<ActionRef>(r.getActionRef());
+            Collections.sort(arl, ActionRefComparator.INSTANCE);
+
+            for (ActionRef ar: arl) {
+                ActionInstance ai = contractTenant.getAction(ar.getName());
+                if (ai == null) {
+                    // XXX TODO fail the match and raise an exception
+                    LOG.warn("Action instance {} not found",
+                             ar.getName().getValue());
+                    return;
+                }
+                Action act = SubjectFeatures.getAction(ai.getActionDefinitionId());
+                if (act == null) {
+                    // XXX TODO fail the match and raise an exception
+                    LOG.warn("Action definition {} not found",
+                             ai.getActionDefinitionId().getValue());
+                    return;
+                }
+
+                Map<String,Object> params = new HashMap<>();
+                if (ai.getParameterValue() != null) {
+                    for (ParameterValue v : ai.getParameterValue()) {
+                        if (v.getName() == null) continue;
+                        if (v.getIntValue() != null) {
+                            params.put(v.getName().getValue(), v.getIntValue());
+                        } else if (v.getStringValue() != null) {
+                            params.put(v.getName().getValue(), v.getStringValue());
+                        }
+                    }
+                }
+                /*
+                 * Convert the GBP Action to one or more OpenFlow
+                 * Actions
+                 */
+                abl = act.updateAction(abl, params, ar.getOrder());
+            }
+        }
+        else {
+            Action act = SubjectFeatures.getAction(AllowAction.ID);
+            abl = act.updateAction(abl, new HashMap<String,Object>(),  0);
+        }
+
+
         for (ClassifierRef cr : r.getClassifierRef()) {
             if (cr.getDirection() != null && 
                 !cr.getDirection().equals(Direction.Bidirectional) && 
@@ -301,7 +391,7 @@ public class PolicyEnforcer extends FlowTable {
                     flow.setMatch(m)
                         .setId(flowId)
                         .setPriority(Integer.valueOf(priority))
-                        .setInstructions(instructions(applyActionIns(nxOutputRegAction(NxmNxReg7.class))));
+                        .setInstructions(instructions(applyActionIns(abl)));
                     writeFlow(t, tiid, flow.build());
                 }
             }
diff --git a/renderers/ofoverlay/src/main/java/org/opendaylight/groupbasedpolicy/renderer/ofoverlay/sf/Action.java b/renderers/ofoverlay/src/main/java/org/opendaylight/groupbasedpolicy/renderer/ofoverlay/sf/Action.java
new file mode 100644 (file)
index 0000000..c7ee772
--- /dev/null
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.groupbasedpolicy.renderer.ofoverlay.sf;
+
+import java.util.List;
+import java.util.Map;
+
+import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.list.ActionBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.common.rev140421.ActionDefinitionId;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.subject.feature.definitions.ActionDefinition;
+
+/**
+ * Represent an action definition, and provide tools for generating
+ * flow instructions based on the action
+ * @author tbachman
+ */
+public abstract class Action {
+    /**
+     * Get the action definition for this action
+     * @return the {@link ActionDefinition} for this action
+     */
+    public abstract ActionDefinitionId getId();
+
+    /**
+     * Get the action definition for this action
+     * @return the {@link ActionDefinition} for this action
+     */
+    public abstract ActionDefinition getActionDef();
+
+    /**
+     * Construct a set of actions that will apply to the traffic.  Augment
+     * the existing list of actions or add new actions.  It's important
+     * that the order of the returned list be consistent however
+     * @param actions The existing actions
+     * @param params the parameters for the action instance
+     * @return the updated list of actions (may be a different length)
+     */
+    public abstract List<ActionBuilder> updateAction(List<ActionBuilder> actions,
+                                                     Map<String, Object> params,
+                                                     Integer i);
+}
diff --git a/renderers/ofoverlay/src/main/java/org/opendaylight/groupbasedpolicy/renderer/ofoverlay/sf/AllowAction.java b/renderers/ofoverlay/src/main/java/org/opendaylight/groupbasedpolicy/renderer/ofoverlay/sf/AllowAction.java
new file mode 100644 (file)
index 0000000..87bad60
--- /dev/null
@@ -0,0 +1,68 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.groupbasedpolicy.renderer.ofoverlay.sf;
+
+import static org.opendaylight.groupbasedpolicy.renderer.ofoverlay.flow.FlowUtils.nxOutputRegAction;
+
+import java.util.List;
+import java.util.Map;
+
+import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.list.ActionBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.common.rev140421.ActionDefinitionId;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.common.rev140421.ActionName;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.common.rev140421.Description;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.subject.feature.definitions.ActionDefinition;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.subject.feature.definitions.ActionDefinitionBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.openflowjava.nx.match.rev140421.NxmNxReg7;
+
+
+/**
+ * Allow action
+ */
+public class AllowAction extends Action {
+    public static final ActionDefinitionId ID =
+            new ActionDefinitionId("f942e8fd-e957-42b7-bd18-f73d11266d17");
+    protected static final String TYPE = "type";
+    protected static final ActionDefinition DEF =
+            new ActionDefinitionBuilder()
+                .setId(ID)
+                .setName(new ActionName("allow"))
+                .setDescription(new Description("Allow the specified traffic to pass"))
+                .build();
+
+    // How allow is implemented in the PolicyEnforcer table
+       private final org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.Action allow = 
+                       nxOutputRegAction(NxmNxReg7.class);
+
+    @Override
+    public ActionDefinitionId getId() {
+        return ID;
+    }
+
+    @Override
+    public ActionDefinition getActionDef() {
+        return DEF;
+    }
+
+    @Override
+    public List<ActionBuilder> updateAction(List<ActionBuilder> actions,
+                                            Map<String, Object> params,
+                                            Integer order) {
+        /*
+         * Allow action doesn't use parameters
+         * TODO: check to make sure ActionBuilder w/allow isn't already present
+         */
+        ActionBuilder ab = new ActionBuilder();
+        ab.setAction(allow)
+          .setOrder(order);
+        actions.add(ab);
+        return actions;
+    }
+
+}
index 7d8377d7f95dabfbdbe95a2a3bba6de24948e1eb..7ad60df97c32180eb6e5df9d0d151bfeb461140d 100644 (file)
@@ -46,16 +46,24 @@ public class SubjectFeatures {
                 }
             ));
     
-    public static final ActionDefinition ALLOW = 
-            new ActionDefinitionBuilder()
-                .setId(new ActionDefinitionId("f942e8fd-e957-42b7-bd18-f73d11266d17"))
-                .setName(new ActionName("allow"))
-                .setDescription(new Description("Allow the specified traffic to pass"))
-                .build();
+    private static final Map<ActionDefinitionId, Action> actions =
+            ImmutableMap.<ActionDefinitionId, Action>
+                of(AllowAction.ID, new AllowAction());
+
+    public static final List<ActionDefinition> actionDefs =
+            ImmutableList.copyOf(Collections2.transform(actions.values(),
+                new Function<Action, ActionDefinition>() {
+                    @Override
+                    public ActionDefinition apply(Action input) {
+                        return input.getActionDef();
+                    }
+                }
+             ));
+
 
     public static final SubjectFeatureDefinitions OF_OVERLAY_FEATURES =
             new SubjectFeatureDefinitionsBuilder()
-                .setActionDefinition(ImmutableList.of(ALLOW))
+                .setActionDefinition(actionDefs)
                 .setClassifierDefinition(classifierDefs)
                 .build();
 
@@ -69,5 +77,16 @@ public class SubjectFeatures {
     public static Classifier getClassifier(ClassifierDefinitionId id) {
         return classifiers.get(id);
     }
+
+    /**
+     * Get the {@link Action} associated with the given
+     * {@link ActionDefinitionId}
+     * @param id the {@link ActionDefinitionId} to look up
+     * @return the {@link Action} if one exists, or <code>null</code>
+     * otherwise
+     */
+    public static Action getAction(ActionDefinitionId id) {
+        return actions.get(id);
+    }
                                            
 }
index 2c228d60394777c4550699be0354f4addab40c47..0813faa9260aa5bedcfa49c9f51aa8f911e28b4e 100644 (file)
@@ -12,6 +12,7 @@ import org.opendaylight.groupbasedpolicy.renderer.ofoverlay.MockEndpointManager;
 import org.opendaylight.groupbasedpolicy.renderer.ofoverlay.MockPolicyManager;
 import org.opendaylight.groupbasedpolicy.renderer.ofoverlay.MockSwitchManager;
 import org.opendaylight.groupbasedpolicy.renderer.ofoverlay.flow.OfTable.OfTableCtx;
+import org.opendaylight.groupbasedpolicy.renderer.ofoverlay.sf.AllowAction;
 import org.opendaylight.groupbasedpolicy.renderer.ofoverlay.sf.L4Classifier;
 import org.opendaylight.groupbasedpolicy.renderer.ofoverlay.sf.SubjectFeatures;
 import org.opendaylight.groupbasedpolicy.resolver.MockPolicyResolver;
@@ -169,7 +170,7 @@ public class OfTableTest {
                      .build()))
                 .setActionInstance(ImmutableList.of(new ActionInstanceBuilder()
                     .setName(new ActionName("allow"))
-                    .setActionDefinitionId(SubjectFeatures.ALLOW.getId())
+                    .setActionDefinitionId(new AllowAction().getId())
                     .build()))
                 .build())
             .setContract(ImmutableList.of(new ContractBuilder()