Adding experimental Flow Objective composition
Change-Id: I35e4414845ad5034145157ab83f933e40f75a1d9
diff --git a/cli/src/main/java/org/onosproject/cli/net/FlowObjectiveCompositionCommand.java b/cli/src/main/java/org/onosproject/cli/net/FlowObjectiveCompositionCommand.java
new file mode 100644
index 0000000..9bacc7a
--- /dev/null
+++ b/cli/src/main/java/org/onosproject/cli/net/FlowObjectiveCompositionCommand.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.cli.net;
+
+import org.apache.karaf.shell.commands.Argument;
+import org.apache.karaf.shell.commands.Command;
+import org.onosproject.cli.AbstractShellCommand;
+import org.onosproject.net.flowobjective.FlowObjectiveService;
+
+/**
+ * Manages FlowObjectiveComposition policy.
+ */
+@Command(scope = "onos", name = "policy",
+ description = "Manages FlowObjectiveComposition policy")
+public class FlowObjectiveCompositionCommand extends AbstractShellCommand {
+
+ @Argument(index = 0, name = "command",
+ description = "Command name (install)",
+ required = true, multiValued = false)
+ String command = null;
+
+ @Argument(index = 1, name = "names", description = "policy string",
+ required = true, multiValued = true)
+ String[] policies = null;
+
+ @Override
+ protected void execute() {
+ FlowObjectiveService service = get(FlowObjectiveService.class);
+ service.initPolicy(policies[0]);
+ print("Policy %s installed", policies[0]);
+ }
+}
diff --git a/cli/src/main/resources/OSGI-INF/blueprint/shell-config.xml b/cli/src/main/resources/OSGI-INF/blueprint/shell-config.xml
index ca470e3..367013c 100644
--- a/cli/src/main/resources/OSGI-INF/blueprint/shell-config.xml
+++ b/cli/src/main/resources/OSGI-INF/blueprint/shell-config.xml
@@ -30,6 +30,10 @@
</command>
<command>
+ <action class="org.onosproject.cli.net.FlowObjectiveCompositionCommand"/>
+ </command>
+
+ <command>
<action class="org.onosproject.cli.app.ApplicationsListCommand"/>
</command>
diff --git a/core/api/src/main/java/org/onosproject/net/flowobjective/FlowObjectiveService.java b/core/api/src/main/java/org/onosproject/net/flowobjective/FlowObjectiveService.java
index cb9a248..d325415 100644
--- a/core/api/src/main/java/org/onosproject/net/flowobjective/FlowObjectiveService.java
+++ b/core/api/src/main/java/org/onosproject/net/flowobjective/FlowObjectiveService.java
@@ -56,4 +56,10 @@
*/
int allocateNextId();
+ /**
+ * Installs the filtering rules onto the specified device.
+ *
+ * @param policy policy expression
+ */
+ void initPolicy(String policy);
}
diff --git a/core/net/src/main/java/org/onosproject/net/flowobjective/impl/FlowObjectiveManager.java b/core/net/src/main/java/org/onosproject/net/flowobjective/impl/FlowObjectiveManager.java
index 4247f03..7d0713b 100644
--- a/core/net/src/main/java/org/onosproject/net/flowobjective/impl/FlowObjectiveManager.java
+++ b/core/net/src/main/java/org/onosproject/net/flowobjective/impl/FlowObjectiveManager.java
@@ -218,6 +218,9 @@
return flowObjectiveStore.allocateNextId();
}
+ @Override
+ public void initPolicy(String policy) {}
+
private boolean queueObjective(DeviceId deviceId, ForwardingObjective fwd) {
if (fwd.nextId() != null &&
flowObjectiveStore.getNextGroup(fwd.nextId()) == null) {
diff --git a/core/net/src/main/java/org/onosproject/net/flowobjective/impl/composition/FilterTable.java b/core/net/src/main/java/org/onosproject/net/flowobjective/impl/composition/FilterTable.java
new file mode 100644
index 0000000..b46ce8b
--- /dev/null
+++ b/core/net/src/main/java/org/onosproject/net/flowobjective/impl/composition/FilterTable.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.net.flowobjective.impl.composition;
+
+import org.onosproject.net.flowobjective.FilteringObjective;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Provides a table to store Fitler.
+ */
+public class FilterTable {
+
+ protected Map<Integer, FilteringObjective> filterMap;
+
+ public FilterTable() {
+ this.filterMap = new HashMap<>();
+ }
+
+ public List<FilteringObjective> updateFilter(FilteringObjective filteringObjective) {
+ List<FilteringObjective> updates = new ArrayList<>();
+ switch (filteringObjective.op()) {
+ case ADD:
+ this.filterMap.put(filteringObjective.id(), filteringObjective);
+ updates.add(filteringObjective);
+ break;
+ case REMOVE:
+ this.filterMap.remove(filteringObjective.id());
+ updates.add(filteringObjective);
+ break;
+ default:
+ break;
+ }
+ return updates;
+ }
+
+ public List<FilteringObjective> updateFilter(List<FilteringObjective> filteringObjectives) {
+ List<FilteringObjective> updates = new ArrayList<>();
+ for (FilteringObjective filteringObjective : filteringObjectives) {
+ updates.addAll(this.updateFilter(filteringObjective));
+ }
+ return updates;
+ }
+
+}
diff --git a/core/net/src/main/java/org/onosproject/net/flowobjective/impl/composition/FlowObjectiveCompositionManager.java b/core/net/src/main/java/org/onosproject/net/flowobjective/impl/composition/FlowObjectiveCompositionManager.java
new file mode 100644
index 0000000..1d25c2e
--- /dev/null
+++ b/core/net/src/main/java/org/onosproject/net/flowobjective/impl/composition/FlowObjectiveCompositionManager.java
@@ -0,0 +1,439 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.net.flowobjective.impl.composition;
+
+import com.google.common.collect.Maps;
+import com.google.common.collect.Sets;
+import org.apache.felix.scr.annotations.Activate;
+import org.apache.felix.scr.annotations.Component;
+import org.apache.felix.scr.annotations.Deactivate;
+import org.apache.felix.scr.annotations.Reference;
+import org.apache.felix.scr.annotations.ReferenceCardinality;
+import org.apache.felix.scr.annotations.Service;
+import org.onlab.osgi.DefaultServiceDirectory;
+import org.onlab.osgi.ServiceDirectory;
+import org.onlab.util.ItemNotFoundException;
+import org.onosproject.cluster.ClusterService;
+import org.onosproject.core.Permission;
+import org.onosproject.mastership.MastershipEvent;
+import org.onosproject.mastership.MastershipListener;
+import org.onosproject.mastership.MastershipService;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.behaviour.Pipeliner;
+import org.onosproject.net.behaviour.PipelinerContext;
+import org.onosproject.net.device.DeviceEvent;
+import org.onosproject.net.device.DeviceListener;
+import org.onosproject.net.device.DeviceService;
+import org.onosproject.net.driver.DefaultDriverProviderService;
+import org.onosproject.net.driver.DriverHandler;
+import org.onosproject.net.driver.DriverService;
+import org.onosproject.net.flow.FlowRuleService;
+import org.onosproject.net.flow.criteria.Criterion;
+import org.onosproject.net.flow.instructions.Instruction;
+import org.onosproject.net.flowobjective.FilteringObjective;
+import org.onosproject.net.flowobjective.FlowObjectiveService;
+import org.onosproject.net.flowobjective.FlowObjectiveStore;
+import org.onosproject.net.flowobjective.FlowObjectiveStoreDelegate;
+import org.onosproject.net.flowobjective.ForwardingObjective;
+import org.onosproject.net.flowobjective.NextObjective;
+import org.onosproject.net.flowobjective.Objective;
+import org.onosproject.net.flowobjective.ObjectiveError;
+import org.onosproject.net.flowobjective.ObjectiveEvent;
+import org.onosproject.net.group.GroupService;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.ExecutorService;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static java.util.concurrent.Executors.newFixedThreadPool;
+import static org.onlab.util.Tools.groupedThreads;
+import static org.onosproject.security.AppGuard.checkPermission;
+
+
+/**
+ * Provides implementation of the flow objective programming service with composition feature.
+ *
+ * Note: This is an experimental, alternative implementation of the FlowObjectiveManager
+ * that supports composition. It can be enabled by setting the enable flag below to true,
+ * and you should also add "enabled = false" to the FlowObjectiveManager.
+ *
+ * The implementation relies a FlowObjectiveCompositionTree that is not yet distributed,
+ * so it will not have high availability and may break if device mastership changes.
+ * Therefore, it is safest to use this component in a single instance scenario.
+ * This comment will be removed when a distributed implementation is available.
+ */
+@Component(immediate = true, enabled = false)
+@Service
+public class FlowObjectiveCompositionManager implements FlowObjectiveService {
+
+ public enum PolicyOperator {
+ Parallel,
+ Sequential,
+ Override,
+ Application
+ }
+
+ public static final int INSTALL_RETRY_ATTEMPTS = 5;
+ public static final long INSTALL_RETRY_INTERVAL = 1000; // ms
+
+ private final Logger log = LoggerFactory.getLogger(getClass());
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected DriverService driverService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected DeviceService deviceService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected MastershipService mastershipService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected ClusterService clusterService;
+
+ // Note: The following dependencies are added on behalf of the pipeline
+ // driver behaviours to assure these services are available for their
+ // initialization.
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected FlowRuleService flowRuleService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected GroupService groupService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected FlowObjectiveStore flowObjectiveStore;
+
+ // Note: This must remain an optional dependency to allow re-install of default drivers.
+ // Note: For now disabled until we can move to OPTIONAL_UNARY dependency
+ // @Reference(cardinality = ReferenceCardinality.OPTIONAL_UNARY, policy = ReferencePolicy.DYNAMIC)
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected DefaultDriverProviderService defaultDriverService;
+
+ private final FlowObjectiveStoreDelegate delegate = new InternalStoreDelegate();
+
+ private final Map<DeviceId, DriverHandler> driverHandlers = Maps.newConcurrentMap();
+ private final Map<DeviceId, Pipeliner> pipeliners = Maps.newConcurrentMap();
+
+ private final PipelinerContext context = new InnerPipelineContext();
+ private final MastershipListener mastershipListener = new InnerMastershipListener();
+ private final DeviceListener deviceListener = new InnerDeviceListener();
+
+ protected ServiceDirectory serviceDirectory = new DefaultServiceDirectory();
+
+ private Map<Integer, Set<PendingNext>> pendingForwards = Maps.newConcurrentMap();
+
+ private ExecutorService executorService;
+
+ private String policy;
+ private Map<DeviceId, FlowObjectiveCompositionTree> deviceCompositionTreeMap;
+
+ @Activate
+ protected void activate() {
+ executorService = newFixedThreadPool(4, groupedThreads("onos/objective-installer", "%d"));
+ flowObjectiveStore.setDelegate(delegate);
+ mastershipService.addListener(mastershipListener);
+ deviceService.addListener(deviceListener);
+ deviceService.getDevices().forEach(device -> setupPipelineHandler(device.id()));
+ deviceCompositionTreeMap = Maps.newConcurrentMap();
+ log.info("Started");
+ }
+
+ @Deactivate
+ protected void deactivate() {
+ flowObjectiveStore.unsetDelegate(delegate);
+ mastershipService.removeListener(mastershipListener);
+ deviceService.removeListener(deviceListener);
+ executorService.shutdown();
+ pipeliners.clear();
+ driverHandlers.clear();
+ deviceCompositionTreeMap.clear();
+ log.info("Stopped");
+ }
+
+ /**
+ * Task that passes the flow objective down to the driver. The task will
+ * make a few attempts to find the appropriate driver, then eventually give
+ * up and report an error if no suitable driver could be found.
+ */
+ private class ObjectiveInstaller implements Runnable {
+ private final DeviceId deviceId;
+ private final Objective objective;
+
+ private final int numAttempts;
+
+ public ObjectiveInstaller(DeviceId deviceId, Objective objective) {
+ this(deviceId, objective, 1);
+ }
+
+ public ObjectiveInstaller(DeviceId deviceId, Objective objective, int attemps) {
+ this.deviceId = checkNotNull(deviceId);
+ this.objective = checkNotNull(objective);
+ this.numAttempts = checkNotNull(attemps);
+ }
+
+ @Override
+ public void run() {
+ try {
+ Pipeliner pipeliner = getDevicePipeliner(deviceId);
+
+ if (pipeliner != null) {
+ if (objective instanceof NextObjective) {
+ pipeliner.next((NextObjective) objective);
+ } else if (objective instanceof ForwardingObjective) {
+ pipeliner.forward((ForwardingObjective) objective);
+ } else {
+ pipeliner.filter((FilteringObjective) objective);
+ }
+ } else if (numAttempts < INSTALL_RETRY_ATTEMPTS) {
+ Thread.sleep(INSTALL_RETRY_INTERVAL);
+ executorService.submit(new ObjectiveInstaller(deviceId, objective, numAttempts + 1));
+ } else {
+ // Otherwise we've tried a few times and failed, report an
+ // error back to the user.
+ objective.context().ifPresent(
+ c -> c.onError(objective, ObjectiveError.DEVICEMISSING));
+ }
+ } catch (Exception e) {
+ log.warn("Exception while installing flow objective", e);
+ }
+ }
+ }
+
+ @Override
+ public void filter(DeviceId deviceId, FilteringObjective filteringObjective) {
+ checkPermission(Permission.FLOWRULE_WRITE);
+
+ List<FilteringObjective> filteringObjectives
+ = this.deviceCompositionTreeMap.get(deviceId).updateFilter(filteringObjective);
+ for (FilteringObjective tmp : filteringObjectives) {
+ executorService.submit(new ObjectiveInstaller(deviceId, tmp));
+ }
+ }
+
+ @Override
+ public void forward(DeviceId deviceId, ForwardingObjective forwardingObjective) {
+ checkPermission(Permission.FLOWRULE_WRITE);
+
+ if (queueObjective(deviceId, forwardingObjective)) {
+ return;
+ }
+ List<ForwardingObjective> forwardingObjectives
+ = this.deviceCompositionTreeMap.get(deviceId).updateForward(forwardingObjective);
+ for (ForwardingObjective tmp : forwardingObjectives) {
+ executorService.submit(new ObjectiveInstaller(deviceId, tmp));
+ }
+ }
+
+ @Override
+ public void next(DeviceId deviceId, NextObjective nextObjective) {
+ checkPermission(Permission.FLOWRULE_WRITE);
+
+ List<NextObjective> nextObjectives = this.deviceCompositionTreeMap.get(deviceId).updateNext(nextObjective);
+ for (NextObjective tmp : nextObjectives) {
+ executorService.submit(new ObjectiveInstaller(deviceId, tmp));
+ }
+ }
+
+ @Override
+ public int allocateNextId() {
+ checkPermission(Permission.FLOWRULE_WRITE);
+
+ return flowObjectiveStore.allocateNextId();
+ }
+
+ private boolean queueObjective(DeviceId deviceId, ForwardingObjective fwd) {
+ if (fwd.nextId() != null &&
+ flowObjectiveStore.getNextGroup(fwd.nextId()) == null) {
+ log.trace("Queuing forwarding objective for nextId {}", fwd.nextId());
+ if (pendingForwards.putIfAbsent(fwd.nextId(),
+ Sets.newHashSet(new PendingNext(deviceId, fwd))) != null) {
+ Set<PendingNext> pending = pendingForwards.get(fwd.nextId());
+ pending.add(new PendingNext(deviceId, fwd));
+ }
+ return true;
+ }
+ return false;
+ }
+
+ @Override
+ public void initPolicy(String policy) {
+ this.policy = policy;
+ deviceService.getDevices().forEach(device ->
+ this.deviceCompositionTreeMap.put(device.id(), FlowObjectiveCompositionUtil.parsePolicyString(policy)));
+ log.info("Initialize policy {}", policy);
+ }
+
+ // Retrieves the device pipeline behaviour from the cache.
+ private Pipeliner getDevicePipeliner(DeviceId deviceId) {
+ Pipeliner pipeliner = pipeliners.get(deviceId);
+ return pipeliner;
+ }
+
+ private void setupPipelineHandler(DeviceId deviceId) {
+ if (defaultDriverService == null) {
+ // We're not ready to go to work yet.
+ return;
+ }
+
+ // Attempt to lookup the handler in the cache
+ DriverHandler handler = driverHandlers.get(deviceId);
+ if (handler == null) {
+ try {
+ // Otherwise create it and if it has pipeline behaviour, cache it
+ handler = driverService.createHandler(deviceId);
+ if (!handler.driver().hasBehaviour(Pipeliner.class)) {
+ log.warn("Pipeline behaviour not supported for device {}",
+ deviceId);
+ return;
+ }
+ } catch (ItemNotFoundException e) {
+ log.warn("No applicable driver for device {}", deviceId);
+ return;
+ }
+
+ driverHandlers.put(deviceId, handler);
+ }
+
+ // Always (re)initialize the pipeline behaviour
+ log.info("Driver {} bound to device {} ... initializing driver",
+ handler.driver().name(), deviceId);
+ Pipeliner pipeliner = handler.behaviour(Pipeliner.class);
+ pipeliner.init(deviceId, context);
+ pipeliners.putIfAbsent(deviceId, pipeliner);
+ }
+
+ // Triggers driver setup when the local node becomes a device master.
+ private class InnerMastershipListener implements MastershipListener {
+ @Override
+ public void event(MastershipEvent event) {
+ switch (event.type()) {
+ case MASTER_CHANGED:
+ log.debug("mastership changed on device {}", event.subject());
+ if (deviceService.isAvailable(event.subject())) {
+ setupPipelineHandler(event.subject());
+ }
+ break;
+ case BACKUPS_CHANGED:
+ break;
+ default:
+ break;
+ }
+ }
+ }
+
+ // Triggers driver setup when a device is (re)detected.
+ private class InnerDeviceListener implements DeviceListener {
+ @Override
+ public void event(DeviceEvent event) {
+ switch (event.type()) {
+ case DEVICE_ADDED:
+ case DEVICE_AVAILABILITY_CHANGED:
+ log.debug("Device either added or availability changed {}",
+ event.subject().id());
+ if (deviceService.isAvailable(event.subject().id())) {
+ log.debug("Device is now available {}", event.subject().id());
+ setupPipelineHandler(event.subject().id());
+ }
+ break;
+ case DEVICE_UPDATED:
+ break;
+ case DEVICE_REMOVED:
+ break;
+ case DEVICE_SUSPENDED:
+ break;
+ case PORT_ADDED:
+ break;
+ case PORT_UPDATED:
+ break;
+ case PORT_REMOVED:
+ break;
+ default:
+ break;
+ }
+ }
+ }
+
+ // Processing context for initializing pipeline driver behaviours.
+ private class InnerPipelineContext implements PipelinerContext {
+ @Override
+ public ServiceDirectory directory() {
+ return serviceDirectory;
+ }
+
+ @Override
+ public FlowObjectiveStore store() {
+ return flowObjectiveStore;
+ }
+ }
+
+ private class InternalStoreDelegate implements FlowObjectiveStoreDelegate {
+ @Override
+ public void notify(ObjectiveEvent event) {
+ log.debug("Received notification of obj event {}", event);
+ Set<PendingNext> pending = pendingForwards.remove(event.subject());
+
+ if (pending == null) {
+ log.debug("Nothing pending for this obj event");
+ return;
+ }
+
+ log.debug("Processing pending forwarding objectives {}", pending.size());
+
+ pending.forEach(p -> getDevicePipeliner(p.deviceId())
+ .forward(p.forwardingObjective()));
+
+ }
+ }
+
+ /**
+ * Data class used to hold a pending forwarding objective that could not
+ * be processed because the associated next object was not present.
+ */
+ private class PendingNext {
+ private final DeviceId deviceId;
+ private final ForwardingObjective fwd;
+
+ public PendingNext(DeviceId deviceId, ForwardingObjective fwd) {
+ this.deviceId = deviceId;
+ this.fwd = fwd;
+ }
+
+ public DeviceId deviceId() {
+ return deviceId;
+ }
+
+ public ForwardingObjective forwardingObjective() {
+ return fwd;
+ }
+ }
+
+ public static String forwardingObjectiveToString(ForwardingObjective forwardingObjective) {
+ String str = forwardingObjective.priority() + " ";
+ str += "selector( ";
+ for (Criterion criterion : forwardingObjective.selector().criteria()) {
+ str += criterion + " ";
+ }
+ str += ") treatment( ";
+ for (Instruction instruction : forwardingObjective.treatment().allInstructions()) {
+ str += instruction + " ";
+ }
+ str += ")";
+ return str;
+ }
+}
diff --git a/core/net/src/main/java/org/onosproject/net/flowobjective/impl/composition/FlowObjectiveCompositionTree.java b/core/net/src/main/java/org/onosproject/net/flowobjective/impl/composition/FlowObjectiveCompositionTree.java
new file mode 100644
index 0000000..152622b
--- /dev/null
+++ b/core/net/src/main/java/org/onosproject/net/flowobjective/impl/composition/FlowObjectiveCompositionTree.java
@@ -0,0 +1,271 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.net.flowobjective.impl.composition;
+
+import org.onosproject.net.flowobjective.FilteringObjective;
+import org.onosproject.net.flowobjective.ForwardingObjective;
+import org.onosproject.net.flowobjective.NextObjective;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+
+/**
+ * Provides a policy tree to store all flow tables for each device.
+ *
+ * Note: This class uses in-memory structures and is not yet distributed.
+ */
+public class FlowObjectiveCompositionTree {
+
+ public FlowObjectiveCompositionManager.PolicyOperator operator;
+ public FlowObjectiveCompositionTree leftChild;
+ public FlowObjectiveCompositionTree rightChild;
+ public short applicationId;
+ protected FilterTable filterTable;
+ protected ForwardTable forwardTable;
+ protected NextTable nextTable;
+
+ protected int priorityMultiplier;
+ protected int priorityAddend;
+
+ public FlowObjectiveCompositionTree(short applicationId) {
+ this.operator = FlowObjectiveCompositionManager.PolicyOperator.Application;
+ this.leftChild = null;
+ this.rightChild = null;
+ this.applicationId = applicationId;
+ this.filterTable = new FilterTable();
+ this.forwardTable = new ForwardTable();
+ this.nextTable = new NextTable();
+ this.priorityMultiplier = 10;
+ this.priorityAddend = 10;
+ }
+
+ public FlowObjectiveCompositionTree(Character ch) {
+ switch (ch) {
+ case '+':
+ this.operator = FlowObjectiveCompositionManager.PolicyOperator.Parallel;
+ break;
+ case '>':
+ this.operator = FlowObjectiveCompositionManager.PolicyOperator.Sequential;
+ break;
+ case '/':
+ this.operator = FlowObjectiveCompositionManager.PolicyOperator.Override;
+ break;
+ default:
+ this.operator = FlowObjectiveCompositionManager.PolicyOperator.Application;
+ break;
+ }
+ this.leftChild = null;
+ this.rightChild = null;
+ this.applicationId = (short) -1;
+ this.filterTable = new FilterTable();
+ this.forwardTable = new ForwardTable();
+ this.nextTable = new NextTable();
+ this.priorityMultiplier = 10;
+ this.priorityAddend = 10;
+ }
+
+ protected List<FilteringObjective> updateFilter(FilteringObjective filteringObjective) {
+ switch (this.operator) {
+ case Parallel:
+ return updateFilterParallel(filteringObjective);
+ case Sequential:
+ return updateFilterSequential(filteringObjective);
+ case Override:
+ return updateFilterOverride(filteringObjective);
+ case Application:
+ if (filteringObjective.appId().id() == this.applicationId) {
+ return this.filterTable.updateFilter(filteringObjective);
+ } else {
+ return new ArrayList<>();
+ }
+ default:
+ return new ArrayList<>();
+ }
+ }
+
+ // Parallel composition: the filter set is the union of the children
+ protected List<FilteringObjective> updateFilterParallel(FilteringObjective filteringObjective) {
+ List<FilteringObjective> leftUpdates = this.leftChild.updateFilter(filteringObjective);
+ List<FilteringObjective> rightUpdates = this.rightChild.updateFilter(filteringObjective);
+
+ List<FilteringObjective> updates = new ArrayList<>();
+ updates.addAll(leftUpdates);
+ updates.addAll(rightUpdates);
+
+ return this.filterTable.updateFilter(updates);
+ }
+
+ // Sequential composition: the filter set is the filter set of the left child
+ protected List<FilteringObjective> updateFilterSequential(FilteringObjective filteringObjective) {
+ List<FilteringObjective> leftUpdates = this.leftChild.updateFilter(filteringObjective);
+ List<FilteringObjective> rightUpdates = this.rightChild.updateFilter(filteringObjective);
+ return this.filterTable.updateFilter(leftUpdates);
+ }
+
+ // Override composition: the filter set is the filter set of the left child
+ protected List<FilteringObjective> updateFilterOverride(FilteringObjective filteringObjective) {
+ List<FilteringObjective> leftUpdates = this.leftChild.updateFilter(filteringObjective);
+ List<FilteringObjective> rightUpdates = this.rightChild.updateFilter(filteringObjective);
+ return this.filterTable.updateFilter(leftUpdates);
+ }
+
+ public List<ForwardingObjective> updateForward(ForwardingObjective forwardingObjective) {
+ return this.updateForwardNode(forwardingObjective).toForwardingObjectiveList();
+ }
+
+ public ForwardUpdateTable updateForwardNode(ForwardingObjective forwardingObjective) {
+ switch (this.operator) {
+ case Parallel:
+ case Sequential:
+ case Override:
+ return updateForwardComposition(forwardingObjective);
+ case Application:
+ if (forwardingObjective.appId().id() == this.applicationId) {
+ return this.forwardTable.updateForward(forwardingObjective);
+ } else {
+ return (new ForwardUpdateTable());
+ }
+ default:
+ return (new ForwardUpdateTable());
+ }
+ }
+
+ protected ForwardUpdateTable updateForwardComposition(ForwardingObjective forwardingObjective) {
+ ForwardUpdateTable leftUpdates = this.leftChild.updateForwardNode(forwardingObjective);
+ ForwardUpdateTable rightUpdates = this.rightChild.updateForwardNode(forwardingObjective);
+
+ List<ForwardingObjective> addUpdates = new ArrayList<>();
+ List<ForwardingObjective> removeUpdates = new ArrayList<>();
+ // Handle ADD
+ if (this.operator == FlowObjectiveCompositionManager.PolicyOperator.Parallel
+ || this.operator == FlowObjectiveCompositionManager.PolicyOperator.Sequential) {
+ for (ForwardingObjective fo1 : leftUpdates.addObjectives) {
+ for (ForwardingObjective fo2 : this.rightChild.forwardTable.getForwardingObjectives()) {
+ ForwardingObjective composedFo = null;
+ if (this.operator == FlowObjectiveCompositionManager.PolicyOperator.Parallel) {
+ composedFo = FlowObjectiveCompositionUtil.composeParallel(fo1, fo2);
+ } else {
+ composedFo = FlowObjectiveCompositionUtil.composeSequential(fo1, fo2, this.priorityMultiplier);
+ }
+ if (composedFo != null) {
+ addUpdates.add(composedFo);
+ this.leftChild.forwardTable.addGeneratedParentForwardingObjective(fo1, composedFo);
+ this.rightChild.forwardTable.addGeneratedParentForwardingObjective(fo2, composedFo);
+ }
+ }
+ }
+ Collection<ForwardingObjective> leftTableWithoutAdd = FlowObjectiveCompositionUtil
+ .minusForwardingObjectives(this.leftChild.forwardTable.getForwardingObjectives(),
+ leftUpdates.addObjectives);
+ for (ForwardingObjective fo1 : leftTableWithoutAdd) {
+ for (ForwardingObjective fo2 : rightUpdates.addObjectives) {
+ ForwardingObjective composedFo = null;
+ if (this.operator == FlowObjectiveCompositionManager.PolicyOperator.Parallel) {
+ composedFo = FlowObjectiveCompositionUtil.composeParallel(fo1, fo2);
+ } else {
+ composedFo = FlowObjectiveCompositionUtil.composeSequential(fo1, fo2, this.priorityMultiplier);
+ }
+ if (composedFo != null) {
+ addUpdates.add(composedFo);
+ this.leftChild.forwardTable.addGeneratedParentForwardingObjective(fo1, composedFo);
+ this.rightChild.forwardTable.addGeneratedParentForwardingObjective(fo2, composedFo);
+ }
+ }
+ }
+ } else {
+ for (ForwardingObjective fo : leftUpdates.addObjectives) {
+ ForwardingObjective composedFo = FlowObjectiveCompositionUtil.composeOverride(fo, this.priorityAddend);
+ addUpdates.add(composedFo);
+ this.leftChild.forwardTable.addGeneratedParentForwardingObjective(fo, composedFo);
+ }
+ for (ForwardingObjective fo : rightUpdates.addObjectives) {
+ ForwardingObjective composedFo = FlowObjectiveCompositionUtil.composeOverride(fo, 0);
+ addUpdates.add(composedFo);
+ this.rightChild.forwardTable.addGeneratedParentForwardingObjective(fo, composedFo);
+ }
+ }
+
+ // Handle REMOVE
+ for (ForwardingObjective fo : leftUpdates.removeObjectives) {
+ List<ForwardingObjective> fos = this.leftChild.forwardTable
+ .getGeneratedParentForwardingObjectiveForRemove(fo);
+ removeUpdates.addAll(fos);
+ }
+ this.leftChild.forwardTable.deleteGeneratedParentForwardingObjective(leftUpdates.removeObjectives);
+ for (ForwardingObjective fo : rightUpdates.removeObjectives) {
+ List<ForwardingObjective> fos = this.rightChild.forwardTable
+ .getGeneratedParentForwardingObjectiveForRemove(fo);
+ removeUpdates.addAll(fos);
+ }
+ this.rightChild.forwardTable.deleteGeneratedParentForwardingObjective(rightUpdates.removeObjectives);
+
+ ForwardUpdateTable updates = new ForwardUpdateTable();
+ updates.addUpdateTable(this.forwardTable.updateForward(addUpdates));
+ updates.addUpdateTable(this.forwardTable.updateForward(removeUpdates));
+ return updates;
+ }
+
+ public List<NextObjective> updateNext(NextObjective nextObjective) {
+ switch (this.operator) {
+ case Parallel:
+ case Sequential:
+ case Override:
+ return updateNextComposition(nextObjective);
+ case Application:
+ if (nextObjective.appId().id() == this.applicationId) {
+ return this.nextTable.updateNext(nextObjective);
+ } else {
+ return new ArrayList<>();
+ }
+ default:
+ return new ArrayList<>();
+ }
+ }
+
+ // Next: the union of the children
+ protected List<NextObjective> updateNextComposition(NextObjective nextObjective) {
+ List<NextObjective> leftUpdates = this.leftChild.updateNext(nextObjective);
+ List<NextObjective> rightUpdates = this.rightChild.updateNext(nextObjective);
+
+ List<NextObjective> updates = new ArrayList<>();
+ updates.addAll(leftUpdates);
+ updates.addAll(rightUpdates);
+
+ return this.nextTable.updateNext(updates);
+ }
+
+ @Override
+ public String toString() {
+ String str = null;
+ switch (this.operator) {
+ case Parallel:
+ str = "(" + this.leftChild + "+" + this.rightChild + ")";
+ break;
+ case Sequential:
+ str = "(" + this.leftChild + ">" + this.rightChild + ")";
+ break;
+ case Override:
+ str = "(" + this.leftChild + "/" + this.rightChild + ")";
+ break;
+ default:
+ str = " " + applicationId + " ";
+ break;
+ }
+ return str;
+ }
+
+}
diff --git a/core/net/src/main/java/org/onosproject/net/flowobjective/impl/composition/FlowObjectiveCompositionUtil.java b/core/net/src/main/java/org/onosproject/net/flowobjective/impl/composition/FlowObjectiveCompositionUtil.java
new file mode 100644
index 0000000..0cc383b
--- /dev/null
+++ b/core/net/src/main/java/org/onosproject/net/flowobjective/impl/composition/FlowObjectiveCompositionUtil.java
@@ -0,0 +1,488 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.net.flowobjective.impl.composition;
+
+import org.onlab.packet.IpPrefix;
+import org.onosproject.net.flow.DefaultTrafficSelector;
+import org.onosproject.net.flow.DefaultTrafficTreatment;
+import org.onosproject.net.flow.TrafficSelector;
+import org.onosproject.net.flow.TrafficTreatment;
+import org.onosproject.net.flow.criteria.Criterion;
+import org.onosproject.net.flow.criteria.LambdaCriterion;
+import org.onosproject.net.flow.criteria.OchSignalCriterion;
+import org.onosproject.net.flow.criteria.EthCriterion;
+import org.onosproject.net.flow.criteria.VlanIdCriterion;
+import org.onosproject.net.flow.criteria.VlanPcpCriterion;
+import org.onosproject.net.flow.criteria.MplsCriterion;
+import org.onosproject.net.flow.criteria.IPCriterion;
+import org.onosproject.net.flow.criteria.Criteria;
+import org.onosproject.net.flow.instructions.Instruction;
+import org.onosproject.net.flow.instructions.L0ModificationInstruction;
+import org.onosproject.net.flow.instructions.L2ModificationInstruction;
+import org.onosproject.net.flow.instructions.L3ModificationInstruction;
+import org.onosproject.net.flowobjective.DefaultForwardingObjective;
+import org.onosproject.net.flowobjective.ForwardingObjective;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.Stack;
+
+/**
+ * Provide util functions for FlowObjectiveComposition.
+ */
+public final class FlowObjectiveCompositionUtil {
+
+ private FlowObjectiveCompositionUtil() {}
+
+ // only work with VERSATILE
+ public static ForwardingObjective composeParallel(ForwardingObjective fo1, ForwardingObjective fo2) {
+
+ TrafficSelector trafficSelector = intersectTrafficSelector(fo1.selector(), fo2.selector());
+ if (trafficSelector == null) {
+ return null;
+ }
+
+ TrafficTreatment trafficTreatment = unionTrafficTreatment(fo1.treatment(), fo2.treatment());
+
+ return DefaultForwardingObjective.builder()
+ .fromApp(fo1.appId())
+ .makePermanent()
+ .withFlag(ForwardingObjective.Flag.VERSATILE)
+ .withPriority(fo1.priority() + fo2.priority())
+ .withSelector(trafficSelector)
+ .withTreatment(trafficTreatment)
+ .add();
+ }
+
+ public static ForwardingObjective composeSequential(ForwardingObjective fo1,
+ ForwardingObjective fo2,
+ int priorityMultiplier) {
+
+ TrafficSelector revertTrafficSelector = revertTreatmentSelector(fo1.treatment(), fo2.selector());
+ if (revertTrafficSelector == null) {
+ return null;
+ }
+
+ TrafficSelector trafficSelector = intersectTrafficSelector(fo1.selector(), revertTrafficSelector);
+ if (trafficSelector == null) {
+ return null;
+ }
+
+ TrafficTreatment trafficTreatment = unionTrafficTreatment(fo1.treatment(), fo2.treatment());
+
+ return DefaultForwardingObjective.builder()
+ .fromApp(fo1.appId())
+ .makePermanent()
+ .withFlag(ForwardingObjective.Flag.VERSATILE)
+ .withPriority(fo1.priority() * priorityMultiplier + fo2.priority())
+ .withSelector(trafficSelector)
+ .withTreatment(trafficTreatment)
+ .add();
+ }
+
+ public static ForwardingObjective composeOverride(ForwardingObjective fo, int priorityAddend) {
+ return DefaultForwardingObjective.builder()
+ .fromApp(fo.appId())
+ .makePermanent()
+ .withFlag(fo.flag())
+ .withPriority(fo.priority() + priorityAddend)
+ .withSelector(fo.selector())
+ .withTreatment(fo.treatment())
+ .add();
+ }
+
+ public static TrafficSelector intersectTrafficSelector(TrafficSelector ts1, TrafficSelector ts2) {
+
+ TrafficSelector.Builder selectorBuilder = DefaultTrafficSelector.builder();
+
+ Set<Criterion.Type> ts1IntersectTs2 = getTypeSet(ts1);
+ ts1IntersectTs2.retainAll(getTypeSet(ts2));
+ for (Criterion.Type type : ts1IntersectTs2) {
+ Criterion criterion = intersectCriterion(ts1.getCriterion(type), ts2.getCriterion(type));
+ if (criterion == null) {
+ return null;
+ } else {
+ selectorBuilder.add(criterion);
+ }
+ }
+
+ Set<Criterion.Type> ts1MinusTs2 = getTypeSet(ts1);
+ ts1MinusTs2.removeAll(getTypeSet(ts2));
+ for (Criterion.Type type : ts1MinusTs2) {
+ selectorBuilder.add(ts1.getCriterion(type));
+ }
+
+ Set<Criterion.Type> ts2MinusTs1 = getTypeSet(ts2);
+ ts2MinusTs1.removeAll(getTypeSet(ts1));
+ for (Criterion.Type type : ts2MinusTs1) {
+ selectorBuilder.add(ts2.getCriterion(type));
+ }
+
+ return selectorBuilder.build();
+ }
+
+ public static TrafficTreatment unionTrafficTreatment(TrafficTreatment tt1, TrafficTreatment tt2) {
+
+ TrafficTreatment.Builder treatmentBuilder = DefaultTrafficTreatment.builder();
+
+ for (Instruction instruction : tt1.allInstructions()) {
+ treatmentBuilder.add(instruction);
+ }
+
+ for (Instruction instruction : tt2.allInstructions()) {
+ treatmentBuilder.add(instruction);
+ }
+
+ return treatmentBuilder.build();
+ }
+
+ public static TrafficSelector revertTreatmentSelector(TrafficTreatment trafficTreatment,
+ TrafficSelector trafficSelector) {
+
+ TrafficSelector.Builder selectorBuilder = DefaultTrafficSelector.builder();
+
+ Map<Criterion.Type, Criterion> criterionMap = new HashMap<>();
+ for (Criterion criterion : trafficSelector.criteria()) {
+ criterionMap.put(criterion.type(), criterion);
+ }
+
+ for (Instruction instruction : trafficTreatment.allInstructions()) {
+ switch (instruction.type()) {
+ case DROP:
+ return null;
+ case OUTPUT:
+ break;
+ case GROUP:
+ break;
+ case L0MODIFICATION: {
+ L0ModificationInstruction l0 = (L0ModificationInstruction) instruction;
+ switch (l0.subtype()) {
+ case LAMBDA:
+ if (criterionMap.containsKey(Criterion.Type.OCH_SIGID)) {
+ if (((LambdaCriterion) criterionMap.get((Criterion.Type.OCH_SIGID))).lambda()
+ == ((L0ModificationInstruction.ModLambdaInstruction) l0).lambda()) {
+ criterionMap.remove(Criterion.Type.OCH_SIGID);
+ } else {
+ return null;
+ }
+ } else {
+ break;
+ }
+ case OCH:
+ if (criterionMap.containsKey(Criterion.Type.OCH_SIGID)) {
+ if (((OchSignalCriterion) criterionMap.get((Criterion.Type.OCH_SIGID))).lambda()
+ .equals(((L0ModificationInstruction.ModOchSignalInstruction) l0).lambda())) {
+ criterionMap.remove(Criterion.Type.OCH_SIGID);
+ } else {
+ return null;
+ }
+ } else {
+ break;
+ }
+ default:
+ break;
+ }
+ break;
+ }
+ case L2MODIFICATION: {
+ L2ModificationInstruction l2 = (L2ModificationInstruction) instruction;
+ switch (l2.subtype()) {
+ case ETH_SRC:
+ if (criterionMap.containsKey(Criterion.Type.ETH_SRC)) {
+ if (((EthCriterion) criterionMap.get((Criterion.Type.ETH_SRC))).mac()
+ .equals(((L2ModificationInstruction.ModEtherInstruction) l2).mac())) {
+ criterionMap.remove(Criterion.Type.ETH_SRC);
+ } else {
+ return null;
+ }
+ } else {
+ break;
+ }
+ case ETH_DST:
+ if (criterionMap.containsKey(Criterion.Type.ETH_DST)) {
+ if (((EthCriterion) criterionMap.get((Criterion.Type.ETH_DST))).mac()
+ .equals(((L2ModificationInstruction.ModEtherInstruction) l2).mac())) {
+ criterionMap.remove(Criterion.Type.ETH_DST);
+ } else {
+ return null;
+ }
+ } else {
+ break;
+ }
+ case VLAN_ID:
+ if (criterionMap.containsKey(Criterion.Type.VLAN_VID)) {
+ if (((VlanIdCriterion) criterionMap.get((Criterion.Type.VLAN_VID))).vlanId()
+ .equals(((L2ModificationInstruction.ModVlanIdInstruction) l2).vlanId())) {
+ criterionMap.remove(Criterion.Type.VLAN_VID);
+ } else {
+ return null;
+ }
+ } else {
+ break;
+ }
+ case VLAN_PCP:
+ if (criterionMap.containsKey(Criterion.Type.VLAN_PCP)) {
+ if (((VlanPcpCriterion) criterionMap.get((Criterion.Type.VLAN_PCP))).priority()
+ == ((L2ModificationInstruction.ModVlanPcpInstruction) l2).vlanPcp()) {
+ criterionMap.remove(Criterion.Type.VLAN_PCP);
+ } else {
+ return null;
+ }
+ } else {
+ break;
+ }
+ case MPLS_LABEL:
+ if (criterionMap.containsKey(Criterion.Type.MPLS_LABEL)) {
+ if (((MplsCriterion) criterionMap.get((Criterion.Type.MPLS_LABEL))).label()
+ .equals(((L2ModificationInstruction.ModMplsLabelInstruction) l2).label())) {
+ criterionMap.remove(Criterion.Type.ETH_DST);
+ } else {
+ return null;
+ }
+ } else {
+ break;
+ }
+ default:
+ break;
+ }
+ break;
+ }
+ case TABLE:
+ break;
+ case L3MODIFICATION: {
+ L3ModificationInstruction l3 = (L3ModificationInstruction) instruction;
+ switch (l3.subtype()) {
+ case IPV4_SRC:
+ if (criterionMap.containsKey(Criterion.Type.IPV4_SRC)) {
+ if (((IPCriterion) criterionMap.get(Criterion.Type.IPV4_SRC)).ip()
+ .contains(((L3ModificationInstruction.ModIPInstruction) l3).ip())) {
+ criterionMap.remove(Criterion.Type.IPV4_SRC);
+ } else {
+ return null;
+ }
+ } else {
+ break;
+ }
+ case IPV4_DST:
+ if (criterionMap.containsKey(Criterion.Type.IPV4_DST)) {
+ if (((IPCriterion) criterionMap.get(Criterion.Type.IPV4_DST)).ip()
+ .contains(((L3ModificationInstruction.ModIPInstruction) l3).ip())) {
+ criterionMap.remove(Criterion.Type.IPV4_DST);
+ } else {
+ return null;
+ }
+ } else {
+ break;
+ }
+ case IPV6_SRC:
+ if (criterionMap.containsKey(Criterion.Type.IPV6_SRC)) {
+ if (((IPCriterion) criterionMap.get(Criterion.Type.IPV6_SRC)).ip()
+ .contains(((L3ModificationInstruction.ModIPInstruction) l3).ip())) {
+ criterionMap.remove(Criterion.Type.IPV6_SRC);
+ } else {
+ return null;
+ }
+ } else {
+ break;
+ }
+ case IPV6_DST:
+ if (criterionMap.containsKey(Criterion.Type.IPV6_DST)) {
+ if (((IPCriterion) criterionMap.get(Criterion.Type.IPV6_DST)).ip()
+ .contains(((L3ModificationInstruction.ModIPInstruction) l3).ip())) {
+ criterionMap.remove(Criterion.Type.IPV6_DST);
+ } else {
+ return null;
+ }
+ } else {
+ break;
+ }
+ case IPV6_FLABEL:
+ if (criterionMap.containsKey(Criterion.Type.IPV6_FLABEL)) {
+ if (((IPCriterion) criterionMap.get(Criterion.Type.IPV6_FLABEL)).ip()
+ .equals(((L3ModificationInstruction.ModIPv6FlowLabelInstruction) l3)
+ .flowLabel())) {
+ criterionMap.remove(Criterion.Type.IPV4_SRC);
+ } else {
+ return null;
+ }
+ } else {
+ break;
+ }
+ default:
+ break;
+ }
+ break;
+ }
+ case METADATA:
+ break;
+ default:
+ break;
+ }
+ }
+
+ for (Criterion criterion : criterionMap.values()) {
+ selectorBuilder.add(criterion);
+ }
+
+ return selectorBuilder.build();
+ }
+
+ public static Set<Criterion.Type> getTypeSet(TrafficSelector trafficSelector) {
+ Set<Criterion.Type> typeSet = new HashSet<>();
+ for (Criterion criterion : trafficSelector.criteria()) {
+ typeSet.add(criterion.type());
+ }
+ return typeSet;
+ }
+
+ public static Criterion intersectCriterion(Criterion c1, Criterion c2) {
+ switch (c1.type()) {
+ case IPV4_SRC: {
+ IpPrefix ipPrefix = intersectIpPrefix(((IPCriterion) c1).ip(), ((IPCriterion) c2).ip());
+ if (ipPrefix == null) {
+ return null;
+ } else {
+ return Criteria.matchIPSrc(ipPrefix);
+ }
+ }
+ case IPV4_DST: {
+ IpPrefix ipPrefix = intersectIpPrefix(((IPCriterion) c1).ip(), ((IPCriterion) c2).ip());
+ if (ipPrefix == null) {
+ return null;
+ } else {
+ return Criteria.matchIPDst(ipPrefix);
+ }
+ }
+ case IPV6_SRC: {
+ IpPrefix ipPrefix = intersectIpPrefix(((IPCriterion) c1).ip(), ((IPCriterion) c2).ip());
+ if (ipPrefix == null) {
+ return null;
+ } else {
+ return Criteria.matchIPv6Src(ipPrefix);
+ }
+ }
+ case IPV6_DST: {
+ IpPrefix ipPrefix = intersectIpPrefix(((IPCriterion) c1).ip(), ((IPCriterion) c2).ip());
+ if (ipPrefix == null) {
+ return null;
+ } else {
+ return Criteria.matchIPv6Dst(ipPrefix);
+ }
+ }
+ default:
+ if (!c1.equals(c2)) {
+ return null;
+ } else {
+ return c1;
+ }
+ }
+ }
+
+ public static IpPrefix intersectIpPrefix(IpPrefix ip1, IpPrefix ip2) {
+ if (ip1.contains(ip2)) {
+ return ip1;
+ } else if (ip2.contains(ip1)) {
+ return ip2;
+ } else {
+ return null;
+ }
+ }
+
+ public static FlowObjectiveCompositionTree parsePolicyString(String policy) {
+ List<FlowObjectiveCompositionTree> postfix = transformToPostfixForm(policy);
+ return buildPolicyTree(postfix);
+ }
+
+ private static List<FlowObjectiveCompositionTree> transformToPostfixForm(String policy) {
+ Stack<Character> stack = new Stack<>();
+ List<FlowObjectiveCompositionTree> postfix = new ArrayList<>();
+
+ for (int i = 0; i < policy.length(); i++) {
+ Character ch = policy.charAt(i);
+ if (Character.isDigit(ch)) {
+
+ int applicationId = ch - '0';
+ while (i + 1 < policy.length() && Character.isDigit(policy.charAt(i + 1))) {
+ i++;
+ applicationId = applicationId * 10 + policy.charAt(i) - '0';
+ }
+
+ postfix.add(new FlowObjectiveCompositionTree((short) applicationId));
+ } else if (ch == '(') {
+ stack.push(ch);
+ } else if (ch == ')') {
+ while (stack.peek() != '(') {
+ postfix.add(new FlowObjectiveCompositionTree(stack.pop()));
+ }
+ stack.pop();
+ } else {
+ while (!stack.isEmpty() && compareOperatorPriority(stack.peek(), ch)) {
+ postfix.add(new FlowObjectiveCompositionTree(stack.pop()));
+ }
+ stack.push(ch);
+ }
+ }
+ while (!stack.isEmpty()) {
+ postfix.add(new FlowObjectiveCompositionTree(stack.pop()));
+ }
+
+ return postfix;
+ }
+
+ private static boolean compareOperatorPriority(char peek, char cur) {
+ if (peek == '/' && (cur == '+' || cur == '>' || cur == '/')) {
+ return true;
+ } else if (peek == '>' && (cur == '+' || cur == '>')) {
+ return true;
+ } else if (peek == '+' && cur == '+') {
+ return true;
+ }
+ return false;
+ }
+
+ private static FlowObjectiveCompositionTree buildPolicyTree(List<FlowObjectiveCompositionTree> postfix) {
+ Stack<FlowObjectiveCompositionTree> stack = new Stack<>();
+ for (FlowObjectiveCompositionTree node : postfix) {
+ if (node.operator == FlowObjectiveCompositionManager.PolicyOperator.Application) {
+ stack.push(node);
+ } else {
+ node.rightChild = stack.pop();
+ node.leftChild = stack.pop();
+ stack.push(node);
+ }
+ }
+ return stack.pop();
+ }
+
+ public static Collection<ForwardingObjective> minusForwardingObjectives(Collection<ForwardingObjective> fo1,
+ Collection<ForwardingObjective> fo2) {
+ Map<Integer, ForwardingObjective> map = new HashMap<>();
+ for (ForwardingObjective fo : fo1) {
+ map.put(fo.id(), fo);
+ }
+ for (ForwardingObjective fo : fo2) {
+ map.remove(fo.id());
+ }
+ return map.values();
+ }
+
+
+}
diff --git a/core/net/src/main/java/org/onosproject/net/flowobjective/impl/composition/ForwardTable.java b/core/net/src/main/java/org/onosproject/net/flowobjective/impl/composition/ForwardTable.java
new file mode 100644
index 0000000..1384bbe
--- /dev/null
+++ b/core/net/src/main/java/org/onosproject/net/flowobjective/impl/composition/ForwardTable.java
@@ -0,0 +1,109 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.net.flowobjective.impl.composition;
+
+import org.onosproject.net.flowobjective.DefaultForwardingObjective;
+import org.onosproject.net.flowobjective.ForwardingObjective;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+
+/**
+ * Provides a table to store Forward.
+ */
+public class ForwardTable {
+
+ protected Map<Integer, ForwardingObjective> forwardMap;
+ protected Map<Integer, List<ForwardingObjective>> generatedParentForwardingObjectiveMap;
+
+ public ForwardTable() {
+ this.forwardMap = new HashMap<>();
+ this.generatedParentForwardingObjectiveMap = new HashMap<>();
+ }
+
+ public ForwardUpdateTable updateForward(ForwardingObjective forwardingObjective) {
+ ForwardUpdateTable updates = new ForwardUpdateTable();
+ switch (forwardingObjective.op()) {
+ case ADD:
+ this.forwardMap.put(forwardingObjectiveHash(forwardingObjective), forwardingObjective);
+ this.generatedParentForwardingObjectiveMap
+ .put(forwardingObjectiveHash(forwardingObjective), new ArrayList<>());
+ updates.addObjectives.add(forwardingObjective);
+ break;
+ case REMOVE:
+ if (this.forwardMap.remove(forwardingObjectiveHash(forwardingObjective)) != null) {
+ updates.removeObjectives.add(forwardingObjective);
+ }
+ break;
+ default:
+ break;
+ }
+ return updates;
+ }
+
+ public ForwardUpdateTable updateForward(List<ForwardingObjective> forwardingObjectives) {
+ ForwardUpdateTable updates = new ForwardUpdateTable();
+ for (ForwardingObjective forwardingObjective : forwardingObjectives) {
+ updates.addUpdateTable(this.updateForward(forwardingObjective));
+ }
+ return updates;
+ }
+
+ public void addGeneratedParentForwardingObjective(ForwardingObjective child, ForwardingObjective parent) {
+ this.generatedParentForwardingObjectiveMap.get(forwardingObjectiveHash(child)).add(parent);
+ }
+
+ public void deleteGeneratedParentForwardingObjective(List<ForwardingObjective> children) {
+ for (ForwardingObjective fo : children) {
+ this.generatedParentForwardingObjectiveMap.remove(forwardingObjectiveHash(fo));
+ }
+ }
+
+ private List<ForwardingObjective> getGeneratedParentForwardingObjective(ForwardingObjective child) {
+ return this.generatedParentForwardingObjectiveMap.get(forwardingObjectiveHash(child));
+ }
+
+ public List<ForwardingObjective> getGeneratedParentForwardingObjectiveForRemove(ForwardingObjective child) {
+ List<ForwardingObjective> fos = this.generatedParentForwardingObjectiveMap.get(forwardingObjectiveHash(child));
+ List<ForwardingObjective> removeFos = new ArrayList<>();
+ for (ForwardingObjective fo : fos) {
+ removeFos.add(DefaultForwardingObjective.builder()
+ .fromApp(fo.appId())
+ .makePermanent()
+ .withFlag(fo.flag())
+ .withPriority(fo.priority())
+ .withSelector(fo.selector())
+ .withTreatment(fo.treatment())
+ .remove());
+ }
+ return removeFos;
+ }
+
+ public Collection<ForwardingObjective> getForwardingObjectives() {
+ return this.forwardMap.values();
+ }
+
+ public static int forwardingObjectiveHash(ForwardingObjective forwardingObjective) {
+ return Objects.hash(forwardingObjective.selector(), forwardingObjective.flag(),
+ forwardingObjective.permanent(), forwardingObjective.timeout(),
+ forwardingObjective.appId(), forwardingObjective.priority(),
+ forwardingObjective.nextId(), forwardingObjective.treatment());
+ }
+}
diff --git a/core/net/src/main/java/org/onosproject/net/flowobjective/impl/composition/ForwardUpdateTable.java b/core/net/src/main/java/org/onosproject/net/flowobjective/impl/composition/ForwardUpdateTable.java
new file mode 100644
index 0000000..9818cfd
--- /dev/null
+++ b/core/net/src/main/java/org/onosproject/net/flowobjective/impl/composition/ForwardUpdateTable.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.net.flowobjective.impl.composition;
+
+import org.onosproject.net.flowobjective.ForwardingObjective;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Provides an update table for Forward.
+ */
+public class ForwardUpdateTable {
+ public List<ForwardingObjective> addObjectives;
+ public List<ForwardingObjective> removeObjectives;
+
+ public ForwardUpdateTable() {
+ this.addObjectives = new ArrayList<>();
+ this.removeObjectives = new ArrayList<>();
+ }
+
+ public void addUpdateTable(ForwardUpdateTable updateTable) {
+ this.addObjectives.addAll(updateTable.addObjectives);
+ this.removeObjectives.addAll(updateTable.removeObjectives);
+ }
+
+ public List<ForwardingObjective> toForwardingObjectiveList() {
+ List<ForwardingObjective> forwardingObjectives = new ArrayList<>();
+ forwardingObjectives.addAll(this.addObjectives);
+ forwardingObjectives.addAll(this.removeObjectives);
+ return forwardingObjectives;
+ }
+}
diff --git a/core/net/src/main/java/org/onosproject/net/flowobjective/impl/composition/NextTable.java b/core/net/src/main/java/org/onosproject/net/flowobjective/impl/composition/NextTable.java
new file mode 100644
index 0000000..e2787ed
--- /dev/null
+++ b/core/net/src/main/java/org/onosproject/net/flowobjective/impl/composition/NextTable.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.net.flowobjective.impl.composition;
+
+import org.onosproject.net.flowobjective.NextObjective;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Provides a table to store Next.
+ */
+public class NextTable {
+
+ protected Map<Integer, NextObjective> nextMap;
+
+ public NextTable() {
+ this.nextMap = new HashMap<>();
+ }
+
+ public List<NextObjective> updateNext(NextObjective nextObjective) {
+ List<NextObjective> updates = new ArrayList<>();
+ switch (nextObjective.op()) {
+ case ADD:
+ this.nextMap.put(nextObjective.id(), nextObjective);
+ updates.add(nextObjective);
+ break;
+ case REMOVE:
+ this.nextMap.remove(nextObjective.id());
+ updates.add(nextObjective);
+ break;
+ default:
+ break;
+ }
+ return updates;
+ }
+
+ public List<NextObjective> updateNext(List<NextObjective> nextObjectives) {
+ List<NextObjective> updates = new ArrayList<>();
+ for (NextObjective nextObjective : nextObjectives) {
+ updates.addAll(this.updateNext(nextObjective));
+ }
+ return updates;
+ }
+
+}
diff --git a/core/net/src/test/java/org/onosproject/net/flowobjective/impl/FlowObjectiveCompositionTreeTest.java b/core/net/src/test/java/org/onosproject/net/flowobjective/impl/FlowObjectiveCompositionTreeTest.java
new file mode 100644
index 0000000..1e898d3
--- /dev/null
+++ b/core/net/src/test/java/org/onosproject/net/flowobjective/impl/FlowObjectiveCompositionTreeTest.java
@@ -0,0 +1,603 @@
+/*
+ * Copyright 2014-2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.net.flowobjective.impl;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Ignore;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Test FlowObjectiveCompositionTree.
+ */
+@Ignore
+public class FlowObjectiveCompositionTreeTest {
+
+ private final Logger log = LoggerFactory.getLogger(getClass());
+
+ @Before
+ public void setUp() {}
+
+ @After
+ public void tearDown() {}
+
+ /*@Test
+ public void testParallelComposition() {
+ FlowObjectiveCompositionTree policyTree = FlowObjectiveCompositionUtil.parsePolicyString("31+32");
+
+ {
+ TrafficSelector selector = DefaultTrafficSelector.builder()
+ .matchIPSrc(IpPrefix.valueOf("1.0.0.0/24"))
+ .build();
+
+ TrafficTreatment treatment = DefaultTrafficTreatment.builder().build();
+
+ ForwardingObjective forwardingObjective = DefaultForwardingObjective.builder()
+ .fromApp(new DefaultApplicationId(31, "a"))
+ .makePermanent()
+ .withFlag(ForwardingObjective.Flag.VERSATILE)
+ .withPriority(1)
+ .withSelector(selector)
+ .withTreatment(treatment)
+ .add();
+
+ helper(policyTree, forwardingObjective);
+ }
+
+ {
+ TrafficSelector selector = DefaultTrafficSelector.builder().build();
+
+ TrafficTreatment treatment = DefaultTrafficTreatment.builder().build();
+
+ ForwardingObjective forwardingObjective = DefaultForwardingObjective.builder()
+ .fromApp(new DefaultApplicationId(31, "a"))
+ .makePermanent()
+ .withFlag(ForwardingObjective.Flag.VERSATILE)
+ .withPriority(0)
+ .withSelector(selector)
+ .withTreatment(treatment)
+ .add();
+
+ helper(policyTree, forwardingObjective);
+ }
+
+
+ {
+ TrafficSelector selector = DefaultTrafficSelector.builder()
+ .matchIPDst(IpPrefix.valueOf("2.0.0.1/32"))
+ .build();
+
+ TrafficTreatment treatment = DefaultTrafficTreatment.builder()
+ .setOutput(PortNumber.portNumber(1))
+ .build();
+
+ ForwardingObjective forwardingObjective = DefaultForwardingObjective.builder()
+ .fromApp(new DefaultApplicationId(32, "b"))
+ .makePermanent()
+ .withFlag(ForwardingObjective.Flag.VERSATILE)
+ .withPriority(1)
+ .withSelector(selector)
+ .withTreatment(treatment)
+ .add();
+
+ helper(policyTree, forwardingObjective);
+ }
+
+ {
+ TrafficSelector selector = DefaultTrafficSelector.builder()
+ .matchIPDst(IpPrefix.valueOf("2.0.0.2/32"))
+ .build();
+
+ TrafficTreatment treatment = DefaultTrafficTreatment.builder()
+ .setOutput(PortNumber.portNumber(2))
+ .build();
+
+ ForwardingObjective forwardingObjective = DefaultForwardingObjective.builder()
+ .fromApp(new DefaultApplicationId(32, "b"))
+ .makePermanent()
+ .withFlag(ForwardingObjective.Flag.VERSATILE)
+ .withPriority(1)
+ .withSelector(selector)
+ .withTreatment(treatment)
+ .add();
+ helper(policyTree, forwardingObjective);
+ }
+
+ {
+ TrafficSelector selector = DefaultTrafficSelector.builder().build();
+
+ TrafficTreatment treatment = DefaultTrafficTreatment.builder().build();
+
+ ForwardingObjective forwardingObjective = DefaultForwardingObjective.builder()
+ .fromApp(new DefaultApplicationId(32, "b"))
+ .makePermanent()
+ .withFlag(ForwardingObjective.Flag.VERSATILE)
+ .withPriority(0)
+ .withSelector(selector)
+ .withTreatment(treatment)
+ .add();
+
+ helper(policyTree, forwardingObjective);
+ }
+
+ System.out.println("---------- Parallel ----------");
+ for (ForwardingObjective fo : policyTree.forwardTable.getForwardingObjectives()) {
+ System.out.println(forwardingObjectiveToString(fo));
+ }
+
+ {
+ TrafficSelector selector = DefaultTrafficSelector.builder()
+ .matchIPDst(IpPrefix.valueOf("2.0.0.3/32"))
+ .build();
+
+ TrafficTreatment treatment = DefaultTrafficTreatment.builder()
+ .setOutput(PortNumber.portNumber(3))
+ .build();
+
+ ForwardingObjective forwardingObjective = DefaultForwardingObjective.builder()
+ .fromApp(new DefaultApplicationId(32, "b"))
+ .makePermanent()
+ .withFlag(ForwardingObjective.Flag.VERSATILE)
+ .withPriority(1)
+ .withSelector(selector)
+ .withTreatment(treatment)
+ .add();
+ helper(policyTree, forwardingObjective);
+ }
+
+ System.out.println("---------- Parallel ----------");
+ for (ForwardingObjective fo : policyTree.forwardTable.getForwardingObjectives()) {
+ System.out.println(forwardingObjectiveToString(fo));
+ }
+
+ {
+ TrafficSelector selector = DefaultTrafficSelector.builder()
+ .matchIPDst(IpPrefix.valueOf("2.0.0.3/32"))
+ .build();
+
+ TrafficTreatment treatment = DefaultTrafficTreatment.builder()
+ .setOutput(PortNumber.portNumber(3))
+ .build();
+
+ ForwardingObjective forwardingObjective = DefaultForwardingObjective.builder()
+ .fromApp(new DefaultApplicationId(32, "b"))
+ .makePermanent()
+ .withFlag(ForwardingObjective.Flag.VERSATILE)
+ .withPriority(1)
+ .withSelector(selector)
+ .withTreatment(treatment)
+ .remove();
+ helper(policyTree, forwardingObjective);
+ }
+
+ {
+ TrafficSelector selector = DefaultTrafficSelector.builder()
+ .matchIPSrc(IpPrefix.valueOf("1.0.0.0/24"))
+ .build();
+
+ TrafficTreatment treatment = DefaultTrafficTreatment.builder().build();
+
+ ForwardingObjective forwardingObjective = DefaultForwardingObjective.builder()
+ .fromApp(new DefaultApplicationId(31, "a"))
+ .makePermanent()
+ .withFlag(ForwardingObjective.Flag.VERSATILE)
+ .withPriority(1)
+ .withSelector(selector)
+ .withTreatment(treatment)
+ .remove();
+
+ helper(policyTree, forwardingObjective);
+ }
+
+ {
+ TrafficSelector selector = DefaultTrafficSelector.builder().build();
+
+ TrafficTreatment treatment = DefaultTrafficTreatment.builder().build();
+
+ ForwardingObjective forwardingObjective = DefaultForwardingObjective.builder()
+ .fromApp(new DefaultApplicationId(31, "a"))
+ .makePermanent()
+ .withFlag(ForwardingObjective.Flag.VERSATILE)
+ .withPriority(0)
+ .withSelector(selector)
+ .withTreatment(treatment)
+ .remove();
+
+ helper(policyTree, forwardingObjective);
+ }
+
+ System.out.println("---------- Parallel ----------");
+ for (ForwardingObjective fo : policyTree.forwardTable.getForwardingObjectives()) {
+ System.out.println(forwardingObjectiveToString(fo));
+ }
+ }
+
+ @Test
+ public void testSequentialComposition() {
+ FlowObjectiveCompositionTree policyTree = FlowObjectiveCompositionUtil.parsePolicyString("31>32");
+
+ {
+ TrafficSelector selector = DefaultTrafficSelector.builder()
+ .matchIPSrc(IpPrefix.valueOf("0.0.0.0/2"))
+ .matchIPDst(IpPrefix.valueOf("3.0.0.0/32"))
+ .build();
+
+ TrafficTreatment treatment = DefaultTrafficTreatment.builder()
+ .setIpDst(IpAddress.valueOf("2.0.0.1"))
+ .build();
+
+ ForwardingObjective forwardingObjective = DefaultForwardingObjective.builder()
+ .fromApp(new DefaultApplicationId(31, "a"))
+ .makePermanent()
+ .withFlag(ForwardingObjective.Flag.VERSATILE)
+ .withPriority(3)
+ .withSelector(selector)
+ .withTreatment(treatment)
+ .add();
+
+ helper(policyTree, forwardingObjective);
+ }
+
+ {
+ TrafficSelector selector = DefaultTrafficSelector.builder()
+ .matchIPDst(IpPrefix.valueOf("3.0.0.0/32"))
+ .build();
+
+ TrafficTreatment treatment = DefaultTrafficTreatment.builder()
+ .setIpDst(IpAddress.valueOf("2.0.0.2"))
+ .build();
+
+ ForwardingObjective forwardingObjective = DefaultForwardingObjective.builder()
+ .fromApp(new DefaultApplicationId(31, "a"))
+ .makePermanent()
+ .withFlag(ForwardingObjective.Flag.VERSATILE)
+ .withPriority(1)
+ .withSelector(selector)
+ .withTreatment(treatment)
+ .add();
+
+ helper(policyTree, forwardingObjective);
+ }
+
+ {
+ TrafficSelector selector = DefaultTrafficSelector.builder().build();
+
+ TrafficTreatment treatment = DefaultTrafficTreatment.builder().build();
+
+ ForwardingObjective forwardingObjective = DefaultForwardingObjective.builder()
+ .fromApp(new DefaultApplicationId(31, "a"))
+ .makePermanent()
+ .withFlag(ForwardingObjective.Flag.VERSATILE)
+ .withPriority(0)
+ .withSelector(selector)
+ .withTreatment(treatment)
+ .add();
+
+ helper(policyTree, forwardingObjective);
+ }
+
+ {
+ TrafficSelector selector = DefaultTrafficSelector.builder()
+ .matchIPDst(IpPrefix.valueOf("2.0.0.1/32"))
+ .build();
+
+ TrafficTreatment treatment = DefaultTrafficTreatment.builder()
+ .setOutput(PortNumber.portNumber(1))
+ .build();
+
+ ForwardingObjective forwardingObjective = DefaultForwardingObjective.builder()
+ .fromApp(new DefaultApplicationId(32, "b"))
+ .makePermanent()
+ .withFlag(ForwardingObjective.Flag.VERSATILE)
+ .withPriority(1)
+ .withSelector(selector)
+ .withTreatment(treatment)
+ .add();
+
+ helper(policyTree, forwardingObjective);
+ }
+
+ {
+ TrafficSelector selector = DefaultTrafficSelector.builder()
+ .matchIPDst(IpPrefix.valueOf("2.0.0.2/32"))
+ .build();
+
+ TrafficTreatment treatment = DefaultTrafficTreatment.builder()
+ .setOutput(PortNumber.portNumber(2))
+ .build();
+
+ ForwardingObjective forwardingObjective = DefaultForwardingObjective.builder()
+ .fromApp(new DefaultApplicationId(32, "b"))
+ .makePermanent()
+ .withFlag(ForwardingObjective.Flag.VERSATILE)
+ .withPriority(1)
+ .withSelector(selector)
+ .withTreatment(treatment)
+ .add();
+ helper(policyTree, forwardingObjective);
+ }
+
+ {
+ TrafficSelector selector = DefaultTrafficSelector.builder().build();
+
+ TrafficTreatment treatment = DefaultTrafficTreatment.builder().build();
+
+ ForwardingObjective forwardingObjective = DefaultForwardingObjective.builder()
+ .fromApp(new DefaultApplicationId(32, "b"))
+ .makePermanent()
+ .withFlag(ForwardingObjective.Flag.VERSATILE)
+ .withPriority(0)
+ .withSelector(selector)
+ .withTreatment(treatment)
+ .add();
+
+ helper(policyTree, forwardingObjective);
+ }
+
+ System.out.println("---------- Sequential ----------");
+ for (ForwardingObjective fo : policyTree.forwardTable.getForwardingObjectives()) {
+ System.out.println(forwardingObjectiveToString(fo));
+ }
+
+ {
+ TrafficSelector selector = DefaultTrafficSelector.builder()
+ .matchIPDst(IpPrefix.valueOf("2.0.0.3/32"))
+ .build();
+
+ TrafficTreatment treatment = DefaultTrafficTreatment.builder()
+ .setOutput(PortNumber.portNumber(3))
+ .build();
+
+ ForwardingObjective forwardingObjective = DefaultForwardingObjective.builder()
+ .fromApp(new DefaultApplicationId(32, "b"))
+ .makePermanent()
+ .withFlag(ForwardingObjective.Flag.VERSATILE)
+ .withPriority(1)
+ .withSelector(selector)
+ .withTreatment(treatment)
+ .add();
+
+ helper(policyTree, forwardingObjective);
+ }
+
+ {
+ TrafficSelector selector = DefaultTrafficSelector.builder()
+ .matchIPSrc(IpPrefix.valueOf("0.0.0.0/1"))
+ .matchIPDst(IpPrefix.valueOf("3.0.0.0/32"))
+ .build();
+
+ TrafficTreatment treatment = DefaultTrafficTreatment.builder()
+ .setIpDst(IpAddress.valueOf("2.0.0.3"))
+ .build();
+
+ ForwardingObjective forwardingObjective = DefaultForwardingObjective.builder()
+ .fromApp(new DefaultApplicationId(31, "a"))
+ .makePermanent()
+ .withFlag(ForwardingObjective.Flag.VERSATILE)
+ .withPriority(3)
+ .withSelector(selector)
+ .withTreatment(treatment)
+ .add();
+
+ helper(policyTree, forwardingObjective);
+ }
+
+ System.out.println("---------- Sequential ----------");
+ for (ForwardingObjective fo : policyTree.forwardTable.getForwardingObjectives()) {
+ System.out.println(forwardingObjectiveToString(fo));
+ }
+
+ {
+ TrafficSelector selector = DefaultTrafficSelector.builder()
+ .matchIPDst(IpPrefix.valueOf("2.0.0.3/32"))
+ .build();
+
+ TrafficTreatment treatment = DefaultTrafficTreatment.builder()
+ .setOutput(PortNumber.portNumber(3))
+ .build();
+
+ ForwardingObjective forwardingObjective = DefaultForwardingObjective.builder()
+ .fromApp(new DefaultApplicationId(32, "b"))
+ .makePermanent()
+ .withFlag(ForwardingObjective.Flag.VERSATILE)
+ .withPriority(1)
+ .withSelector(selector)
+ .withTreatment(treatment)
+ .remove();
+
+ helper(policyTree, forwardingObjective);
+ }
+
+ {
+ TrafficSelector selector = DefaultTrafficSelector.builder()
+ .matchIPSrc(IpPrefix.valueOf("0.0.0.0/1"))
+ .matchIPDst(IpPrefix.valueOf("3.0.0.0/32"))
+ .build();
+
+ TrafficTreatment treatment = DefaultTrafficTreatment.builder()
+ .setIpDst(IpAddress.valueOf("2.0.0.3"))
+ .build();
+
+ ForwardingObjective forwardingObjective = DefaultForwardingObjective.builder()
+ .fromApp(new DefaultApplicationId(31, "a"))
+ .makePermanent()
+ .withFlag(ForwardingObjective.Flag.VERSATILE)
+ .withPriority(3)
+ .withSelector(selector)
+ .withTreatment(treatment)
+ .remove();
+
+ helper(policyTree, forwardingObjective);
+ }
+
+ System.out.println("---------- Sequential ----------");
+ for (ForwardingObjective fo : policyTree.forwardTable.getForwardingObjectives()) {
+ System.out.println(forwardingObjectiveToString(fo));
+ }
+ }
+
+ @Test
+ public void testOverrideComposition() {
+ FlowObjectiveCompositionTree policyTree = FlowObjectiveCompositionUtil.parsePolicyString("31/32");
+
+ {
+ TrafficSelector selector = DefaultTrafficSelector.builder()
+ .matchIPSrc(IpPrefix.valueOf("1.0.0.0/32"))
+ .matchIPDst(IpPrefix.valueOf("2.0.0.1/32"))
+ .build();
+
+ TrafficTreatment treatment = DefaultTrafficTreatment.builder()
+ .setOutput(PortNumber.portNumber(3))
+ .build();
+
+ ForwardingObjective forwardingObjective = DefaultForwardingObjective.builder()
+ .fromApp(new DefaultApplicationId(31, "a"))
+ .makePermanent()
+ .withFlag(ForwardingObjective.Flag.VERSATILE)
+ .withPriority(1)
+ .withSelector(selector)
+ .withTreatment(treatment)
+ .add();
+
+ helper(policyTree, forwardingObjective);
+ }
+
+ {
+ TrafficSelector selector = DefaultTrafficSelector.builder()
+ .matchIPDst(IpPrefix.valueOf("2.0.0.1/32"))
+ .build();
+
+ TrafficTreatment treatment = DefaultTrafficTreatment.builder()
+ .setOutput(PortNumber.portNumber(1))
+ .build();
+
+ ForwardingObjective forwardingObjective = DefaultForwardingObjective.builder()
+ .fromApp(new DefaultApplicationId(32, "b"))
+ .makePermanent()
+ .withFlag(ForwardingObjective.Flag.VERSATILE)
+ .withPriority(1)
+ .withSelector(selector)
+ .withTreatment(treatment)
+ .add();
+
+ helper(policyTree, forwardingObjective);
+ }
+
+ {
+ TrafficSelector selector = DefaultTrafficSelector.builder()
+ .matchIPDst(IpPrefix.valueOf("2.0.0.2/32"))
+ .build();
+
+ TrafficTreatment treatment = DefaultTrafficTreatment.builder()
+ .setOutput(PortNumber.portNumber(2))
+ .build();
+
+ ForwardingObjective forwardingObjective = DefaultForwardingObjective.builder()
+ .fromApp(new DefaultApplicationId(32, "b"))
+ .makePermanent()
+ .withFlag(ForwardingObjective.Flag.VERSATILE)
+ .withPriority(1)
+ .withSelector(selector)
+ .withTreatment(treatment)
+ .add();
+ helper(policyTree, forwardingObjective);
+ }
+
+ {
+ TrafficSelector selector = DefaultTrafficSelector.builder().build();
+
+ TrafficTreatment treatment = DefaultTrafficTreatment.builder().build();
+
+ ForwardingObjective forwardingObjective = DefaultForwardingObjective.builder()
+ .fromApp(new DefaultApplicationId(32, "b"))
+ .makePermanent()
+ .withFlag(ForwardingObjective.Flag.VERSATILE)
+ .withPriority(0)
+ .withSelector(selector)
+ .withTreatment(treatment)
+ .add();
+
+ helper(policyTree, forwardingObjective);
+ }
+
+ System.out.println("---------- Override ----------");
+ for (ForwardingObjective fo : policyTree.forwardTable.getForwardingObjectives()) {
+ System.out.println(forwardingObjectiveToString(fo));
+ }
+
+ {
+ TrafficSelector selector = DefaultTrafficSelector.builder()
+ .matchIPDst(IpPrefix.valueOf("2.0.0.3/32"))
+ .build();
+
+ TrafficTreatment treatment = DefaultTrafficTreatment.builder()
+ .setOutput(PortNumber.portNumber(3))
+ .build();
+
+ ForwardingObjective forwardingObjective = DefaultForwardingObjective.builder()
+ .fromApp(new DefaultApplicationId(32, "b"))
+ .makePermanent()
+ .withFlag(ForwardingObjective.Flag.VERSATILE)
+ .withPriority(1)
+ .withSelector(selector)
+ .withTreatment(treatment)
+ .add();
+ helper(policyTree, forwardingObjective);
+ }
+
+ System.out.println("---------- Override ----------");
+ for (ForwardingObjective fo : policyTree.forwardTable.getForwardingObjectives()) {
+ System.out.println(forwardingObjectiveToString(fo));
+ }
+
+ {
+ TrafficSelector selector = DefaultTrafficSelector.builder()
+ .matchIPDst(IpPrefix.valueOf("2.0.0.3/32"))
+ .build();
+
+ TrafficTreatment treatment = DefaultTrafficTreatment.builder()
+ .setOutput(PortNumber.portNumber(3))
+ .build();
+
+ ForwardingObjective forwardingObjective = DefaultForwardingObjective.builder()
+ .fromApp(new DefaultApplicationId(32, "b"))
+ .makePermanent()
+ .withFlag(ForwardingObjective.Flag.VERSATILE)
+ .withPriority(1)
+ .withSelector(selector)
+ .withTreatment(treatment)
+ .remove();
+ helper(policyTree, forwardingObjective);
+ }
+
+ System.out.println("---------- Override ----------");
+ for (ForwardingObjective fo : policyTree.forwardTable.getForwardingObjectives()) {
+ System.out.println(forwardingObjectiveToString(fo));
+ }
+ }
+
+ private void helper(FlowObjectiveCompositionTree policyTree, ForwardingObjective forwardingObjective) {
+ log.info("before composition");
+ log.info("\t{}", forwardingObjectiveToString(forwardingObjective));
+ List<ForwardingObjective> forwardingObjectives
+ = policyTree.updateForward(forwardingObjective);
+ log.info("after composition");
+ for (ForwardingObjective fo : forwardingObjectives) {
+ log.info("\t{}", forwardingObjectiveToString(fo));
+ }
+ }*/
+}