ONOS-6613 Non-disruptive intent reallocation

Change-Id: I5d051c20402a226ad540b8bc08695b602ff75273
diff --git a/core/net/src/test/java/org/onosproject/net/intent/impl/installer/FlowRuleIntentInstallerTest.java b/core/net/src/test/java/org/onosproject/net/intent/impl/installer/FlowRuleIntentInstallerTest.java
index f979f27..fdef627 100644
--- a/core/net/src/test/java/org/onosproject/net/intent/impl/installer/FlowRuleIntentInstallerTest.java
+++ b/core/net/src/test/java/org/onosproject/net/intent/impl/installer/FlowRuleIntentInstallerTest.java
@@ -18,11 +18,14 @@
 
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.Lists;
+import com.google.common.collect.Maps;
 import com.google.common.collect.Sets;
 import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
 import org.onlab.packet.VlanId;
+import org.onosproject.cfg.ComponentConfigService;
+import org.onosproject.net.ConnectPoint;
 import org.onosproject.net.NetworkResource;
 import org.onosproject.net.flow.DefaultFlowRule;
 import org.onosproject.net.flow.DefaultTrafficSelector;
@@ -40,13 +43,17 @@
 import org.onosproject.net.intent.IntentState;
 import org.onosproject.net.intent.PathIntent;
 import org.onosproject.store.service.WallClockTimestamp;
+import org.onosproject.store.trivial.SimpleIntentStore;
 
 import java.util.Collection;
 import java.util.List;
+import java.util.Map;
 import java.util.Set;
 import java.util.stream.Collectors;
 
-import static org.junit.Assert.*;
+import static org.easymock.EasyMock.mock;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
 
 /**
  * Tests for flow rule Intent installer.
@@ -54,17 +61,21 @@
 public class FlowRuleIntentInstallerTest extends AbstractIntentInstallerTest {
 
     private TestFlowRuleService flowRuleService;
+    private TestFlowRuleServiceNonDisruptive flowRuleServiceNonDisruptive;
     private FlowRuleIntentInstaller installer;
 
     @Before
     public void setup() {
         super.setup();
         flowRuleService = new TestFlowRuleService();
+        flowRuleServiceNonDisruptive = new TestFlowRuleServiceNonDisruptive();
         installer = new FlowRuleIntentInstaller();
         installer.flowRuleService = flowRuleService;
+        installer.store = new SimpleIntentStore();
         installer.intentExtensionService = intentExtensionService;
         installer.intentInstallCoordinator = intentInstallCoordinator;
         installer.trackerService = trackerService;
+        installer.configService = mock(ComponentConfigService.class);
 
         installer.activate();
     }
@@ -328,6 +339,109 @@
     }
 
     /**
+     * Testing the non-disruptive reallocation.
+     */
+    @Test
+    public void testUninstallAndInstallNonDisruptive() throws InterruptedException {
+
+        installer.flowRuleService = flowRuleServiceNonDisruptive;
+
+        List<Intent> intentsToInstall = createAnotherFlowRuleIntentsNonDisruptive();
+        List<Intent> intentsToUninstall = createFlowRuleIntentsNonDisruptive();
+
+        IntentData toInstall = new IntentData(createP2PIntentNonDisruptive(),
+                                              IntentState.INSTALLING,
+                                              new WallClockTimestamp());
+        toInstall = new IntentData(toInstall, intentsToInstall);
+        IntentData toUninstall = new IntentData(createP2PIntentNonDisruptive(),
+                                                IntentState.INSTALLED,
+                                                new WallClockTimestamp());
+        toUninstall = new IntentData(toUninstall, intentsToUninstall);
+
+        IntentOperationContext<FlowRuleIntent> operationContext;
+        IntentInstallationContext context = new IntentInstallationContext(toUninstall, toInstall);
+        operationContext = new IntentOperationContext(intentsToUninstall, intentsToInstall, context);
+
+        installer.apply(operationContext);
+
+        //A single FlowRule is evaluated for every non-disruptive stage
+        TrafficSelector selector = DefaultTrafficSelector.builder()
+                .matchInPhyPort(CP1.port())
+                .build();
+        TrafficTreatment treatment = DefaultTrafficTreatment.builder()
+                .setOutput(CP3.port())
+                .build();
+
+        FlowRule firstStageInstalledRule = DefaultFlowRule.builder()
+                .forDevice(CP1.deviceId())
+                .withSelector(selector)
+                .withTreatment(treatment)
+                .fromApp(APP_ID)
+                .withPriority(DEFAULT_PRIORITY - 1)
+                .makePermanent()
+                .build();
+
+        assertTrue(flowRuleServiceNonDisruptive.flowRulesAdd.contains(firstStageInstalledRule));
+
+        selector = DefaultTrafficSelector.builder()
+                .matchInPhyPort(CP4_2.port())
+                .build();
+        treatment = DefaultTrafficTreatment.builder()
+                .setOutput(CP4_1.port())
+                .build();
+
+        FlowRule secondStageUninstalledRule = DefaultFlowRule.builder()
+                .forDevice(CP4_1.deviceId())
+                .withSelector(selector)
+                .withTreatment(treatment)
+                .fromApp(APP_ID)
+                .withPriority(DEFAULT_PRIORITY)
+                .makePermanent()
+                .build();
+
+        assertTrue(flowRuleServiceNonDisruptive.flowRulesRemove.contains(secondStageUninstalledRule));
+
+        selector = DefaultTrafficSelector.builder()
+                .matchInPhyPort(CP4_3.port())
+                .build();
+        treatment = DefaultTrafficTreatment.builder()
+                .setOutput(CP4_1.port())
+                .build();
+
+        FlowRule thirdStageInstalledRule = DefaultFlowRule.builder()
+                .forDevice(CP4_1.deviceId())
+                .withSelector(selector)
+                .withTreatment(treatment)
+                .fromApp(APP_ID)
+                .withPriority(DEFAULT_PRIORITY)
+                .makePermanent()
+                .build();
+
+        assertTrue(flowRuleServiceNonDisruptive.flowRulesAdd.contains(thirdStageInstalledRule));
+
+        selector = DefaultTrafficSelector.builder()
+                .matchInPhyPort(CP2_1.port())
+                .build();
+        treatment = DefaultTrafficTreatment.builder()
+                .setOutput(CP2_2.port())
+                .build();
+
+        FlowRule lastStageUninstalledRule = DefaultFlowRule.builder()
+                .forDevice(CP2_1.deviceId())
+                .withSelector(selector)
+                .withTreatment(treatment)
+                .fromApp(APP_ID)
+                .withPriority(DEFAULT_PRIORITY)
+                .makePermanent()
+                .build();
+
+        assertTrue(flowRuleServiceNonDisruptive.flowRulesRemove.contains(lastStageUninstalledRule));
+
+        IntentOperationContext successContext = intentInstallCoordinator.successContext;
+        assertEquals(successContext, operationContext);
+    }
+
+    /**
      * Generates FlowRuleIntents for test.
      *
      * @return the FlowRuleIntents for test
@@ -442,6 +556,107 @@
     }
 
     /**
+     * Generates FlowRuleIntents for testing non-disruptive reallocation.
+     *
+     * @return the FlowRuleIntents for test
+     */
+    public List<Intent> createFlowRuleIntentsNonDisruptive() {
+
+        Map<ConnectPoint, ConnectPoint> portsAssociation = Maps.newHashMap();
+        portsAssociation.put(CP1, CP2);
+        portsAssociation.put(CP2_1, CP2_2);
+        portsAssociation.put(CP4_2, CP4_1);
+
+        List<FlowRule> flowRules = Lists.newArrayList();
+
+        for (ConnectPoint srcPoint : portsAssociation.keySet()) {
+
+            TrafficSelector selector = DefaultTrafficSelector.builder()
+                    .matchInPhyPort(srcPoint.port())
+                    .build();
+            TrafficTreatment treatment = DefaultTrafficTreatment.builder()
+                    .setOutput(portsAssociation.get(srcPoint).port())
+                    .build();
+
+            FlowRule flowRule = DefaultFlowRule.builder()
+                    .forDevice(srcPoint.deviceId())
+                    .withSelector(selector)
+                    .withTreatment(treatment)
+                    .fromApp(APP_ID)
+                    .withPriority(DEFAULT_PRIORITY)
+                    .makePermanent()
+                    .build();
+            flowRules.add(flowRule);
+        }
+
+
+
+        List<NetworkResource> resources = ImmutableList.of(S1_S2, S2_S4);
+
+        FlowRuleIntent intent = new FlowRuleIntent(APP_ID,
+                                                   KEY1,
+                                                   flowRules,
+                                                   resources,
+                                                   PathIntent.ProtectionType.PRIMARY,
+                                                   RG1);
+
+        List<Intent> flowRuleIntents = Lists.newArrayList();
+        flowRuleIntents.add(intent);
+
+        return flowRuleIntents;
+    }
+
+    /**
+     * Generates another FlowRuleIntent, going through a different path, for testing non-disruptive reallocation.
+     *
+     * @return the FlowRuleIntents for test
+     */
+    public List<Intent> createAnotherFlowRuleIntentsNonDisruptive() {
+        Map<ConnectPoint, ConnectPoint> portsAssociation = Maps.newHashMap();
+        portsAssociation.put(CP1, CP3);
+        portsAssociation.put(CP3_1, CP3_2);
+        portsAssociation.put(CP4_3, CP4_1);
+
+        List<FlowRule> flowRules = Lists.newArrayList();
+
+        for (ConnectPoint srcPoint : portsAssociation.keySet()) {
+
+            TrafficSelector selector = DefaultTrafficSelector.builder()
+                    .matchInPhyPort(srcPoint.port())
+                    .build();
+            TrafficTreatment treatment = DefaultTrafficTreatment.builder()
+                    .setOutput(portsAssociation.get(srcPoint).port())
+                    .build();
+
+            FlowRule flowRule = DefaultFlowRule.builder()
+                    .forDevice(srcPoint.deviceId())
+                    .withSelector(selector)
+                    .withTreatment(treatment)
+                    .fromApp(APP_ID)
+                    .withPriority(DEFAULT_PRIORITY)
+                    .makePermanent()
+                    .build();
+            flowRules.add(flowRule);
+        }
+
+
+
+        List<NetworkResource> resources = ImmutableList.of(S1_S3, S3_S4);
+
+        FlowRuleIntent intent = new FlowRuleIntent(APP_ID,
+                                                   KEY1,
+                                                   flowRules,
+                                                   resources,
+                                                   PathIntent.ProtectionType.PRIMARY,
+                                                   RG1);
+
+        List<Intent> flowRuleIntents = Lists.newArrayList();
+        flowRuleIntents.add(intent);
+
+        return flowRuleIntents;
+    }
+
+    /**
      * The FlowRuleService for test; always success for any flow rule operations.
      */
     class TestFlowRuleService extends FlowRuleServiceAdapter {
@@ -489,4 +704,37 @@
         }
     }
 
+    /**
+     * The FlowRuleService for testing non-disruptive reallocation.
+     * It keeps all the FlowRules installed/uninstalled.
+     */
+    class TestFlowRuleServiceNonDisruptive extends FlowRuleServiceAdapter {
+
+        Set<FlowRule> flowRulesAdd = Sets.newHashSet();
+        Set<FlowRule> flowRulesRemove = Sets.newHashSet();
+
+        public void record(FlowRuleOperations ops) {
+            ops.stages().forEach(stage -> {
+                stage.forEach(op -> {
+                    switch (op.type()) {
+                        case ADD:
+                            flowRulesAdd.add(op.rule());
+                            break;
+                        case REMOVE:
+                            flowRulesRemove.add(op.rule());
+                            break;
+                        default:
+                            break;
+                    }
+                });
+            });
+        }
+
+        @Override
+        public void apply(FlowRuleOperations ops) {
+            record(ops);
+            ops.callback().onSuccess(ops);
+        }
+    }
+
 }