[ONOS-6163] (vnet) revise intent service for vnets

Revise intent service for virtual networks.

Change-Id: I4955cde2296e58d8f0e356d19483e84f51193318
diff --git a/apps/evpnopenflow/pom.xml b/apps/evpnopenflow/pom.xml
index a1ec1b8..da130fa 100644
--- a/apps/evpnopenflow/pom.xml
+++ b/apps/evpnopenflow/pom.xml
@@ -1,6 +1,6 @@
 <?xml version="1.0"?>
 <!--
-  ~ Copyright 2017-present Open Networking Laboratory
+  ~ Copyright 2017-present Open Networking Foundation
   ~
   ~ Licensed under the Apache License, Version 2.0 (the "License");
   ~ you may not use this file except in compliance with the License.
diff --git a/apps/evpnopenflow/src/main/resources/OSGI-INF/blueprint/shell-config.xml b/apps/evpnopenflow/src/main/resources/OSGI-INF/blueprint/shell-config.xml
index bb55858..fcc09bf 100644
--- a/apps/evpnopenflow/src/main/resources/OSGI-INF/blueprint/shell-config.xml
+++ b/apps/evpnopenflow/src/main/resources/OSGI-INF/blueprint/shell-config.xml
@@ -1,5 +1,5 @@
 <!--
-  ~ Copyright 2017-present Open Networking Laboratory
+  ~ Copyright 2017-present Open Networking Foundation
   ~
   ~ Licensed under the Apache License, Version 2.0 (the "License");
   ~ you may not use this file except in compliance with the License.
diff --git a/incubator/api/src/main/java/org/onosproject/incubator/net/virtual/intent/VirtualIntentCompiler.java b/incubator/api/src/main/java/org/onosproject/incubator/net/virtual/intent/VirtualIntentCompiler.java
new file mode 100644
index 0000000..e76bf6e
--- /dev/null
+++ b/incubator/api/src/main/java/org/onosproject/incubator/net/virtual/intent/VirtualIntentCompiler.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2017-present Open Networking Foundation
+ *
+ * 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.incubator.net.virtual.intent;
+
+import com.google.common.annotations.Beta;
+import org.onosproject.incubator.net.virtual.NetworkId;
+import org.onosproject.net.intent.Intent;
+
+import java.util.List;
+
+/**
+ * Abstraction of a compiler which is capable of taking an intent
+ * and translating it to other, potentially installable, intents for virtual networks.
+ *
+ * @param <T> the type of intent
+ */
+@Beta
+public interface VirtualIntentCompiler<T extends Intent> {
+    /**
+     * Compiles the specified intent into other intents.
+     *
+     * @param networkId   network identifier
+     * @param intent      intent to be compiled
+     * @param installable previous compilation result; optional
+     * @return list of resulting intents
+     * @throws VirtualIntentException if issues are encountered while compiling the intent
+     */
+     List<Intent> compile(NetworkId networkId, T intent, List<Intent> installable);
+}
diff --git a/incubator/api/src/main/java/org/onosproject/incubator/net/virtual/intent/VirtualIntentException.java b/incubator/api/src/main/java/org/onosproject/incubator/net/virtual/intent/VirtualIntentException.java
new file mode 100644
index 0000000..49cb908
--- /dev/null
+++ b/incubator/api/src/main/java/org/onosproject/incubator/net/virtual/intent/VirtualIntentException.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright 2017-present Open Networking Foundation
+ *
+ * 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.incubator.net.virtual.intent;
+
+public class VirtualIntentException extends RuntimeException {
+
+    //FIXME: how to obatin UID?
+    private static final long serialVersionUID = 1907263634145241319L;
+
+    /**
+     * Constructs an exception with no message and no underlying cause.
+     */
+    public VirtualIntentException() {
+    }
+
+    /**
+     * Constructs an exception with the specified message.
+     *
+     * @param message the message describing the specific nature of the error
+     */
+    public VirtualIntentException(String message) {
+        super(message);
+    }
+
+    /**
+     * Constructs an exception with the specified message and the underlying cause.
+     *
+     * @param message the message describing the specific nature of the error
+     * @param cause   the underlying cause of this error
+     */
+    public VirtualIntentException(String message, Throwable cause) {
+        super(message, cause);
+    }
+}
diff --git a/incubator/api/src/main/java/org/onosproject/incubator/net/virtual/intent/package-info.java b/incubator/api/src/main/java/org/onosproject/incubator/net/virtual/intent/package-info.java
new file mode 100644
index 0000000..c5673ac
--- /dev/null
+++ b/incubator/api/src/main/java/org/onosproject/incubator/net/virtual/intent/package-info.java
@@ -0,0 +1,22 @@
+/*
+ * Copyright 2017-present Open Networking Foundation
+ *
+ * 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.
+ */
+
+/**
+ * Set of abstractions for conveying high-level intents for treatment of
+ * selected network traffic by allowing applications to express the
+ * <em>what</em> rather than the <em>how</em> for virtual networks.
+ */
+package org.onosproject.incubator.net.virtual.intent;
\ No newline at end of file
diff --git a/incubator/net/src/main/java/org/onosproject/incubator/net/virtual/impl/VirtualNetworkIntentManager.java b/incubator/net/src/main/java/org/onosproject/incubator/net/virtual/impl/VirtualNetworkIntentManager.java
index a133c66..e40bf4e 100644
--- a/incubator/net/src/main/java/org/onosproject/incubator/net/virtual/impl/VirtualNetworkIntentManager.java
+++ b/incubator/net/src/main/java/org/onosproject/incubator/net/virtual/impl/VirtualNetworkIntentManager.java
@@ -16,7 +16,7 @@
 
 package org.onosproject.incubator.net.virtual.impl;
 
-import com.google.common.collect.Iterators;
+import org.onlab.util.Tools;
 import org.onosproject.incubator.net.virtual.NetworkId;
 import org.onosproject.incubator.net.virtual.VirtualNetworkIntent;
 import org.onosproject.incubator.net.virtual.VirtualNetworkIntentStore;
@@ -25,26 +25,46 @@
 import org.onosproject.incubator.net.virtual.VirtualPort;
 import org.onosproject.incubator.net.virtual.VnetService;
 import org.onosproject.incubator.net.virtual.event.AbstractVirtualListenerManager;
+import org.onosproject.incubator.net.virtual.impl.intent.phase.VirtualFinalIntentProcessPhase;
+import org.onosproject.incubator.net.virtual.impl.intent.VirtualIntentInstallCoordinator;
+import org.onosproject.incubator.net.virtual.impl.intent.VirtualIntentAccumulator;
+import org.onosproject.incubator.net.virtual.impl.intent.VirtualIntentCompilerRegistry;
+import org.onosproject.incubator.net.virtual.impl.intent.VirtualIntentInstallerRegistry;
+import org.onosproject.incubator.net.virtual.impl.intent.phase.VirtualIntentProcessPhase;
+import org.onosproject.incubator.net.virtual.impl.intent.VirtualIntentProcessor;
+import org.onosproject.incubator.net.virtual.impl.intent.VirtualIntentSkipped;
 import org.onosproject.net.ConnectPoint;
 import org.onosproject.net.DeviceId;
 import org.onosproject.net.Port;
 import org.onosproject.net.PortNumber;
+import org.onosproject.net.group.GroupService;
 import org.onosproject.net.intent.Intent;
+import org.onosproject.net.intent.IntentBatchDelegate;
 import org.onosproject.net.intent.IntentData;
 import org.onosproject.net.intent.IntentEvent;
 import org.onosproject.net.intent.IntentListener;
-import org.onosproject.net.intent.WorkPartitionService;
+import org.onosproject.net.intent.IntentStoreDelegate;
 import org.onosproject.net.intent.IntentService;
 import org.onosproject.net.intent.IntentState;
 import org.onosproject.net.intent.Key;
+import org.onosproject.net.resource.ResourceConsumer;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-import java.util.ArrayList;
+import java.util.Collection;
 import java.util.List;
+import java.util.Objects;
 import java.util.Optional;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.ExecutorService;
+import java.util.stream.Collectors;
 
 import static com.google.common.base.Preconditions.*;
+import static org.onlab.util.BoundedThreadPool.newFixedThreadPool;
+import static org.onlab.util.BoundedThreadPool.newSingleThreadExecutor;
+import static org.onlab.util.Tools.groupedThreads;
+import static org.onosproject.incubator.net.virtual.impl.intent.phase.VirtualIntentProcessPhase.newInitialPhase;
+import static org.onosproject.net.intent.IntentState.FAILED;
 
 /**
  * Intent service implementation built on the virtual network service.
@@ -55,6 +75,9 @@
 
     private final Logger log = LoggerFactory.getLogger(getClass());
 
+    private static final int DEFAULT_NUM_THREADS = 12;
+    private int numThreads = DEFAULT_NUM_THREADS;
+
     private static final String NETWORK_ID_NULL = "Network ID cannot be null";
     private static final String DEVICE_NULL = "Device cannot be null";
     private static final String INTENT_NULL = "Intent cannot be null";
@@ -63,10 +86,28 @@
     private static final String INTENT_KEY_NULL = "Intent key cannot be null";
     private static final String CP_NULL = "Connect Point cannot be null";
 
-    protected IntentService intentService;
-    protected VirtualNetworkStore store;
+    //FIXME: Tracker service for vnet.
+
+    //ONOS core services
+    protected VirtualNetworkStore virtualNetworkStore;
     protected VirtualNetworkIntentStore intentStore;
-    protected WorkPartitionService partitionService;
+
+    //Virtual network services
+    protected GroupService groupService;
+
+    private final IntentBatchDelegate batchDelegate = new InternalBatchDelegate();
+    private final InternalIntentProcessor processor = new InternalIntentProcessor();
+    private final IntentStoreDelegate delegate = new InternalStoreDelegate();
+    private final VirtualIntentCompilerRegistry compilerRegistry =
+            VirtualIntentCompilerRegistry.getInstance();
+    private final VirtualIntentInstallerRegistry installerRegistry =
+            VirtualIntentInstallerRegistry.getInstance();
+    private final VirtualIntentAccumulator accumulator =
+            new VirtualIntentAccumulator(batchDelegate);
+
+    private VirtualIntentInstallCoordinator installCoordinator;
+    private ExecutorService batchExecutor;
+    private ExecutorService workerExecutor;
 
     /**
      * Creates a new VirtualNetworkIntentService object.
@@ -79,10 +120,18 @@
 
         super(virtualNetworkManager, networkId, IntentEvent.class);
 
-        this.store = serviceDirectory.get(VirtualNetworkStore.class);
+        this.virtualNetworkStore = serviceDirectory.get(VirtualNetworkStore.class);
         this.intentStore = serviceDirectory.get(VirtualNetworkIntentStore.class);
-        this.intentService = serviceDirectory.get(IntentService.class);
-        this.partitionService = serviceDirectory.get(WorkPartitionService.class);
+
+        this.groupService = manager.get(networkId, GroupService.class);
+
+        intentStore.setDelegate(networkId, delegate);
+        batchExecutor = newSingleThreadExecutor(groupedThreads("onos/intent", "batch", log));
+        workerExecutor = newFixedThreadPool(numThreads, groupedThreads("onos/intent", "worker-%d", log));
+
+        installCoordinator = new VirtualIntentInstallCoordinator(networkId, installerRegistry, intentStore);
+        log.info("Started");
+
     }
 
     @Override
@@ -91,7 +140,8 @@
         checkState(intent instanceof VirtualNetworkIntent, "Only VirtualNetworkIntent is supported.");
         checkArgument(validateIntent((VirtualNetworkIntent) intent), "Invalid Intent");
 
-        intentService.submit(intent);
+        IntentData data = IntentData.submit(intent);
+        intentStore.addPending(networkId, data);
     }
 
     /**
@@ -146,37 +196,19 @@
     @Override
     public void withdraw(Intent intent) {
         checkNotNull(intent, INTENT_NULL);
-        // Withdraws the physical intents created due to the virtual intents.
-        store.getTunnelIds(intent).forEach(tunnelId -> {
-            Key intentKey = Key.of(tunnelId.id(), intent.appId());
-            Intent physicalIntent = intentService.getIntent(intentKey);
-            checkNotNull(physicalIntent, INTENT_NULL);
-
-            // Withdraw the physical intent(s)
-            log.debug("Withdrawing pt-pt intent: " + physicalIntent);
-            intentService.withdraw(physicalIntent);
-        });
-        // Now withdraw the virtual intent
-        log.debug("Withdrawing virtual intent: " + intent);
-        intentService.withdraw(intent);
+        IntentData data = IntentData.withdraw(intent);
+        intentStore.addPending(networkId, data);
     }
 
     @Override
     public void purge(Intent intent) {
         checkNotNull(intent, INTENT_NULL);
-        // Purges the physical intents created for each tunnelId.
-        store.getTunnelIds(intent)
-                .forEach(tunnelId -> {
-                    Key intentKey = Key.of(tunnelId.id(), intent.appId());
-                    Intent physicalIntent = intentService.getIntent(intentKey);
-                    checkNotNull(physicalIntent, INTENT_NULL);
 
-                    // Purge the physical intent(s)
-                    intentService.purge(physicalIntent);
-                    store.removeTunnelId(intent, tunnelId);
-                });
-        // Now purge the virtual intent
-        intentService.purge(intent);
+        IntentData data = IntentData.purge(intent);
+        intentStore.addPending(networkId, data);
+
+        // remove associated group if there is one
+        // FIXME: Remove P2P intent for vnets
     }
 
     @Override
@@ -192,7 +224,9 @@
 
     @Override
     public void addPending(IntentData intentData) {
-        intentService.addPending(intentData);
+        checkNotNull(intentData, INTENT_NULL);
+        //TODO we might consider further checking / assertions
+        intentStore.addPending(networkId, intentData);
     }
 
     @Override
@@ -202,38 +236,177 @@
 
     @Override
     public long getIntentCount() {
-        return Iterators.size(getIntents().iterator());
+        return intentStore.getIntentCount(networkId);
     }
 
     @Override
     public IntentState getIntentState(Key intentKey) {
         checkNotNull(intentKey, KEY_NULL);
-        return Optional.ofNullable(intentStore.getIntentData(networkId, intentKey))
-                .map(IntentData::state)
-                .orElse(null);
+        return intentStore.getIntentState(networkId, intentKey);
     }
 
     @Override
     public List<Intent> getInstallableIntents(Key intentKey) {
-        List<Intent> intents = new ArrayList<>();
-        getIntentData().forEach(intentData -> {
-            if (intentData.intent().key().equals(intentKey)) {
-                intents.addAll(intentData.installables());
-            }
-        });
-        return intents;
+        return intentStore.getInstallableIntents(networkId, intentKey);
     }
 
     @Override
     public boolean isLocal(Key intentKey) {
-        checkNotNull(intentKey, INTENT_KEY_NULL);
-        Intent intent = getIntent(intentKey);
-        checkNotNull(intent, INTENT_NULL);
-        return partitionService.isMine(intentKey, Key::hash);
+        return intentStore.isMaster(networkId, intentKey);
     }
 
     @Override
     public Iterable<Intent> getPending() {
-        return null;
+        return intentStore.getPending(networkId);
+    }
+
+    // Store delegate to re-post events emitted from the store.
+    private class InternalStoreDelegate implements IntentStoreDelegate {
+        @Override
+        public void notify(IntentEvent event) {
+            post(event);
+            switch (event.type()) {
+                case WITHDRAWN:
+                    //FIXME: release resources
+                    break;
+                default:
+                    break;
+            }
+        }
+
+        @Override
+        public void process(IntentData data) {
+            accumulator.add(data);
+        }
+
+        @Override
+        public void onUpdate(IntentData intentData) {
+            //FIXME: track intent
+        }
+
+        private void releaseResources(Intent intent) {
+            // If a resource group is set on the intent, the resource consumer is
+            // set equal to it. Otherwise it's set to the intent key
+            ResourceConsumer resourceConsumer =
+                    intent.resourceGroup() != null ? intent.resourceGroup() : intent.key();
+
+            // By default the resource doesn't get released
+            boolean removeResource = false;
+
+            if (intent.resourceGroup() == null) {
+                // If the intent doesn't have a resource group, it means the
+                // resource was registered using the intent key, so it can be
+                // released
+                removeResource = true;
+            } else {
+                // When a resource group is set, we make sure there are no other
+                // intents using the same resource group, before deleting the
+                // related resources.
+                Long remainingIntents =
+                        Tools.stream(intentStore.getIntents(networkId))
+                                .filter(i -> {
+                                    return i.resourceGroup() != null
+                                            && i.resourceGroup().equals(intent.resourceGroup());
+                                })
+                                .count();
+                if (remainingIntents == 0) {
+                    removeResource = true;
+                }
+            }
+
+            if (removeResource) {
+                // Release resources allocated to withdrawn intent
+                // FIXME: confirm resources are released
+            }
+        }
+    }
+
+    private class InternalBatchDelegate implements IntentBatchDelegate {
+        @Override
+        public void execute(Collection<IntentData> operations) {
+            log.debug("Execute {} operation(s).", operations.size());
+            log.trace("Execute operations: {}", operations);
+
+            // batchExecutor is single-threaded, so only one batch is in flight at a time
+            CompletableFuture.runAsync(() -> {
+                // process intent until the phase reaches one of the final phases
+                List<CompletableFuture<IntentData>> futures = operations.stream()
+                        .map(data -> {
+                            log.debug("Start processing of {} {}@{}", data.request(), data.key(), data.version());
+                            return data;
+                        })
+                        .map(x -> CompletableFuture.completedFuture(x)
+                                .thenApply(VirtualNetworkIntentManager.this::createInitialPhase)
+                                .thenApplyAsync(VirtualIntentProcessPhase::process, workerExecutor)
+                                .thenApply(VirtualFinalIntentProcessPhase::data)
+                                .exceptionally(e -> {
+                                    // When the future fails, we update the Intent to simulate the failure of
+                                    // the installation/withdrawal phase and we save in the current map. In
+                                    // the next round the CleanUp Thread will pick this Intent again.
+                                    log.warn("Future failed", e);
+                                    log.warn("Intent {} - state {} - request {}",
+                                             x.key(), x.state(), x.request());
+                                    switch (x.state()) {
+                                        case INSTALL_REQ:
+                                        case INSTALLING:
+                                        case WITHDRAW_REQ:
+                                        case WITHDRAWING:
+                                            // TODO should we swtich based on current
+                                            IntentData current = intentStore.getIntentData(networkId, x.key());
+                                            return IntentData.nextState(current, FAILED);
+                                        default:
+                                            return null;
+                                    }
+                                }))
+                        .collect(Collectors.toList());
+
+                // write multiple data to store in order
+                intentStore.batchWrite(networkId, Tools.allOf(futures).join().stream()
+                                         .filter(Objects::nonNull)
+                                         .collect(Collectors.toList()));
+            }, batchExecutor).exceptionally(e -> {
+                log.error("Error submitting batches:", e);
+                // FIXME incomplete Intents should be cleaned up
+                //       (transition to FAILED, etc.)
+
+                // the batch has failed
+                // TODO: maybe we should do more?
+                log.error("Walk the plank, matey...");
+                return null;
+            }).thenRun(accumulator::ready);
+
+        }
+    }
+
+    private VirtualIntentProcessPhase createInitialPhase(IntentData data) {
+        IntentData pending = intentStore.getPendingData(networkId, data.key());
+        if (pending == null || pending.version().isNewerThan(data.version())) {
+            /*
+                If the pending map is null, then this intent was compiled by a
+                previous batch iteration, so we can skip it.
+                If the pending map has a newer request, it will get compiled as
+                part of the next batch, so we can skip it.
+             */
+            return VirtualIntentSkipped.getPhase();
+        }
+        IntentData current = intentStore.getIntentData(networkId, data.key());
+        return newInitialPhase(networkId, processor, data, current);
+    }
+
+    private class InternalIntentProcessor implements VirtualIntentProcessor {
+        @Override
+        public List<Intent> compile(NetworkId networkId,
+                                    Intent intent,
+                                    List<Intent> previousInstallables) {
+            return compilerRegistry.compile(networkId, intent, previousInstallables);
+        }
+
+        @Override
+        public void apply(NetworkId networkId,
+                          Optional<IntentData> toUninstall,
+                          Optional<IntentData> toInstall) {
+
+            installCoordinator.installIntents(toUninstall, toInstall);
+        }
     }
 }
diff --git a/incubator/net/src/main/java/org/onosproject/incubator/net/virtual/impl/intent/VirtualIntentAccumulator.java b/incubator/net/src/main/java/org/onosproject/incubator/net/virtual/impl/intent/VirtualIntentAccumulator.java
new file mode 100644
index 0000000..0328383
--- /dev/null
+++ b/incubator/net/src/main/java/org/onosproject/incubator/net/virtual/impl/intent/VirtualIntentAccumulator.java
@@ -0,0 +1,82 @@
+/*
+ * Copyright 2017-present Open Networking Foundation
+ *
+ * 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.incubator.net.virtual.impl.intent;
+
+import com.google.common.collect.Maps;
+import org.onlab.util.AbstractAccumulator;
+import org.onosproject.net.intent.IntentBatchDelegate;
+import org.onosproject.net.intent.IntentData;
+import org.onosproject.net.intent.Key;
+
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+import java.util.Timer;
+
+/**
+ * An accumulator for building batches of intent operations for virtual network.
+ * Only one batch should be in process per instance at a time.
+ */
+public class VirtualIntentAccumulator extends AbstractAccumulator<IntentData> {
+    private static final int DEFAULT_MAX_EVENTS = 1000;
+    private static final int DEFAULT_MAX_IDLE_MS = 10;
+    private static final int DEFAULT_MAX_BATCH_MS = 50;
+
+    // FIXME: Replace with a system-wide timer instance;
+    // TODO: Convert to use HashedWheelTimer or produce a variant of that; then decide which we want to adopt
+    private static final Timer TIMER = new Timer("virtual-intent-op-batching");
+
+    private final IntentBatchDelegate delegate;
+
+    private volatile boolean ready;
+
+    /**
+     * Creates an intent operation accumulator.
+     *
+     * @param delegate the intent batch delegate
+     */
+    public VirtualIntentAccumulator(IntentBatchDelegate delegate) {
+        super(TIMER, DEFAULT_MAX_EVENTS, DEFAULT_MAX_BATCH_MS, DEFAULT_MAX_IDLE_MS);
+        this.delegate = delegate;
+        // Assume that the delegate is ready for work at the start
+        ready = true; //TODO validate the assumption that delegate is ready
+    }
+
+    @Override
+    public void processItems(List<IntentData> items) {
+        ready = false;
+        delegate.execute(reduce(items));
+    }
+
+    private Collection<IntentData> reduce(List<IntentData> ops) {
+        Map<Key, IntentData> map = Maps.newHashMap();
+        for (IntentData op : ops) {
+            map.put(op.key(), op);
+        }
+        //TODO check the version... or maybe store will handle this.
+        return map.values();
+    }
+
+    @Override
+    public boolean isReady() {
+        return ready;
+    }
+
+    public void ready() {
+        ready = true;
+    }
+}
diff --git a/incubator/net/src/main/java/org/onosproject/incubator/net/virtual/impl/intent/VirtualIntentCompilerRegistry.java b/incubator/net/src/main/java/org/onosproject/incubator/net/virtual/impl/intent/VirtualIntentCompilerRegistry.java
new file mode 100644
index 0000000..f922c22
--- /dev/null
+++ b/incubator/net/src/main/java/org/onosproject/incubator/net/virtual/impl/intent/VirtualIntentCompilerRegistry.java
@@ -0,0 +1,170 @@
+/*
+ * Copyright 2017-present Open Networking Foundation
+ *
+ * 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.incubator.net.virtual.impl.intent;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import org.onosproject.incubator.net.virtual.NetworkId;
+import org.onosproject.incubator.net.virtual.intent.VirtualIntentCompiler;
+import org.onosproject.net.intent.Intent;
+import org.onosproject.net.intent.IntentException;
+
+import java.util.ArrayList;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Queue;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+
+public final class VirtualIntentCompilerRegistry {
+    private final ConcurrentMap<Class<? extends Intent>,
+                VirtualIntentCompiler<? extends Intent>> compilers = new ConcurrentHashMap<>();
+
+    // non-instantiable (except for our Singleton)
+    private VirtualIntentCompilerRegistry() {
+
+    }
+
+    public static VirtualIntentCompilerRegistry getInstance() {
+        return SingletonHelper.INSTANCE;
+    }
+
+    /**
+     * Registers the specified compiler for the given intent class.
+     *
+     * @param cls      intent class
+     * @param compiler intent compiler
+     * @param <T>      the type of intent
+     */
+    public <T extends Intent> void registerCompiler(Class<T> cls,
+                                                    VirtualIntentCompiler<T> compiler) {
+        compilers.put(cls, compiler);
+    }
+
+    /**
+     * Unregisters the compiler for the specified intent class.
+     *
+     * @param cls intent class
+     * @param <T> the type of intent
+     */
+    public <T extends Intent> void unregisterCompiler(Class<T> cls) {
+        compilers.remove(cls);
+    }
+
+    /**
+     * Returns immutable set of bindings of currently registered intent compilers.
+     *
+     * @return the set of compiler bindings
+     */
+    public Map<Class<? extends Intent>, VirtualIntentCompiler<? extends Intent>> getCompilers() {
+        return ImmutableMap.copyOf(compilers);
+    }
+
+    /**
+     * Compiles an intent recursively.
+     *
+     * @param networkId network identifier
+     * @param intent intent
+     * @param previousInstallables previous intent installables
+     * @return result of compilation
+     */
+    public List<Intent> compile(NetworkId networkId,
+                         Intent intent, List<Intent> previousInstallables) {
+        if (intent.isInstallable()) {
+            return ImmutableList.of(intent);
+        }
+
+        // FIXME: get previous resources
+        List<Intent> installables = new ArrayList<>();
+        Queue<Intent> compileQueue = new LinkedList<>();
+        compileQueue.add(intent);
+
+        Intent compiling;
+        while ((compiling = compileQueue.poll()) != null) {
+            registerSubclassCompilerIfNeeded(compiling);
+
+            List<Intent> compiled = getCompiler(compiling)
+                    .compile(networkId, compiling, previousInstallables);
+
+            compiled.forEach(i -> {
+                if (i.isInstallable()) {
+                    installables.add(i);
+                } else {
+                    compileQueue.add(i);
+                }
+            });
+        }
+        return installables;
+    }
+
+    /**
+     * Returns the corresponding intent compiler to the specified intent.
+     *
+     * @param intent intent
+     * @param <T>    the type of intent
+     * @return intent compiler corresponding to the specified intent
+     */
+    private <T extends Intent> VirtualIntentCompiler<T> getCompiler(T intent) {
+        @SuppressWarnings("unchecked")
+        VirtualIntentCompiler<T> compiler =
+                (VirtualIntentCompiler<T>) compilers.get(intent.getClass());
+        if (compiler == null) {
+            throw new IntentException("no compiler for class " + intent.getClass());
+        }
+        return compiler;
+    }
+
+    /**
+     * Registers an intent compiler of the specified intent if an intent compiler
+     * for the intent is not registered. This method traverses the class hierarchy of
+     * the intent. Once an intent compiler for a parent type is found, this method
+     * registers the found intent compiler.
+     *
+     * @param intent intent
+     */
+    private void registerSubclassCompilerIfNeeded(Intent intent) {
+        if (!compilers.containsKey(intent.getClass())) {
+            Class<?> cls = intent.getClass();
+            while (cls != Object.class) {
+                // As long as we're within the Intent class descendants
+                if (Intent.class.isAssignableFrom(cls)) {
+                    VirtualIntentCompiler<?> compiler = compilers.get(cls);
+                    if (compiler != null) {
+                        compilers.put(intent.getClass(), compiler);
+                        return;
+                    }
+                }
+                cls = cls.getSuperclass();
+            }
+        }
+    }
+
+    /**
+     * Prevents object instantiation from external.
+     */
+    private static final class SingletonHelper {
+        private static final String ILLEGAL_ACCESS_MSG =
+                "Should not instantiate this class.";
+        private static final VirtualIntentCompilerRegistry INSTANCE =
+                new VirtualIntentCompilerRegistry();
+
+        private SingletonHelper() {
+            throw new IllegalAccessError(ILLEGAL_ACCESS_MSG);
+        }
+    }
+}
diff --git a/incubator/net/src/main/java/org/onosproject/incubator/net/virtual/impl/intent/VirtualIntentInstallCoordinator.java b/incubator/net/src/main/java/org/onosproject/incubator/net/virtual/impl/intent/VirtualIntentInstallCoordinator.java
new file mode 100644
index 0000000..4a09250
--- /dev/null
+++ b/incubator/net/src/main/java/org/onosproject/incubator/net/virtual/impl/intent/VirtualIntentInstallCoordinator.java
@@ -0,0 +1,226 @@
+/*
+ * Copyright 2017-present Open Networking Foundation
+ *
+ * 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.incubator.net.virtual.impl.intent;
+
+import com.google.common.collect.ArrayListMultimap;
+import com.google.common.collect.Maps;
+import com.google.common.collect.Sets;
+import org.onosproject.incubator.net.virtual.NetworkId;
+import org.onosproject.incubator.net.virtual.VirtualNetworkIntentStore;
+import org.onosproject.incubator.net.virtual.impl.VirtualNetworkIntentManager;
+import org.onosproject.net.intent.Intent;
+import org.onosproject.net.intent.IntentData;
+import org.onosproject.net.intent.IntentInstallationContext;
+import org.onosproject.net.intent.IntentInstaller;
+import org.onosproject.net.intent.IntentOperationContext;
+import org.slf4j.Logger;
+
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import java.util.Set;
+
+import static org.onosproject.net.intent.IntentState.*;
+import static org.slf4j.LoggerFactory.getLogger;
+
+/**
+ * Implementation of IntentInstallCoordinator for virtual network.
+ */
+public class VirtualIntentInstallCoordinator {
+    private static final String INSTALLER_NOT_FOUND = "Intent installer not found, Intent: {}";
+    private final Logger log = getLogger(VirtualNetworkIntentManager.class);
+
+    NetworkId networkId;
+    private VirtualIntentInstallerRegistry installerRegistry;
+    private VirtualNetworkIntentStore intentStore;
+
+    /**
+     * Creates an InstallCoordinator.
+     *
+     * @param networkId virtual network identifier
+     * @param installerRegistry the installer registry
+     * @param intentStore the Intent store
+     */
+    public VirtualIntentInstallCoordinator(NetworkId networkId,
+                                           VirtualIntentInstallerRegistry installerRegistry,
+                                           VirtualNetworkIntentStore intentStore) {
+        this.networkId = networkId;
+        this.installerRegistry = installerRegistry;
+        this.intentStore = intentStore;
+    }
+
+    /**
+     * Applies Intent data to be uninstalled and to be installed.
+     *
+     * @param toUninstall Intent data to be uninstalled
+     * @param toInstall Intent data to be installed
+     */
+    public void installIntents(Optional<IntentData> toUninstall,
+                               Optional<IntentData> toInstall) {
+        // If no any Intents to be uninstalled or installed, ignore it.
+        if (!toUninstall.isPresent() && !toInstall.isPresent()) {
+            return;
+        }
+
+        // Classify installable Intents to different installers.
+        ArrayListMultimap<IntentInstaller, Intent> uninstallInstallers;
+        ArrayListMultimap<IntentInstaller, Intent> installInstallers;
+        Set<IntentInstaller> allInstallers = Sets.newHashSet();
+
+        if (toUninstall.isPresent()) {
+            uninstallInstallers = getInstallers(toUninstall.get());
+            allInstallers.addAll(uninstallInstallers.keySet());
+        } else {
+            uninstallInstallers = ArrayListMultimap.create();
+        }
+
+        if (toInstall.isPresent()) {
+            installInstallers = getInstallers(toInstall.get());
+            allInstallers.addAll(installInstallers.keySet());
+        } else {
+            installInstallers = ArrayListMultimap.create();
+        }
+
+        // Generates an installation context for the high level Intent.
+        IntentInstallationContext installationContext =
+                new IntentInstallationContext(toUninstall.orElse(null), toInstall.orElse(null));
+
+        //Generates different operation context for different installable Intents.
+        Map<IntentInstaller, IntentOperationContext> contexts = Maps.newHashMap();
+        allInstallers.forEach(installer -> {
+            List<Intent> intentsToUninstall = uninstallInstallers.get(installer);
+            List<Intent> intentsToInstall = installInstallers.get(installer);
+
+            // Connect context to high level installation context
+            IntentOperationContext context =
+                    new IntentOperationContext(intentsToUninstall, intentsToInstall,
+                                               installationContext);
+            installationContext.addPendingContext(context);
+            contexts.put(installer, context);
+        });
+
+        // Apply contexts to installers
+        contexts.forEach((installer, context) -> {
+            installer.apply(context);
+        });
+    }
+
+    /**
+     * Generates a mapping for installable Intents to installers.
+     *
+     * @param intentData the Intent data which contains installable Intents
+     * @return the mapping for installable Intents to installers
+     */
+    private ArrayListMultimap<IntentInstaller, Intent> getInstallers(IntentData intentData) {
+        ArrayListMultimap<IntentInstaller, Intent> intentInstallers = ArrayListMultimap.create();
+        intentData.installables().forEach(intent -> {
+            IntentInstaller installer = installerRegistry.getInstaller(intent.getClass());
+            if (installer != null) {
+                intentInstallers.put(installer, intent);
+            } else {
+                log.warn(INSTALLER_NOT_FOUND, intent);
+            }
+        });
+        return intentInstallers;
+    }
+
+    /**
+     * Handles success operation context.
+     *
+     * @param context the operation context
+     */
+    public void success(IntentOperationContext context) {
+        IntentInstallationContext intentInstallationContext =
+                context.intentInstallationContext();
+        intentInstallationContext.removePendingContext(context);
+
+        if (intentInstallationContext.isPendingContextsEmpty()) {
+            finish(intentInstallationContext);
+        }
+    }
+
+    /**
+     * Handles failed operation context.
+     *
+     * @param context the operation context
+     */
+    public void failed(IntentOperationContext context) {
+        IntentInstallationContext intentInstallationContext =
+                context.intentInstallationContext();
+        intentInstallationContext.addErrorContext(context);
+        intentInstallationContext.removePendingContext(context);
+
+        if (intentInstallationContext.isPendingContextsEmpty()) {
+            finish(intentInstallationContext);
+        }
+    }
+
+    /**
+     * Completed the installation context and update the Intent store.
+     *
+     * @param intentInstallationContext the installation context
+     */
+    private void finish(IntentInstallationContext intentInstallationContext) {
+        Set<IntentOperationContext> errCtxs = intentInstallationContext.errorContexts();
+        Optional<IntentData> toUninstall = intentInstallationContext.toUninstall();
+        Optional<IntentData> toInstall = intentInstallationContext.toInstall();
+
+        // Intent install success
+        if (errCtxs == null || errCtxs.isEmpty()) {
+            if (toInstall.isPresent()) {
+                IntentData installData = toInstall.get();
+                log.debug("Completed installing: {}", installData.key());
+                installData = new IntentData(installData, installData.installables());
+                installData.setState(INSTALLED);
+                intentStore.write(networkId, installData);
+            } else if (toUninstall.isPresent()) {
+                IntentData uninstallData = toUninstall.get();
+                uninstallData = new IntentData(uninstallData, Collections.emptyList());
+                log.debug("Completed withdrawing: {}", uninstallData.key());
+                switch (uninstallData.request()) {
+                    case INSTALL_REQ:
+                        log.warn("{} was requested to withdraw during installation?",
+                                 uninstallData.intent());
+                        uninstallData.setState(FAILED);
+                        break;
+                    case WITHDRAW_REQ:
+                    default: //TODO "default" case should not happen
+                        uninstallData.setState(WITHDRAWN);
+                        break;
+                }
+                // Intent has been withdrawn; we can clear the installables
+                intentStore.write(networkId, uninstallData);
+            }
+        } else {
+            // if toInstall was cause of error, then recompile (manage/increment counter, when exceeded -> CORRUPT)
+            if (toInstall.isPresent()) {
+                IntentData installData = toInstall.get();
+                installData.setState(CORRUPT);
+                installData.incrementErrorCount();
+                intentStore.write(networkId, installData);
+            }
+            // if toUninstall was cause of error, then CORRUPT (another job will clean this up)
+            if (toUninstall.isPresent()) {
+                IntentData uninstallData = toUninstall.get();
+                uninstallData.setState(CORRUPT);
+                uninstallData.incrementErrorCount();
+                intentStore.write(networkId, uninstallData);
+            }
+        }
+    }
+}
diff --git a/incubator/net/src/main/java/org/onosproject/incubator/net/virtual/impl/intent/VirtualIntentInstallerRegistry.java b/incubator/net/src/main/java/org/onosproject/incubator/net/virtual/impl/intent/VirtualIntentInstallerRegistry.java
new file mode 100644
index 0000000..a0e6448
--- /dev/null
+++ b/incubator/net/src/main/java/org/onosproject/incubator/net/virtual/impl/intent/VirtualIntentInstallerRegistry.java
@@ -0,0 +1,96 @@
+/*
+ * Copyright 2017-present Open Networking Foundation
+ *
+ * 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.incubator.net.virtual.impl.intent;
+
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.Maps;
+import org.onosproject.net.intent.Intent;
+import org.onosproject.net.intent.IntentInstaller;
+
+import java.util.Map;
+
+/**
+ * The local registry for Intent installer for virtual networks.
+ */
+public final class VirtualIntentInstallerRegistry {
+    private final Map<Class<? extends Intent>,
+                IntentInstaller<? extends Intent>> installers;
+
+    // non-instantiable (except for our Singleton)
+    private VirtualIntentInstallerRegistry() {
+        installers = Maps.newConcurrentMap();
+    }
+
+    public static VirtualIntentInstallerRegistry getInstance() {
+        return SingletonHelper.INSTANCE;
+    }
+
+    /**
+     * Registers the specific installer for the given intent class.
+     *
+     * @param cls intent class
+     * @param installer intent installer
+     * @param <T> the type of intent
+     */
+    public <T extends Intent> void registerInstaller(Class<T> cls, IntentInstaller<T> installer) {
+        installers.put(cls, installer);
+    }
+
+    /**
+     * Unregisters the installer for the specific intent class.
+     *
+     * @param cls intent class
+     * @param <T> the type of intent
+     */
+    public <T extends Intent> void unregisterInstaller(Class<T> cls) {
+        installers.remove(cls);
+    }
+
+    /**
+     * Returns immutable set of binding of currently registered intent installers.
+     *
+     * @return the set of installer bindings
+     */
+    public Map<Class<? extends Intent>, IntentInstaller<? extends Intent>> getInstallers() {
+        return ImmutableMap.copyOf(installers);
+    }
+
+    /**
+     * Get an Intent installer by given Intent type.
+     *
+     * @param cls the Intent type
+     * @param <T> the Intent type
+     * @return the Intent installer of the Intent type if exists; null otherwise
+     */
+    public <T extends Intent> IntentInstaller<T> getInstaller(Class<T> cls) {
+        return (IntentInstaller<T>) installers.get(cls);
+    }
+
+    /**
+     * Prevents object instantiation from external.
+     */
+    private static final class SingletonHelper {
+        private static final String ILLEGAL_ACCESS_MSG =
+                "Should not instantiate this class.";
+        private static final VirtualIntentInstallerRegistry INSTANCE =
+                new VirtualIntentInstallerRegistry();
+
+        private SingletonHelper() {
+            throw new IllegalAccessError(ILLEGAL_ACCESS_MSG);
+        }
+    }
+}
diff --git a/incubator/net/src/main/java/org/onosproject/incubator/net/virtual/impl/intent/VirtualIntentProcessor.java b/incubator/net/src/main/java/org/onosproject/incubator/net/virtual/impl/intent/VirtualIntentProcessor.java
new file mode 100644
index 0000000..a7719f9
--- /dev/null
+++ b/incubator/net/src/main/java/org/onosproject/incubator/net/virtual/impl/intent/VirtualIntentProcessor.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright 2017-present Open Networking Foundation
+ *
+ * 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.incubator.net.virtual.impl.intent;
+
+import org.onosproject.incubator.net.virtual.NetworkId;
+import org.onosproject.net.intent.Intent;
+import org.onosproject.net.intent.IntentData;
+
+import java.util.List;
+import java.util.Optional;
+
+/**
+ * A collection of methods to process an intent for virtual networks.
+ *
+ * This interface is public, but intended to be used only by IntentManager and
+ * IntentProcessPhase subclasses stored under phase package.
+ */
+public interface VirtualIntentProcessor {
+    /**
+     * Compiles an intent recursively.
+     *
+     * @param networkId virtual network identifier
+     * @param intent intent
+     * @param previousInstallables previous intent installables
+     * @return result of compilation
+     */
+    List<Intent> compile(NetworkId networkId, Intent intent, List<Intent> previousInstallables);
+
+    /**
+     * Applies intents.
+     *
+     * @param networkId virtual network identifier
+     * @param toUninstall Intent data describing flows to uninstall.
+     * @param toInstall Intent data describing flows to install.
+     */
+    void apply(NetworkId networkId, Optional<IntentData> toUninstall,
+               Optional<IntentData> toInstall);
+}
diff --git a/incubator/net/src/main/java/org/onosproject/incubator/net/virtual/impl/intent/VirtualIntentSkipped.java b/incubator/net/src/main/java/org/onosproject/incubator/net/virtual/impl/intent/VirtualIntentSkipped.java
new file mode 100644
index 0000000..df7f451
--- /dev/null
+++ b/incubator/net/src/main/java/org/onosproject/incubator/net/virtual/impl/intent/VirtualIntentSkipped.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright 2017-present Open Networking Foundation
+ *
+ * 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.incubator.net.virtual.impl.intent;
+
+import org.onosproject.incubator.net.virtual.impl.intent.phase.VirtualFinalIntentProcessPhase;
+import org.onosproject.net.intent.IntentData;
+
+/**
+ * Represents a phase where an intent is not compiled for a virtual network.
+ * This should be used if a new version of the intent will immediately override
+ * this one.
+ */
+public final class VirtualIntentSkipped extends VirtualFinalIntentProcessPhase {
+
+    private static final VirtualIntentSkipped SINGLETON = new VirtualIntentSkipped();
+
+    /**
+     * Returns a shared skipped phase.
+     *
+     * @return skipped phase
+     */
+    public static VirtualIntentSkipped getPhase() {
+        return SINGLETON;
+    }
+
+    // Prevent object construction; use getPhase()
+    private VirtualIntentSkipped() {
+    }
+
+    @Override
+    public IntentData data() {
+        return null;
+    }
+}
diff --git a/incubator/net/src/main/java/org/onosproject/incubator/net/virtual/impl/intent/package-info.java b/incubator/net/src/main/java/org/onosproject/incubator/net/virtual/impl/intent/package-info.java
new file mode 100644
index 0000000..c6758ae
--- /dev/null
+++ b/incubator/net/src/main/java/org/onosproject/incubator/net/virtual/impl/intent/package-info.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright 2017-present Open Networking Foundation
+ *
+ * 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.
+ */
+
+/**
+ * Core subsystem for tracking high-level intents for treatment of selected
+ * network traffic for virtual networks.
+ */
+package org.onosproject.incubator.net.virtual.impl.intent;
\ No newline at end of file
diff --git a/incubator/net/src/main/java/org/onosproject/incubator/net/virtual/impl/intent/phase/VirtualFinalIntentProcessPhase.java b/incubator/net/src/main/java/org/onosproject/incubator/net/virtual/impl/intent/phase/VirtualFinalIntentProcessPhase.java
new file mode 100644
index 0000000..978c95c
--- /dev/null
+++ b/incubator/net/src/main/java/org/onosproject/incubator/net/virtual/impl/intent/phase/VirtualFinalIntentProcessPhase.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright 2017-present Open Networking Foundation
+ *
+ * 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.incubator.net.virtual.impl.intent.phase;
+
+import org.onosproject.net.intent.IntentData;
+
+import java.util.Optional;
+
+/**
+ * Represents a final phase of processing an intent for virtual networks.
+ */
+public abstract class VirtualFinalIntentProcessPhase
+        implements VirtualIntentProcessPhase {
+
+    @Override
+    public final Optional<VirtualIntentProcessPhase> execute() {
+        preExecute();
+        return Optional.empty();
+    }
+
+    /**
+     * Executes operations that must take place before the phase starts.
+     */
+    protected void preExecute() {}
+
+    /**
+     * Returns the IntentData object being acted on by this phase.
+     *
+     * @return intent data object for the phase
+     */
+    public abstract IntentData data();
+}
diff --git a/incubator/net/src/main/java/org/onosproject/incubator/net/virtual/impl/intent/phase/VirtualIntentCompiling.java b/incubator/net/src/main/java/org/onosproject/incubator/net/virtual/impl/intent/phase/VirtualIntentCompiling.java
new file mode 100644
index 0000000..639c04c
--- /dev/null
+++ b/incubator/net/src/main/java/org/onosproject/incubator/net/virtual/impl/intent/phase/VirtualIntentCompiling.java
@@ -0,0 +1,81 @@
+/*
+ * Copyright 2017-present Open Networking Foundation
+ *
+ * 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.incubator.net.virtual.impl.intent.phase;
+
+import org.onosproject.incubator.net.virtual.NetworkId;
+import org.onosproject.incubator.net.virtual.impl.intent.VirtualIntentProcessor;
+import org.onosproject.net.intent.Intent;
+import org.onosproject.net.intent.IntentData;
+import org.onosproject.net.intent.IntentException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.List;
+import java.util.Optional;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+/**
+ * Represents a phase where an intent is being compiled or recompiled
+ * for virtual networks.
+ */
+public class VirtualIntentCompiling implements VirtualIntentProcessPhase {
+    private static final Logger log = LoggerFactory.getLogger(VirtualIntentCompiling.class);
+
+    private final NetworkId networkId;
+    private final VirtualIntentProcessor processor;
+    private final IntentData data;
+    private final Optional<IntentData> stored;
+
+    /**
+     * Creates a intent recompiling phase.
+     *
+     * @param networkId virtual network identifier
+     * @param processor intent processor that does work for recompiling
+     * @param data      intent data containing an intent to be recompiled
+     * @param stored    intent data stored in the store
+     */
+    VirtualIntentCompiling(NetworkId networkId, VirtualIntentProcessor processor,
+                           IntentData data, Optional<IntentData> stored) {
+        this.networkId = checkNotNull(networkId);
+        this.processor = checkNotNull(processor);
+        this.data = checkNotNull(data);
+        this.stored = checkNotNull(stored);
+    }
+
+    @Override
+    public Optional<VirtualIntentProcessPhase> execute() {
+        try {
+            List<Intent> compiled = processor
+                    .compile(networkId, data.intent(),
+                             //TODO consider passing an optional here in the future
+                             stored.map(IntentData::installables).orElse(null));
+            return Optional.of(new VirtualIntentInstalling(networkId, processor,
+                                                           IntentData.compiled(data, compiled), stored));
+        } catch (IntentException e) {
+            log.warn("Unable to compile intent {} due to:", data.intent(), e);
+            if (stored.filter(x -> !x.installables().isEmpty()).isPresent()) {
+                // removing orphaned flows and deallocating resources
+                return Optional.of(
+                        new VirtualIntentWithdrawing(networkId, processor,
+                                                     new IntentData(data, stored.get().installables())));
+            } else {
+                return Optional.of(new VirtualIntentFailed(data));
+            }
+        }
+    }
+}
diff --git a/incubator/net/src/main/java/org/onosproject/incubator/net/virtual/impl/intent/phase/VirtualIntentFailed.java b/incubator/net/src/main/java/org/onosproject/incubator/net/virtual/impl/intent/phase/VirtualIntentFailed.java
new file mode 100644
index 0000000..dff1d13
--- /dev/null
+++ b/incubator/net/src/main/java/org/onosproject/incubator/net/virtual/impl/intent/phase/VirtualIntentFailed.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright 2017-present Open Networking Foundation
+ *
+ * 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.incubator.net.virtual.impl.intent.phase;
+
+import org.onosproject.net.intent.IntentData;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static org.onosproject.net.intent.IntentState.FAILED;
+
+/**
+ * Represents a phase where the compile has failed.
+ */
+public class VirtualIntentFailed extends VirtualFinalIntentProcessPhase {
+
+    private final IntentData data;
+
+    /**
+     * Create an instance with the specified data.
+     *
+     * @param data intentData
+     */
+    VirtualIntentFailed(IntentData data) {
+        this.data = IntentData.nextState(checkNotNull(data), FAILED);
+    }
+
+    @Override
+    public IntentData data() {
+        return data;
+    }
+}
diff --git a/incubator/net/src/main/java/org/onosproject/incubator/net/virtual/impl/intent/phase/VirtualIntentInstallRequest.java b/incubator/net/src/main/java/org/onosproject/incubator/net/virtual/impl/intent/phase/VirtualIntentInstallRequest.java
new file mode 100644
index 0000000..08bfec7
--- /dev/null
+++ b/incubator/net/src/main/java/org/onosproject/incubator/net/virtual/impl/intent/phase/VirtualIntentInstallRequest.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright 2017-present Open Networking Foundation
+ *
+ * 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.incubator.net.virtual.impl.intent.phase;
+
+import org.onosproject.incubator.net.virtual.NetworkId;
+import org.onosproject.incubator.net.virtual.impl.intent.VirtualIntentProcessor;
+import org.onosproject.net.intent.IntentData;
+
+import java.util.Optional;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static org.onosproject.incubator.net.virtual.impl.intent.phase.VirtualIntentProcessPhase.transferErrorCount;
+
+/**
+ * Represents a phase where intent installation has been requested
+ * for a virtual network.
+ */
+final class VirtualIntentInstallRequest implements VirtualIntentProcessPhase {
+
+    private final NetworkId networkId;
+    private final VirtualIntentProcessor processor;
+    private final IntentData data;
+    private final Optional<IntentData> stored;
+
+    /**
+     * Creates an install request phase.
+     *
+     * @param networkId virtual network identifier
+     * @param processor  intent processor to be passed to intent process phases
+     *                   generated after this phase
+     * @param intentData intent data to be processed
+     * @param stored     intent data stored in the store
+     */
+    VirtualIntentInstallRequest(NetworkId networkId, VirtualIntentProcessor processor,
+                                IntentData intentData, Optional<IntentData> stored) {
+        this.networkId = checkNotNull(networkId);
+        this.processor = checkNotNull(processor);
+        this.data = checkNotNull(intentData);
+        this.stored = checkNotNull(stored);
+    }
+
+    @Override
+    public Optional<VirtualIntentProcessPhase> execute() {
+        transferErrorCount(data, stored);
+
+        return Optional.of(new VirtualIntentCompiling(networkId, processor, data, stored));
+    }
+}
diff --git a/incubator/net/src/main/java/org/onosproject/incubator/net/virtual/impl/intent/phase/VirtualIntentInstalling.java b/incubator/net/src/main/java/org/onosproject/incubator/net/virtual/impl/intent/phase/VirtualIntentInstalling.java
new file mode 100644
index 0000000..cd8a185
--- /dev/null
+++ b/incubator/net/src/main/java/org/onosproject/incubator/net/virtual/impl/intent/phase/VirtualIntentInstalling.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright 2017-present Open Networking Foundation
+ *
+ * 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.incubator.net.virtual.impl.intent.phase;
+
+import org.onosproject.incubator.net.virtual.NetworkId;
+import org.onosproject.incubator.net.virtual.impl.intent.VirtualIntentProcessor;
+import org.onosproject.net.intent.IntentData;
+
+import java.util.Optional;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static org.onosproject.net.intent.IntentState.INSTALLING;
+
+/**
+ * Represents a phase where an intent is being installed for a virtual network.
+ */
+//FIXME: better way to implement intent phase and processing for virtual networks?
+public class VirtualIntentInstalling extends VirtualFinalIntentProcessPhase {
+
+    private final NetworkId networkId;
+    private final VirtualIntentProcessor processor;
+    private final IntentData data;
+    private final Optional<IntentData> stored;
+
+    /**
+     * Create an installing phase.
+     *
+     * @param networkId virtual network identifier
+     * @param processor intent processor that does work for installing
+     * @param data      intent data containing an intent to be installed
+     * @param stored    intent data already stored
+     */
+    VirtualIntentInstalling(NetworkId networkId, VirtualIntentProcessor processor,
+                            IntentData data,
+                            Optional<IntentData> stored) {
+        this.networkId = checkNotNull(networkId);
+        this.processor = checkNotNull(processor);
+        this.data = checkNotNull(data);
+        this.stored = checkNotNull(stored);
+        this.data.setState(INSTALLING);
+    }
+
+    @Override
+    public void preExecute() {
+        processor.apply(networkId, stored, Optional.of(data));
+    }
+
+    @Override
+    public IntentData data() {
+        return data;
+    }
+}
diff --git a/incubator/net/src/main/java/org/onosproject/incubator/net/virtual/impl/intent/phase/VirtualIntentProcessPhase.java b/incubator/net/src/main/java/org/onosproject/incubator/net/virtual/impl/intent/phase/VirtualIntentProcessPhase.java
new file mode 100644
index 0000000..99fab54
--- /dev/null
+++ b/incubator/net/src/main/java/org/onosproject/incubator/net/virtual/impl/intent/phase/VirtualIntentProcessPhase.java
@@ -0,0 +1,87 @@
+/*
+ * Copyright 2017-present Open Networking Foundation
+ *
+ * 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.incubator.net.virtual.impl.intent.phase;
+
+import org.onosproject.incubator.net.virtual.NetworkId;
+import org.onosproject.incubator.net.virtual.impl.intent.VirtualIntentProcessor;
+import org.onosproject.net.intent.IntentData;
+
+import java.util.Objects;
+import java.util.Optional;
+
+/**
+ * Represents a phase of processing an intent.
+ */
+public interface VirtualIntentProcessPhase {
+    /**
+     * Execute the procedure represented by the instance
+     * and generates the next update instance.
+     *
+     * @return next update
+     */
+    Optional<VirtualIntentProcessPhase> execute();
+
+    /**
+     * Create a starting intent process phase according to intent data this class holds.
+     *
+     * @param networkId virtual network identifier
+     * @param processor intent processor to be passed to intent process phases
+     *                  generated while this instance is working
+     * @param data intent data to be processed
+     * @param current intent date that is stored in the store
+     * @return starting intent process phase
+     */
+    static VirtualIntentProcessPhase newInitialPhase(NetworkId networkId,
+                                                     VirtualIntentProcessor processor,
+                                                     IntentData data, IntentData current) {
+        switch (data.request()) {
+            case INSTALL_REQ:
+                return new VirtualIntentInstallRequest(networkId, processor, data,
+                                                       Optional.ofNullable(current));
+            case WITHDRAW_REQ:
+                return new VirtualIntentWithdrawRequest(networkId, processor, data,
+                                                        Optional.ofNullable(current));
+            case PURGE_REQ:
+                return new VirtualIntentPurgeRequest(data, Optional.ofNullable(current));
+            default:
+                // illegal state
+                return new VirtualIntentFailed(data);
+        }
+    }
+
+    static VirtualFinalIntentProcessPhase process(VirtualIntentProcessPhase initial) {
+        Optional<VirtualIntentProcessPhase> currentPhase = Optional.of(initial);
+        VirtualIntentProcessPhase previousPhase = initial;
+
+        while (currentPhase.isPresent()) {
+            previousPhase = currentPhase.get();
+            currentPhase = previousPhase.execute();
+        }
+        return (VirtualFinalIntentProcessPhase) previousPhase;
+    }
+
+    static void transferErrorCount(IntentData data, Optional<IntentData> stored) {
+        stored.ifPresent(storedData -> {
+            if (Objects.equals(data.intent(), storedData.intent()) &&
+                    Objects.equals(data.request(), storedData.request())) {
+                data.setErrorCount(storedData.errorCount());
+            } else {
+                data.setErrorCount(0);
+            }
+        });
+    }
+}
diff --git a/incubator/net/src/main/java/org/onosproject/incubator/net/virtual/impl/intent/phase/VirtualIntentPurgeRequest.java b/incubator/net/src/main/java/org/onosproject/incubator/net/virtual/impl/intent/phase/VirtualIntentPurgeRequest.java
new file mode 100644
index 0000000..a6f29ea
--- /dev/null
+++ b/incubator/net/src/main/java/org/onosproject/incubator/net/virtual/impl/intent/phase/VirtualIntentPurgeRequest.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright 2017-present Open Networking Foundation
+ *
+ * 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.incubator.net.virtual.impl.intent.phase;
+
+import org.onosproject.net.intent.IntentData;
+import org.onosproject.net.intent.IntentState;
+import org.slf4j.Logger;
+
+import java.util.Optional;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static org.slf4j.LoggerFactory.getLogger;
+
+/**
+ * Represents a phase of requesting a purge of an intent for a virtual network.
+ * Note: The purge will only succeed if the intent is FAILED or WITHDRAWN.
+ */
+final class VirtualIntentPurgeRequest extends VirtualFinalIntentProcessPhase {
+    private static final Logger log = getLogger(VirtualIntentPurgeRequest.class);
+
+    private final IntentData data;
+    protected final Optional<IntentData> stored;
+
+    VirtualIntentPurgeRequest(IntentData intentData, Optional<IntentData> stored) {
+        this.data = checkNotNull(intentData);
+        this.stored = checkNotNull(stored);
+    }
+
+    private boolean shouldAcceptPurge() {
+        if (!stored.isPresent()) {
+            log.info("Purge for intent {}, but intent is not present",
+                     data.key());
+            return true;
+        }
+
+        IntentData storedData = stored.get();
+        if (storedData.state() == IntentState.WITHDRAWN
+                || storedData.state() == IntentState.FAILED) {
+            return true;
+        }
+        log.info("Purge for intent {} is rejected because intent state is {}",
+                 data.key(), storedData.state());
+        return false;
+    }
+
+    @Override
+    public IntentData data() {
+        if (shouldAcceptPurge()) {
+            return data;
+        } else {
+            return stored.get();
+        }
+    }
+}
diff --git a/incubator/net/src/main/java/org/onosproject/incubator/net/virtual/impl/intent/phase/VirtualIntentWithdrawRequest.java b/incubator/net/src/main/java/org/onosproject/incubator/net/virtual/impl/intent/phase/VirtualIntentWithdrawRequest.java
new file mode 100644
index 0000000..ce8dfc9
--- /dev/null
+++ b/incubator/net/src/main/java/org/onosproject/incubator/net/virtual/impl/intent/phase/VirtualIntentWithdrawRequest.java
@@ -0,0 +1,81 @@
+/*
+ * Copyright 2017-present Open Networking Foundation
+ *
+ * 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.incubator.net.virtual.impl.intent.phase;
+
+import org.onosproject.incubator.net.virtual.NetworkId;
+import org.onosproject.incubator.net.virtual.impl.intent.VirtualIntentProcessor;
+import org.onosproject.net.intent.IntentData;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.Optional;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static org.onosproject.incubator.net.virtual.impl.intent.phase.VirtualIntentProcessPhase.transferErrorCount;
+
+/**
+ * Represents a phase of requesting a withdraw of an intent for a virtual network.
+ */
+final class VirtualIntentWithdrawRequest implements VirtualIntentProcessPhase {
+    private static final Logger log = LoggerFactory.getLogger(VirtualIntentWithdrawRequest.class);
+
+    private final NetworkId networkId;
+    private final VirtualIntentProcessor processor;
+    private final IntentData data;
+    private final Optional<IntentData> stored;
+
+    /**
+     * Creates a withdraw request phase.
+     *
+     * @param networkId virtual network identifier
+     * @param processor  intent processor to be passed to intent process phases
+     *                   generated after this phase
+     * @param intentData intent data to be processed
+     * @param stored     intent data stored in the store
+     */
+    VirtualIntentWithdrawRequest(NetworkId networkId, VirtualIntentProcessor processor,
+                                 IntentData intentData, Optional<IntentData> stored) {
+        this.networkId = checkNotNull(networkId);
+        this.processor = checkNotNull(processor);
+        this.data = checkNotNull(intentData);
+        this.stored = checkNotNull(stored);
+    }
+
+    @Override
+    public Optional<VirtualIntentProcessPhase> execute() {
+        //TODO perhaps we want to validate that the pending and current are the
+        // same version i.e. they are the same
+        // Note: this call is not just the symmetric version of submit
+
+        transferErrorCount(data, stored);
+
+        if (!stored.isPresent() || stored.get().installables().isEmpty()) {
+            switch (data.request()) {
+                case INSTALL_REQ:
+                    // illegal state?
+                    log.warn("{} was requested to withdraw during installation?", data.intent());
+                    return Optional.of(new VirtualIntentFailed(data));
+                case WITHDRAW_REQ:
+                default: //TODO "default" case should not happen
+                    return Optional.of(new VirtualIntentWithdrawn(data));
+            }
+        }
+
+        return Optional.of(new VirtualIntentWithdrawing(networkId, processor,
+                                                        new IntentData(data, stored.get().installables())));
+    }
+}
diff --git a/incubator/net/src/main/java/org/onosproject/incubator/net/virtual/impl/intent/phase/VirtualIntentWithdrawing.java b/incubator/net/src/main/java/org/onosproject/incubator/net/virtual/impl/intent/phase/VirtualIntentWithdrawing.java
new file mode 100644
index 0000000..f7a2867
--- /dev/null
+++ b/incubator/net/src/main/java/org/onosproject/incubator/net/virtual/impl/intent/phase/VirtualIntentWithdrawing.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright 2017-present Open Networking Foundation
+ *
+ * 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.incubator.net.virtual.impl.intent.phase;
+
+import org.onosproject.incubator.net.virtual.NetworkId;
+import org.onosproject.incubator.net.virtual.impl.intent.VirtualIntentProcessor;
+import org.onosproject.net.intent.IntentData;
+
+import java.util.Optional;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static org.onosproject.net.intent.IntentState.WITHDRAWING;
+
+/**
+ * Represents a phase where an intent is withdrawing.
+ */
+final class VirtualIntentWithdrawing extends VirtualFinalIntentProcessPhase {
+
+    private final NetworkId networkId;
+    private final VirtualIntentProcessor processor;
+    private final IntentData data;
+
+    /**
+     * Creates a withdrawing phase.
+     *
+     * @param networkId virtual network identifier
+     * @param processor intent processor that does work for withdrawing
+     * @param data      intent data containing an intent to be withdrawn
+     */
+    VirtualIntentWithdrawing(NetworkId networkId, VirtualIntentProcessor processor,
+                             IntentData data) {
+        this.networkId = checkNotNull(networkId);
+        this.processor = checkNotNull(processor);
+        this.data = checkNotNull(data);
+        this.data.setState(WITHDRAWING);
+    }
+
+    @Override
+    protected void preExecute() {
+        processor.apply(networkId, Optional.of(data), Optional.empty());
+    }
+
+    @Override
+    public IntentData data() {
+        return data;
+    }
+}
diff --git a/incubator/net/src/main/java/org/onosproject/incubator/net/virtual/impl/intent/phase/VirtualIntentWithdrawn.java b/incubator/net/src/main/java/org/onosproject/incubator/net/virtual/impl/intent/phase/VirtualIntentWithdrawn.java
new file mode 100644
index 0000000..7e592c2
--- /dev/null
+++ b/incubator/net/src/main/java/org/onosproject/incubator/net/virtual/impl/intent/phase/VirtualIntentWithdrawn.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright 2017-present Open Networking Foundation
+ *
+ * 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.incubator.net.virtual.impl.intent.phase;
+
+import org.onosproject.net.intent.IntentData;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static org.onosproject.net.intent.IntentState.WITHDRAWN;
+
+/**
+ * Represents a phase where an intent has been withdrawn for a virtual network.
+ */
+final class VirtualIntentWithdrawn extends VirtualFinalIntentProcessPhase {
+
+    private final IntentData data;
+
+    /**
+     * Create a withdrawn phase.
+     *
+     * @param data intent data containing an intent to be withdrawn
+     */
+    VirtualIntentWithdrawn(IntentData data) {
+        this.data = IntentData.nextState(checkNotNull(data), WITHDRAWN);
+    }
+
+    @Override
+    public IntentData data() {
+        return data;
+    }
+}
diff --git a/incubator/net/src/main/java/org/onosproject/incubator/net/virtual/impl/intent/phase/package-info.java b/incubator/net/src/main/java/org/onosproject/incubator/net/virtual/impl/intent/phase/package-info.java
new file mode 100644
index 0000000..5fac168
--- /dev/null
+++ b/incubator/net/src/main/java/org/onosproject/incubator/net/virtual/impl/intent/phase/package-info.java
@@ -0,0 +1,20 @@
+/*
+ * Copyright 2017-present Open Networking Foundation
+ *
+ * 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.
+ */
+
+/**
+ * Implementations of various intent processing phases for virtual networks.
+ */
+package org.onosproject.incubator.net.virtual.impl.intent.phase;
\ No newline at end of file
diff --git a/incubator/net/src/test/java/org/onosproject/incubator/net/virtual/impl/VirtualNetworkIntentManagerTest.java b/incubator/net/src/test/java/org/onosproject/incubator/net/virtual/impl/VirtualNetworkIntentManagerTest.java
index 0695d4e..60521b4 100644
--- a/incubator/net/src/test/java/org/onosproject/incubator/net/virtual/impl/VirtualNetworkIntentManagerTest.java
+++ b/incubator/net/src/test/java/org/onosproject/incubator/net/virtual/impl/VirtualNetworkIntentManagerTest.java
@@ -217,9 +217,7 @@
         virtualNetworkManagerStore.updateLink(link4, link4.tunnelId(), Link.State.ACTIVE);
 
         vnetIntentService = new VirtualNetworkIntentManager(manager, virtualNetwork.id());
-        vnetIntentService.intentService = intentService;
         vnetIntentService.intentStore = intentStore;
-        vnetIntentService.partitionService = workPartitionService;
         return virtualNetwork;
     }
 
diff --git a/incubator/net/src/test/java/org/onosproject/incubator/net/virtual/impl/VirtualNetworkManagerTest.java b/incubator/net/src/test/java/org/onosproject/incubator/net/virtual/impl/VirtualNetworkManagerTest.java
index d47caed..7f5a5fe 100644
--- a/incubator/net/src/test/java/org/onosproject/incubator/net/virtual/impl/VirtualNetworkManagerTest.java
+++ b/incubator/net/src/test/java/org/onosproject/incubator/net/virtual/impl/VirtualNetworkManagerTest.java
@@ -44,6 +44,7 @@
 import org.onosproject.incubator.net.virtual.VirtualNetworkFlowRuleStore;
 import org.onosproject.incubator.net.virtual.VirtualNetworkGroupStore;
 import org.onosproject.incubator.net.virtual.VirtualNetworkIntent;
+import org.onosproject.incubator.net.virtual.VirtualNetworkIntentStore;
 import org.onosproject.incubator.net.virtual.VirtualNetworkListener;
 import org.onosproject.incubator.net.virtual.VirtualNetworkPacketStore;
 import org.onosproject.incubator.net.virtual.VirtualPort;
@@ -58,6 +59,7 @@
 import org.onosproject.incubator.store.virtual.impl.SimpleVirtualFlowObjectiveStore;
 import org.onosproject.incubator.store.virtual.impl.SimpleVirtualFlowRuleStore;
 import org.onosproject.incubator.store.virtual.impl.SimpleVirtualGroupStore;
+import org.onosproject.incubator.store.virtual.impl.SimpleVirtualIntentStore;
 import org.onosproject.incubator.store.virtual.impl.SimpleVirtualPacketStore;
 import org.onosproject.net.ConnectPoint;
 import org.onosproject.net.DeviceId;
@@ -854,11 +856,10 @@
         validateServiceGetReturnsSavedInstance(virtualNetwork.id(), DeviceService.class);
         validateServiceGetReturnsSavedInstance(virtualNetwork.id(), LinkService.class);
         validateServiceGetReturnsSavedInstance(virtualNetwork.id(), TopologyService.class);
-        validateServiceGetReturnsSavedInstance(virtualNetwork.id(), IntentService.class);
         validateServiceGetReturnsSavedInstance(virtualNetwork.id(), HostService.class);
         validateServiceGetReturnsSavedInstance(virtualNetwork.id(), PathService.class);
 
-        // extra setup needed for FlowRuleService, PacketService, GroupService
+        // extra setup needed for FlowRuleService, PacketService, GroupService, and IntentService
         VirtualProviderManager virtualProviderManager = new VirtualProviderManager();
         virtualProviderManager.registerProvider(new DefaultVirtualFlowRuleProvider());
         virtualProviderManager.registerProvider(new DefaultVirtualPacketProvider());
@@ -870,12 +871,14 @@
                 .add(VirtualNetworkFlowRuleStore.class, new SimpleVirtualFlowRuleStore())
                 .add(VirtualNetworkPacketStore.class, new SimpleVirtualPacketStore())
                 .add(VirtualNetworkGroupStore.class, new SimpleVirtualGroupStore())
+                .add(VirtualNetworkIntentStore.class, new SimpleVirtualIntentStore())
                 .add(VirtualNetworkFlowObjectiveStore.class, new SimpleVirtualFlowObjectiveStore());
 
         validateServiceGetReturnsSavedInstance(virtualNetwork.id(), FlowRuleService.class);
         validateServiceGetReturnsSavedInstance(virtualNetwork.id(), FlowObjectiveService.class);
         validateServiceGetReturnsSavedInstance(virtualNetwork.id(), PacketService.class);
         validateServiceGetReturnsSavedInstance(virtualNetwork.id(), GroupService.class);
+        validateServiceGetReturnsSavedInstance(virtualNetwork.id(), IntentService.class);
     }
 
     /**