| /* |
| * 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.vpls; |
| |
| import com.google.common.collect.ImmutableList; |
| import com.google.common.collect.ImmutableSet; |
| import com.google.common.collect.Sets; |
| import org.junit.After; |
| import org.junit.Before; |
| import org.junit.Test; |
| import org.onosproject.cluster.ClusterServiceAdapter; |
| import org.onosproject.cluster.Leader; |
| import org.onosproject.cluster.Leadership; |
| import org.onosproject.cluster.LeadershipEvent; |
| import org.onosproject.net.ConnectPoint; |
| import org.onosproject.net.EncapsulationType; |
| import org.onosproject.net.Host; |
| import org.onosproject.net.host.HostServiceAdapter; |
| import org.onosproject.net.intent.Intent; |
| import org.onosproject.net.intent.IntentData; |
| import org.onosproject.net.intent.IntentEvent; |
| import org.onosproject.net.intent.IntentState; |
| import org.onosproject.net.intent.MockIdGenerator; |
| import org.onosproject.store.service.WallClockTimestamp; |
| import org.onosproject.vpls.api.VplsData; |
| import org.onosproject.vpls.api.VplsOperation; |
| |
| import java.util.ArrayDeque; |
| import java.util.Collection; |
| import java.util.Deque; |
| import java.util.Set; |
| |
| import static org.junit.Assert.*; |
| import static org.onlab.junit.TestTools.assertAfter; |
| import static org.onlab.junit.TestTools.delay; |
| |
| /** |
| * Tests for {@link VplsOperationManager}. |
| */ |
| public class VplsOperationManagerTest extends VplsTest { |
| |
| VplsOperationManager vplsOperationManager; |
| private static final int OPERATION_DELAY = 1000; |
| private static final int OPERATION_DURATION = 1500; |
| |
| @Before |
| public void setup() { |
| MockIdGenerator.cleanBind(); |
| vplsOperationManager = new VplsOperationManager(); |
| vplsOperationManager.coreService = new TestCoreService(); |
| vplsOperationManager.intentService = new TestIntentService(); |
| vplsOperationManager.leadershipService = new TestLeadershipService(); |
| vplsOperationManager.clusterService = new ClusterServiceAdapter(); |
| vplsOperationManager.hostService = new TestHostService(); |
| vplsOperationManager.vplsStore = new TestVplsStore(); |
| vplsOperationManager.isLeader = true; |
| vplsOperationManager.activate(); |
| } |
| |
| @After |
| public void tearDown() { |
| MockIdGenerator.unbind(); |
| vplsOperationManager.deactivate(); |
| } |
| |
| /** |
| * Sends leadership event to the manager and checks if the manager is |
| * leader or not. |
| */ |
| @Test |
| public void testLeadershipEvent() { |
| vplsOperationManager.isLeader = false; |
| vplsOperationManager.localNodeId = NODE_ID_1; |
| |
| // leader changed to self |
| Leader leader = new Leader(NODE_ID_1, 0, 0); |
| Leadership leadership = new Leadership(APP_NAME, leader, ImmutableList.of()); |
| LeadershipEvent event = new LeadershipEvent(LeadershipEvent.Type.LEADER_CHANGED, leadership); |
| ((TestLeadershipService) vplsOperationManager.leadershipService).sendEvent(event); |
| assertTrue(vplsOperationManager.isLeader); |
| |
| // leader changed to other |
| leader = new Leader(NODE_ID_2, 0, 0); |
| leadership = new Leadership(APP_NAME, leader, ImmutableList.of()); |
| event = new LeadershipEvent(LeadershipEvent.Type.LEADER_CHANGED, leadership); |
| ((TestLeadershipService) vplsOperationManager.leadershipService).sendEvent(event); |
| assertFalse(vplsOperationManager.isLeader); |
| } |
| |
| /** |
| * Submits an ADD operation to the operation manager; check if the VPLS |
| * store changed after a period. |
| */ |
| @Test |
| public void testSubmitAddOperation() { |
| VplsData vplsData = VplsData.of(VPLS1); |
| vplsData.addInterfaces(ImmutableSet.of(V100H1, V100H2)); |
| |
| VplsOperation vplsOperation = VplsOperation.of(vplsData, |
| VplsOperation.Operation.ADD); |
| |
| vplsOperationManager.submit(vplsOperation); |
| assertAfter(OPERATION_DELAY, OPERATION_DURATION, () -> { |
| Collection<VplsData> vplss = vplsOperationManager.vplsStore.getAllVpls(); |
| assertEquals(1, vplss.size()); |
| VplsData result = vplss.iterator().next(); |
| |
| assertEquals(vplsData, result); |
| assertEquals(VplsData.VplsState.ADDED, result.state()); |
| |
| Set<Intent> intentsInstalled = |
| Sets.newHashSet(vplsOperationManager.intentService.getIntents()); |
| assertEquals(4, intentsInstalled.size()); |
| }); |
| } |
| |
| /** |
| * Submits an ADD operation to the operation manager; check the VPLS state |
| * from store if Intent install failed. |
| */ |
| @Test |
| public void testSubmitAddOperationFail() { |
| vplsOperationManager.intentService = new AlwaysFailureIntentService(); |
| VplsData vplsData = VplsData.of(VPLS1); |
| vplsData.addInterfaces(ImmutableSet.of(V100H1, V100H2)); |
| |
| VplsOperation vplsOperation = VplsOperation.of(vplsData, |
| VplsOperation.Operation.ADD); |
| vplsOperationManager.submit(vplsOperation); |
| assertAfter(OPERATION_DELAY, OPERATION_DURATION, () -> { |
| Collection<VplsData> vplss = vplsOperationManager.vplsStore.getAllVpls(); |
| assertEquals(1, vplss.size()); |
| VplsData result = vplss.iterator().next(); |
| |
| assertEquals(vplsData, result); |
| assertEquals(VplsData.VplsState.FAILED, result.state()); |
| }); |
| } |
| |
| /** |
| * Submits an REMOVE operation to the operation manager; check if the VPLS |
| * store changed after a period. |
| */ |
| @Test |
| public void testSubmitRemoveOperation() { |
| VplsData vplsData = VplsData.of(VPLS1); |
| vplsData.addInterfaces(ImmutableSet.of(V100H1, V100H2)); |
| vplsData.state(VplsData.VplsState.REMOVING); |
| |
| VplsOperation vplsOperation = VplsOperation.of(vplsData, |
| VplsOperation.Operation.REMOVE); |
| |
| vplsOperationManager.submit(vplsOperation); |
| |
| assertAfter(OPERATION_DELAY, OPERATION_DURATION, () -> { |
| Collection<VplsData> vplss = vplsOperationManager.vplsStore.getAllVpls(); |
| assertEquals(0, vplss.size()); |
| }); |
| } |
| |
| /** |
| * Submits an UPDATE operation with VPLS interface update to the operation manager; check if the VPLS |
| * store changed after a period. |
| */ |
| @Test |
| public void testSubmitUpdateOperation() { |
| VplsData vplsData = VplsData.of(VPLS1); |
| vplsData.addInterfaces(ImmutableSet.of(V100H1)); |
| vplsData.state(VplsData.VplsState.ADDED); |
| vplsOperationManager.vplsStore.addVpls(vplsData); |
| |
| vplsData = VplsData.of(VPLS1, EncapsulationType.VLAN); |
| vplsData.addInterfaces(ImmutableSet.of(V100H1, V100H2)); |
| vplsData.state(VplsData.VplsState.UPDATING); |
| |
| VplsOperation vplsOperation = VplsOperation.of(vplsData, |
| VplsOperation.Operation.UPDATE); |
| |
| vplsOperationManager.submit(vplsOperation); |
| |
| assertAfter(OPERATION_DELAY, OPERATION_DURATION, () -> { |
| Collection<VplsData> vplss = vplsOperationManager.vplsStore.getAllVpls(); |
| VplsData result = vplss.iterator().next(); |
| VplsData expected = VplsData.of(VPLS1, EncapsulationType.VLAN); |
| expected.addInterfaces(ImmutableSet.of(V100H1, V100H2)); |
| expected.state(VplsData.VplsState.ADDED); |
| |
| assertEquals(1, vplss.size()); |
| assertEquals(expected, result); |
| |
| Set<Intent> intentsInstalled = |
| Sets.newHashSet(vplsOperationManager.intentService.getIntents()); |
| assertEquals(4, intentsInstalled.size()); |
| }); |
| } |
| |
| /** |
| * Submits an UPDATE operation with VPLS host update to the operation manager; check if the VPLS |
| * store changed after a period. |
| */ |
| @Test |
| public void testSubmitUpdateHostOperation() { |
| vplsOperationManager.hostService = new EmptyHostService(); |
| VplsData vplsData = VplsData.of(VPLS1); |
| vplsData.addInterfaces(ImmutableSet.of(V100H1, V100H2)); |
| |
| VplsOperation vplsOperation = VplsOperation.of(vplsData, |
| VplsOperation.Operation.ADD); |
| vplsOperationManager.submit(vplsOperation); |
| delay(1000); |
| vplsOperationManager.hostService = new TestHostService(); |
| |
| vplsData = VplsData.of(VPLS1); |
| vplsData.addInterfaces(ImmutableSet.of(V100H1, V100H2)); |
| vplsData.state(VplsData.VplsState.UPDATING); |
| |
| vplsOperation = VplsOperation.of(vplsData, |
| VplsOperation.Operation.UPDATE); |
| |
| vplsOperationManager.submit(vplsOperation); |
| |
| assertAfter(OPERATION_DELAY, OPERATION_DURATION, () -> { |
| Collection<VplsData> vplss = vplsOperationManager.vplsStore.getAllVpls(); |
| VplsData result = vplss.iterator().next(); |
| VplsData expected = VplsData.of(VPLS1); |
| expected.addInterfaces(ImmutableSet.of(V100H1, V100H2)); |
| expected.state(VplsData.VplsState.ADDED); |
| assertEquals(1, vplss.size()); |
| assertEquals(expected, result); |
| |
| assertEquals(4, vplsOperationManager.intentService.getIntentCount()); |
| }); |
| } |
| |
| /** |
| * Submits same operation twice to the manager; the manager should ignore |
| * duplicated operation. |
| */ |
| @Test |
| public void testDuplicateOperationInQueue() { |
| VplsData vplsData = VplsData.of(VPLS1); |
| vplsData.addInterfaces(ImmutableSet.of(V100H1, V100H2)); |
| |
| VplsOperation vplsOperation = VplsOperation.of(vplsData, |
| VplsOperation.Operation.ADD); |
| |
| vplsOperationManager.submit(vplsOperation); |
| vplsOperationManager.submit(vplsOperation); |
| Deque<VplsOperation> opQueue = vplsOperationManager.pendingVplsOperations.get(VPLS1); |
| assertEquals(1, opQueue.size()); |
| |
| // Clear operation queue before scheduler process it |
| opQueue.clear(); |
| } |
| |
| /** |
| * Submits REMOVE operation after submits ADD operation; there should be no |
| * pending or running operation in the manager. |
| */ |
| @Test |
| public void testDoNothingOperation() { |
| VplsData vplsData = VplsData.of(VPLS1); |
| vplsData.addInterfaces(ImmutableSet.of(V100H1, V100H2)); |
| |
| VplsOperation vplsOperation = VplsOperation.of(vplsData, |
| VplsOperation.Operation.ADD); |
| vplsOperationManager.submit(vplsOperation); |
| vplsOperation = VplsOperation.of(vplsData, |
| VplsOperation.Operation.REMOVE); |
| vplsOperationManager.submit(vplsOperation); |
| assertAfter(OPERATION_DELAY, OPERATION_DURATION, () -> { |
| assertEquals(0, vplsOperationManager.pendingVplsOperations.size()); |
| |
| // Should not have any running operation |
| assertEquals(0, vplsOperationManager.runningOperations.size()); |
| }); |
| } |
| |
| /** |
| * Optimize operations which don't need to be optimized. |
| */ |
| @Test |
| public void testOptimizeOperationsNoOptimize() { |
| // empty queue |
| Deque<VplsOperation> operations = new ArrayDeque<>(); |
| VplsOperation vplsOperation = |
| VplsOperationManager.getOptimizedVplsOperation(operations); |
| assertNull(vplsOperation); |
| |
| // one operation |
| VplsData vplsData = VplsData.of(VPLS1); |
| vplsOperation = VplsOperation.of(vplsData, VplsOperation.Operation.ADD); |
| operations.add(vplsOperation); |
| VplsOperation result = |
| VplsOperationManager.getOptimizedVplsOperation(operations); |
| assertEquals(vplsOperation, result); |
| |
| } |
| |
| /** |
| * Optimize operations with first is ADD operation and last is also ADD |
| * operation. |
| */ |
| @Test |
| public void testOptimizeOperationsAToA() { |
| Deque<VplsOperation> operations = new ArrayDeque<>(); |
| VplsData vplsData = VplsData.of(VPLS1); |
| vplsData.addInterfaces(ImmutableSet.of(V100H1)); |
| VplsOperation vplsOperation = VplsOperation.of(vplsData, |
| VplsOperation.Operation.ADD); |
| operations.add(vplsOperation); |
| vplsData = VplsData.of(VPLS1, EncapsulationType.VLAN); |
| vplsData.addInterfaces(ImmutableSet.of(V100H1, V100H2)); |
| vplsOperation = VplsOperation.of(vplsData, |
| VplsOperation.Operation.ADD); |
| operations.add(vplsOperation); |
| vplsOperation = VplsOperationManager.getOptimizedVplsOperation(operations); |
| assertEquals(VplsOperation.of(vplsData, VplsOperation.Operation.ADD), vplsOperation); |
| } |
| |
| /** |
| * Optimize operations with first is ADD operation and last is REMOVE |
| * operation. |
| */ |
| @Test |
| public void testOptimizeOperationsAToR() { |
| Deque<VplsOperation> operations = new ArrayDeque<>(); |
| VplsData vplsData = VplsData.of(VPLS1); |
| vplsData.addInterfaces(ImmutableSet.of(V100H1)); |
| VplsOperation vplsOperation = VplsOperation.of(vplsData, |
| VplsOperation.Operation.ADD); |
| operations.add(vplsOperation); |
| vplsOperation = VplsOperation.of(vplsData, |
| VplsOperation.Operation.REMOVE); |
| operations.add(vplsOperation); |
| vplsOperation = VplsOperationManager.getOptimizedVplsOperation(operations); |
| assertNull(vplsOperation); |
| } |
| |
| /** |
| * Optimize operations with first is ADD operation and last is UPDATE |
| * operation. |
| */ |
| @Test |
| public void testOptimizeOperationsAToU() { |
| Deque<VplsOperation> operations = new ArrayDeque<>(); |
| VplsData vplsData = VplsData.of(VPLS1); |
| vplsData.addInterfaces(ImmutableSet.of(V100H1)); |
| VplsOperation vplsOperation = VplsOperation.of(vplsData, |
| VplsOperation.Operation.ADD); |
| operations.add(vplsOperation); |
| vplsData = VplsData.of(VPLS1, EncapsulationType.VLAN); |
| vplsData.addInterfaces(ImmutableSet.of(V100H1, V100H2)); |
| vplsOperation = VplsOperation.of(vplsData, |
| VplsOperation.Operation.UPDATE); |
| operations.add(vplsOperation); |
| vplsOperation = VplsOperationManager.getOptimizedVplsOperation(operations); |
| assertEquals(VplsOperation.of(vplsData, VplsOperation.Operation.ADD), vplsOperation); |
| } |
| |
| /** |
| * Optimize operations with first is REMOVE operation and last is ADD |
| * operation. |
| */ |
| @Test |
| public void testOptimizeOperationsRToA() { |
| Deque<VplsOperation> operations = new ArrayDeque<>(); |
| VplsData vplsData = VplsData.of(VPLS1); |
| vplsData.addInterfaces(ImmutableSet.of(V100H1)); |
| VplsOperation vplsOperation = VplsOperation.of(vplsData, |
| VplsOperation.Operation.REMOVE); |
| operations.add(vplsOperation); |
| vplsData = VplsData.of(VPLS1, EncapsulationType.VLAN); |
| vplsData.addInterfaces(ImmutableSet.of(V100H1, V100H2)); |
| vplsOperation = VplsOperation.of(vplsData, |
| VplsOperation.Operation.ADD); |
| operations.add(vplsOperation); |
| vplsOperation = VplsOperationManager.getOptimizedVplsOperation(operations); |
| assertEquals(VplsOperation.of(vplsData, VplsOperation.Operation.UPDATE), vplsOperation); |
| } |
| |
| /** |
| * Optimize operations with first is REMOVE operation and last is also |
| * REMOVE operation. |
| */ |
| @Test |
| public void testOptimizeOperationsRToR() { |
| Deque<VplsOperation> operations = new ArrayDeque<>(); |
| VplsData vplsData = VplsData.of(VPLS1); |
| vplsData.addInterfaces(ImmutableSet.of(V100H1)); |
| VplsOperation vplsOperation = VplsOperation.of(vplsData, |
| VplsOperation.Operation.REMOVE); |
| operations.add(vplsOperation); |
| vplsData = VplsData.of(VPLS1, EncapsulationType.VLAN); |
| vplsData.addInterfaces(ImmutableSet.of(V100H1, V100H2)); |
| vplsOperation = VplsOperation.of(vplsData, |
| VplsOperation.Operation.REMOVE); |
| operations.add(vplsOperation); |
| vplsOperation = VplsOperationManager.getOptimizedVplsOperation(operations); |
| vplsData = VplsData.of(VPLS1); |
| vplsData.addInterfaces(ImmutableSet.of(V100H1)); |
| assertEquals(VplsOperation.of(vplsData, VplsOperation.Operation.REMOVE), vplsOperation); |
| } |
| |
| /** |
| * Optimize operations with first is REMOVE operation and last is UPDATE |
| * operation. |
| */ |
| @Test |
| public void testOptimizeOperationsRToU() { |
| Deque<VplsOperation> operations = new ArrayDeque<>(); |
| VplsData vplsData = VplsData.of(VPLS1); |
| vplsData.addInterfaces(ImmutableSet.of(V100H1)); |
| VplsOperation vplsOperation = VplsOperation.of(vplsData, |
| VplsOperation.Operation.REMOVE); |
| operations.add(vplsOperation); |
| vplsData = VplsData.of(VPLS1, EncapsulationType.VLAN); |
| vplsData.addInterfaces(ImmutableSet.of(V100H1, V100H2)); |
| vplsOperation = VplsOperation.of(vplsData, |
| VplsOperation.Operation.UPDATE); |
| operations.add(vplsOperation); |
| vplsOperation = VplsOperationManager.getOptimizedVplsOperation(operations); |
| assertEquals(VplsOperation.of(vplsData, VplsOperation.Operation.UPDATE), vplsOperation); |
| } |
| |
| /** |
| * Optimize operations with first is UPDATE operation and last is ADD |
| * operation. |
| */ |
| @Test |
| public void testOptimizeOperationsUToA() { |
| Deque<VplsOperation> operations = new ArrayDeque<>(); |
| VplsData vplsData = VplsData.of(VPLS1); |
| vplsData.addInterfaces(ImmutableSet.of(V100H1)); |
| VplsOperation vplsOperation = VplsOperation.of(vplsData, |
| VplsOperation.Operation.UPDATE); |
| operations.add(vplsOperation); |
| vplsData = VplsData.of(VPLS1, EncapsulationType.VLAN); |
| vplsData.addInterfaces(ImmutableSet.of(V100H1, V100H2)); |
| vplsOperation = VplsOperation.of(vplsData, |
| VplsOperation.Operation.ADD); |
| operations.add(vplsOperation); |
| vplsOperation = VplsOperationManager.getOptimizedVplsOperation(operations); |
| assertEquals(VplsOperation.of(vplsData, VplsOperation.Operation.UPDATE), vplsOperation); |
| } |
| |
| /** |
| * Optimize operations with first is UPDATE operation and last is REMOVE |
| * operation. |
| */ |
| @Test |
| public void testOptimizeOperationsUToR() { |
| Deque<VplsOperation> operations = new ArrayDeque<>(); |
| VplsData vplsData = VplsData.of(VPLS1); |
| vplsData.addInterfaces(ImmutableSet.of(V100H1)); |
| VplsOperation vplsOperation = VplsOperation.of(vplsData, |
| VplsOperation.Operation.UPDATE); |
| operations.add(vplsOperation); |
| vplsData = VplsData.of(VPLS1, EncapsulationType.VLAN); |
| vplsData.addInterfaces(ImmutableSet.of(V100H1, V100H2)); |
| vplsOperation = VplsOperation.of(vplsData, |
| VplsOperation.Operation.REMOVE); |
| operations.add(vplsOperation); |
| vplsOperation = VplsOperationManager.getOptimizedVplsOperation(operations); |
| assertEquals(VplsOperation.of(vplsData, VplsOperation.Operation.REMOVE), vplsOperation); |
| } |
| |
| /** |
| * Optimize operations with first is UPDATE operation and last is also |
| * UPDATE operation. |
| */ |
| @Test |
| public void testOptimizeOperationsUToU() { |
| Deque<VplsOperation> operations = new ArrayDeque<>(); |
| VplsData vplsData = VplsData.of(VPLS1); |
| vplsData.addInterfaces(ImmutableSet.of(V100H1)); |
| VplsOperation vplsOperation = VplsOperation.of(vplsData, |
| VplsOperation.Operation.UPDATE); |
| operations.add(vplsOperation); |
| vplsData = VplsData.of(VPLS1, EncapsulationType.VLAN); |
| vplsData.addInterfaces(ImmutableSet.of(V100H1, V100H2)); |
| vplsOperation = VplsOperation.of(vplsData, |
| VplsOperation.Operation.UPDATE); |
| operations.add(vplsOperation); |
| vplsOperation = VplsOperationManager.getOptimizedVplsOperation(operations); |
| assertEquals(VplsOperation.of(vplsData, VplsOperation.Operation.UPDATE), vplsOperation); |
| } |
| |
| /** |
| * Test Intent service which always fail when submit or withdraw Intents. |
| */ |
| class AlwaysFailureIntentService extends TestIntentService { |
| @Override |
| public void submit(Intent intent) { |
| intents.add(new IntentData(intent, IntentState.FAILED, new WallClockTimestamp())); |
| if (listener != null) { |
| IntentEvent event = IntentEvent.getEvent(IntentState.FAILED, intent).get(); |
| listener.event(event); |
| } |
| } |
| |
| @Override |
| public void withdraw(Intent intent) { |
| intents.forEach(intentData -> { |
| if (intentData.intent().key().equals(intent.key())) { |
| intentData.setState(IntentState.FAILED); |
| |
| if (listener != null) { |
| IntentEvent event = IntentEvent.getEvent(IntentState.FAILED, intent).get(); |
| listener.event(event); |
| } |
| } |
| }); |
| } |
| } |
| |
| /** |
| * Test host service without any hosts. |
| */ |
| class EmptyHostService extends HostServiceAdapter { |
| @Override |
| public Set<Host> getConnectedHosts(ConnectPoint connectPoint) { |
| return ImmutableSet.of(); |
| } |
| } |
| |
| } |