From: Shigeru Yasuda Date: Wed, 6 Jan 2016 12:14:18 +0000 (+0900) Subject: Increase integration test coverage. X-Git-Tag: release/beryllium~37 X-Git-Url: https://git.opendaylight.org/gerrit/gitweb?a=commitdiff_plain;h=refs%2Fchanges%2F84%2F32184%2F1;p=vtn.git Increase integration test coverage. * Path map Change-Id: Iae5ab95ff9d33ec147c9ef4dc6f79b88a08bbeb6 Signed-off-by: Shigeru Yasuda --- diff --git a/manager/it/core/src/test/java/org/opendaylight/vtn/manager/it/core/PathMapServiceTest.java b/manager/it/core/src/test/java/org/opendaylight/vtn/manager/it/core/PathMapServiceTest.java new file mode 100644 index 00000000..14c2e0b3 --- /dev/null +++ b/manager/it/core/src/test/java/org/opendaylight/vtn/manager/it/core/PathMapServiceTest.java @@ -0,0 +1,467 @@ +/* + * Copyright (c) 2015 NEC Corporation. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ + +package org.opendaylight.vtn.manager.it.core; + +import static org.opendaylight.vtn.manager.it.core.VTNManagerIT.LOG; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map.Entry; +import java.util.Map; +import java.util.Random; +import java.util.Set; + +import org.opendaylight.vtn.manager.it.util.RpcErrorTag; +import org.opendaylight.vtn.manager.it.util.VirtualNetwork; +import org.opendaylight.vtn.manager.it.util.pathmap.PathMap; +import org.opendaylight.vtn.manager.it.util.pathmap.PathMapSet; +import org.opendaylight.vtn.manager.it.util.vnode.VTenantConfig; + +import org.opendaylight.yang.gen.v1.urn.opendaylight.vtn.pathmap.rev150328.ClearPathMapInput; +import org.opendaylight.yang.gen.v1.urn.opendaylight.vtn.pathmap.rev150328.ClearPathMapInputBuilder; +import org.opendaylight.yang.gen.v1.urn.opendaylight.vtn.pathmap.rev150328.RemovePathMapInput; +import org.opendaylight.yang.gen.v1.urn.opendaylight.vtn.pathmap.rev150328.RemovePathMapInputBuilder; +import org.opendaylight.yang.gen.v1.urn.opendaylight.vtn.pathmap.rev150328.SetPathMapInput; +import org.opendaylight.yang.gen.v1.urn.opendaylight.vtn.pathmap.rev150328.SetPathMapInputBuilder; +import org.opendaylight.yang.gen.v1.urn.opendaylight.vtn.pathmap.rev150328.VtnPathMapService; +import org.opendaylight.yang.gen.v1.urn.opendaylight.vtn.pathmap.rev150328.set.path.map.input.PathMapList; +import org.opendaylight.yang.gen.v1.urn.opendaylight.vtn.types.rev150209.VtnErrorTag; +import org.opendaylight.yang.gen.v1.urn.opendaylight.vtn.types.rev150209.VtnUpdateType; + +/** + * Test case for {@link VtnPathMapService}. + */ +public final class PathMapServiceTest extends TestMethodBase { + /** + * The number used to generate random path policy ID. + */ + private static final int PATH_POLICY_BOUNDARY = PATH_POLICY_ID_MAX + 2; + + /** + * Construct a new instance. + * + * @param vit A {@link VTNManagerIT} instance. + */ + public PathMapServiceTest(VTNManagerIT vit) { + super(vit); + } + + /** + * Generate a random path policy ID. + * + * @param rand A pseudo random generator. + * @return An integer that specifies the path policy or {@code null}. + */ + private Integer randomPolicy(Random rand) { + int num = rand.nextInt(PATH_POLICY_BOUNDARY); + return (num <= PATH_POLICY_ID_MAX) ? Integer.valueOf(num) : null; + } + + // TestMethodBase + + /** + * Run the test. + * + * @throws Exception An error occurred. + */ + @Override + protected void runTest() throws Exception { + Random rand = new Random(111222333444555L); + VTNManagerIT vit = getTest(); + VtnPathMapService pmSrv = vit.getPathMapService(); + + // Test for global path map. + testPathMapService(pmSrv, rand, null); + + // Test for VTN path map. + String[] tenants = { + "vtn_1", "vtn_2", "vtn_3", + }; + for (String tname: tenants) { + testPathMapService(pmSrv, rand, tname); + } + + // Error tests. + + // Null input. + checkRpcError(pmSrv.setPathMap(null), + RpcErrorTag.MISSING_ELEMENT, VtnErrorTag.BADREQUEST); + checkRpcError(pmSrv.removePathMap(null), + RpcErrorTag.MISSING_ELEMENT, VtnErrorTag.BADREQUEST); + checkRpcError(pmSrv.clearPathMap(null), + RpcErrorTag.MISSING_ELEMENT, VtnErrorTag.BADREQUEST); + + // Invalid VTN name. + for (String tname: INVALID_VNODE_NAMES) { + notFoundTest(pmSrv, rand, tname); + } + + // Errors should never affect path policies. + VirtualNetwork vnet = getVirtualNetwork(); + vnet.verify(); + + // Remove all the VTN path maps using remove-path-map. + Map vtnMaps = new HashMap<>(); + for (String tname: tenants) { + PathMapSet pmSet = vnet.getTenant(tname).getPathMaps(); + assertEquals(null, vtnMaps.put(tname, pmSet.clone())); + pmSet.removeAll(pmSrv, tname); + vnet.verify(); + } + + // Remove all the global path maps using remove-path-map. + PathMapSet gpmSet = vnet.getPathMaps(); + PathMapSet gpmSaved = gpmSet.clone(); + gpmSet.removeAll(pmSrv, null); + vnet.verify(); + + // Restore all the global and VTN path maps. + gpmSet.add(gpmSaved).restore(pmSrv, null); + vnet.verify(); + + for (Entry entry: vtnMaps.entrySet()) { + String tname = entry.getKey(); + PathMapSet saved = entry.getValue(); + PathMapSet pmSet = vnet.getTenant(tname).getPathMaps(); + pmSet.add(saved).restore(pmSrv, tname); + vnet.verify(); + } + + // Remove all the global path maps using clear-path-map. + assertEquals(VtnUpdateType.REMOVED, clearPathMap(null)); + gpmSet.clear(); + vnet.verify(); + assertEquals(null, clearPathMap(null)); + + // Remove all the VTN path maps using clear-path-map. + for (String tname: tenants) { + PathMapSet pmSet = vnet.getTenant(tname).getPathMaps(); + assertEquals(VtnUpdateType.REMOVED, clearPathMap(tname)); + pmSet.clear(); + vnet.verify(); + assertEquals(null, clearPathMap(tname)); + } + + // Restore VTN path maps again. + for (Entry entry: vtnMaps.entrySet()) { + String tname = entry.getKey(); + PathMapSet saved = entry.getValue(); + PathMapSet pmSet = vnet.getTenant(tname).getPathMaps(); + pmSet.add(saved).restore(pmSrv, tname); + vnet.verify(); + } + + // Remove VTNs. + for (String tname: tenants) { + removeVtn(tname); + vnet.removeTenant(tname).verify(); + } + } + + /** + * Test case for {@link VtnPathMapService}. + * + * @param pmSrv vtn-path-map service. + * @param rand A pseudo random generator. + * @param tname The name of the target VTN. + * {@code null} indicates the global path map. + * @throws Exception An error occurred. + */ + private void testPathMapService(VtnPathMapService pmSrv, Random rand, + String tname) throws Exception { + LOG.debug("testPathMapService: VTN={}", + (tname == null) ? "" : tname); + + VirtualNetwork vnet = getVirtualNetwork(); + PathMapSet pmSet; + if (tname == null) { + // The target is the global path map. + pmSet = vnet.getPathMaps(); + } else { + // Ensure that path map RPCs return NOTFOUND error if the specified + // VTN is not present. + notFoundTest(pmSrv, rand, tname); + + // Errors should never affect path policies. + vnet.verify(); + + // Create the target VTN. + VTenantConfig tconf = new VTenantConfig(); + vnet.addTenant(tname, tconf).apply(); + pmSet = tconf.getPathMaps(); + } + + // Configure path maps. + Set idxSet = new HashSet<>(); + PathMap pmap1 = new PathMap(VTN_INDEX_MIN, "cond_1"); + assertEquals(true, idxSet.add(pmap1.getIndex())); + assertEquals(VtnUpdateType.CREATED, pmSet.addMap(pmSrv, tname, pmap1)); + vnet.verify(); + + PathMap pmap2 = new PathMap(VTN_INDEX_MAX, "cond_2", + PATH_POLICY_ID_MAX); + assertEquals(true, idxSet.add(pmap2.getIndex())); + Map pmResult = new HashMap<>(); + assertEquals(null, pmResult.put(pmap1.getIndex(), null)); + assertEquals(null, + pmResult.put(pmap2.getIndex(), VtnUpdateType.CREATED)); + pmSet.add(pmap2); + assertEquals(pmResult, pmSet.update(pmSrv, tname)); + vnet.verify(); + + PathMap pmap3 = new PathMap( + createVtnIndex(rand, idxSet), "cond_3", 0, 0, 0); + PathMap pmap4 = new PathMap( + createVtnIndex(rand, idxSet), "cond_4", randomPolicy(rand), + 65534, 65535); + PathMap pmap5 = new PathMap( + createVtnIndex(rand, idxSet), "cond_5", randomPolicy(rand), + 100, 101); + PathMap pmap6 = new PathMap( + createVtnIndex(rand, idxSet), "cond_6", randomPolicy(rand), + 3333, 4444); + pmResult.clear(); + assertEquals(null, + pmResult.put(pmap3.getIndex(), VtnUpdateType.CREATED)); + assertEquals(null, + pmResult.put(pmap4.getIndex(), VtnUpdateType.CREATED)); + assertEquals(null, + pmResult.put(pmap5.getIndex(), VtnUpdateType.CREATED)); + assertEquals(null, + pmResult.put(pmap6.getIndex(), VtnUpdateType.CREATED)); + assertEquals(pmResult, + pmSet.addMaps(pmSrv, tname, pmap3, pmap4, pmap5, pmap6)); + vnet.verify(); + + // Update pmap1. + pmap1.setPolicy(PATH_POLICY_ID_MIN); + assertEquals(VtnUpdateType.CHANGED, pmSet.addMap(pmSrv, tname, pmap1)); + vnet.verify(); + + // Update pmap2 and pmap6. + pmResult.clear(); + pmap2.setPolicy(null). + setCondition("cond_22"). + setIdleTimeout(12345). + setHardTimeout(23456); + assertEquals(null, + pmResult.put(pmap2.getIndex(), VtnUpdateType.CHANGED)); + + pmap6.setIdleTimeout(1000). + setHardTimeout(0); + assertEquals(null, + pmResult.put(pmap6.getIndex(), VtnUpdateType.CHANGED)); + + assertEquals(null, pmResult.put(pmap1.getIndex(), null)); + assertEquals(null, pmResult.put(pmap3.getIndex(), null)); + assertEquals(pmResult, + pmSet.addMaps(pmSrv, tname, pmap1, pmap2, pmap3, pmap6)); + vnet.verify(); + + // Update pmap3, and create pmap7. + pmResult.clear(); + pmap3.setPolicy(PATH_POLICY_ID_MIN + 1). + setIdleTimeout(null). + setHardTimeout(null); + assertEquals(null, + pmResult.put(pmap3.getIndex(), VtnUpdateType.CHANGED)); + + PathMap pmap7 = new PathMap( + createVtnIndex(rand, idxSet), "cond_7", randomPolicy(rand), + 9999, 10000); + assertEquals(null, + pmResult.put(pmap7.getIndex(), VtnUpdateType.CREATED)); + + assertEquals(null, pmResult.put(pmap5.getIndex(), null)); + assertEquals(pmResult, + pmSet.addMaps(pmSrv, tname, pmap3, pmap5, pmap7)); + vnet.verify(); + + // Remove pmap4. + assertEquals(VtnUpdateType.REMOVED, + pmSet.removeMap(pmSrv, tname, pmap4)); + vnet.verify(); + + // Remove pmap2 and pmap5. + List indices = new ArrayList<>(); + pmResult.clear(); + assertEquals(null, + pmResult.put(pmap2.getIndex(), VtnUpdateType.REMOVED)); + assertEquals(null, + pmResult.put(pmap5.getIndex(), VtnUpdateType.REMOVED)); + assertEquals(null, pmResult.put(pmap4.getIndex(), null)); + + // Duplicate indices should be ignored. + Collections.addAll( + indices, pmap2.getIndex(), pmap4.getIndex(), pmap5.getIndex(), + pmap2.getIndex(), pmap4.getIndex(), pmap5.getIndex()); + + // Invalid indices in remove-path-map input should be ignored. + for (int idx = -10; idx <= 10; idx++) { + if (idx < VTN_INDEX_MIN || idx > VTN_INDEX_MAX) { + pmResult.put(idx, null); + indices.add(idx); + } + } + + assertEquals(pmResult, pmSet.removeMaps(pmSrv, tname, indices)); + vnet.verify(); + + // Error tests. + + // No path map in set-path-map input. + SetPathMapInput input = new SetPathMapInputBuilder(). + setTenantName(tname). + build(); + checkRpcError(pmSrv.setPathMap(input), + RpcErrorTag.MISSING_ELEMENT, VtnErrorTag.BADREQUEST); + + input = new SetPathMapInputBuilder(). + setTenantName(tname). + setPathMapList(Collections.emptyList()). + build(); + checkRpcError(pmSrv.setPathMap(input), + RpcErrorTag.MISSING_ELEMENT, VtnErrorTag.BADREQUEST); + + // Null path map. + input = new SetPathMapInputBuilder(). + setTenantName(tname). + setPathMapList(Collections.singletonList((PathMapList)null)). + build(); + checkRpcError(pmSrv.setPathMap(input), + RpcErrorTag.MISSING_ELEMENT, VtnErrorTag.BADREQUEST); + + // No path map index. + String cond = "cond"; + PathMap pmap = new PathMap(null, cond); + input = PathMapSet.newInputBuilder(pmap). + setTenantName(tname). + build(); + checkRpcError(pmSrv.setPathMap(input), + RpcErrorTag.MISSING_ELEMENT, VtnErrorTag.BADREQUEST); + + // No flow condition name. + Integer index = 1; + pmap = new PathMap(index, null); + input = PathMapSet.newInputBuilder(pmap). + setTenantName(tname). + build(); + checkRpcError(pmSrv.setPathMap(input), + RpcErrorTag.MISSING_ELEMENT, VtnErrorTag.BADREQUEST); + + Integer policy = PATH_POLICY_ID_MIN; + int[] timeouts = {1, 3000, 65535}; + for (int t: timeouts) { + // Specifying idle timeout without hard timeout. + Integer timeout = Integer.valueOf(t); + pmap = new PathMap(index, cond, policy, timeout, null); + input = PathMapSet.newInputBuilder(pmap). + setTenantName(tname). + build(); + checkRpcError(pmSrv.setPathMap(input), + RpcErrorTag.BAD_ELEMENT, VtnErrorTag.BADREQUEST); + + // Specifying hard timeout without specifying idle timeout. + pmap = new PathMap(index, cond, policy, null, timeout); + input = PathMapSet.newInputBuilder(pmap). + setTenantName(tname). + build(); + checkRpcError(pmSrv.setPathMap(input), + RpcErrorTag.BAD_ELEMENT, VtnErrorTag.BADREQUEST); + + // Inconsistent timeout. + pmap = new PathMap(index, cond, policy, timeout, timeout); + input = PathMapSet.newInputBuilder(pmap). + setTenantName(tname). + build(); + checkRpcError(pmSrv.setPathMap(input), + RpcErrorTag.BAD_ELEMENT, VtnErrorTag.BADREQUEST); + + if (timeout != 65535) { + pmap = new PathMap(index, cond, policy, t + 1, timeout); + input = PathMapSet.newInputBuilder(pmap). + setTenantName(tname). + build(); + checkRpcError(pmSrv.setPathMap(input), + RpcErrorTag.BAD_ELEMENT, VtnErrorTag.BADREQUEST); + } + } + + // Duplicate path map index. + input = PathMapSet.newInputBuilder( + new PathMap(index, cond), + new PathMap(VTN_INDEX_MAX, cond, policy), + new PathMap(index, cond, policy)). + build(); + checkRpcError(pmSrv.setPathMap(input), + RpcErrorTag.BAD_ELEMENT, VtnErrorTag.BADREQUEST); + + // No path map index in remove-path-map input. + RemovePathMapInput rinput = new RemovePathMapInputBuilder(). + setTenantName(tname). + build(); + checkRpcError(pmSrv.removePathMap(rinput), + RpcErrorTag.MISSING_ELEMENT, VtnErrorTag.BADREQUEST); + + rinput = new RemovePathMapInputBuilder(). + setTenantName(tname). + setMapIndex(Collections.emptyList()). + build(); + checkRpcError(pmSrv.removePathMap(rinput), + RpcErrorTag.MISSING_ELEMENT, VtnErrorTag.BADREQUEST); + + // Null map index. + rinput = new RemovePathMapInputBuilder(). + setTenantName(tname). + setMapIndex(Collections.singletonList((Integer)null)). + build(); + checkRpcError(pmSrv.removePathMap(rinput), + RpcErrorTag.MISSING_ELEMENT, VtnErrorTag.BADREQUEST); + + // Errors should never affect path policies. + vnet.verify(); + } + + /** + * Ensure that path map RPCs return NOTFOUND error if the specified VTN + * is not present. + * + * @param pmSrv vtn-path-map service. + * @param rand A pseudo random generator. + * @param tname The name of the target VTN. + * @throws Exception An error occurred. + */ + private void notFoundTest(VtnPathMapService pmSrv, Random rand, + String tname) throws Exception { + PathMapSet pset = new PathMapSet().add(rand); + List idxList = Collections.singletonList(Integer.valueOf(1)); + + SetPathMapInput input = pset.newInputBuilder(). + setTenantName(tname). + build(); + checkRpcError(pmSrv.setPathMap(input), + RpcErrorTag.DATA_MISSING, VtnErrorTag.NOTFOUND); + + RemovePathMapInput rinput = new RemovePathMapInputBuilder(). + setTenantName(tname). + setMapIndex(idxList). + build(); + checkRpcError(pmSrv.removePathMap(rinput), + RpcErrorTag.DATA_MISSING, VtnErrorTag.NOTFOUND); + + ClearPathMapInput cinput = new ClearPathMapInputBuilder(). + setTenantName(tname). + build(); + checkRpcError(pmSrv.clearPathMap(cinput), + RpcErrorTag.DATA_MISSING, VtnErrorTag.NOTFOUND); + } +} diff --git a/manager/it/core/src/test/java/org/opendaylight/vtn/manager/it/core/VTNManagerIT.java b/manager/it/core/src/test/java/org/opendaylight/vtn/manager/it/core/VTNManagerIT.java index e5ad2e49..13d98eab 100644 --- a/manager/it/core/src/test/java/org/opendaylight/vtn/manager/it/core/VTNManagerIT.java +++ b/manager/it/core/src/test/java/org/opendaylight/vtn/manager/it/core/VTNManagerIT.java @@ -437,6 +437,21 @@ public final class VTNManagerIT extends ModelDrivenTestBase new PathPolicyServiceTest(this).runTest(); } + /** + * Test case for {@link VtnPathMapService}. + * + *

+ * This test is independent of inventory information. + *

+ * + * @throws Exception An error occurred. + */ + @Test + public void testPathMapSevice() throws Exception { + LOG.info("Running testPathMapSevice()."); + new PathMapServiceTest(this).runTest(); + } + /** * Ensure that the state of virtual bridge and virtual interface are * changed according to inventory events. diff --git a/manager/it/util/src/main/java/org/opendaylight/vtn/manager/it/util/pathmap/PathMap.java b/manager/it/util/src/main/java/org/opendaylight/vtn/manager/it/util/pathmap/PathMap.java index a2dc14f0..3ee279a4 100644 --- a/manager/it/util/src/main/java/org/opendaylight/vtn/manager/it/util/pathmap/PathMap.java +++ b/manager/it/util/src/main/java/org/opendaylight/vtn/manager/it/util/pathmap/PathMap.java @@ -66,6 +66,16 @@ public final class PathMap { */ private Integer hardTimeout; + /** + * Construct a new instance without specifying policy ID and flow timeout. + * + * @param idx The index of the path map. + * @param cond The name of the flow condition. + */ + public PathMap(Integer idx, String cond) { + this(idx, cond, null, null, null); + } + /** * Construct a new instance without specifying flow timeout. * diff --git a/manager/it/util/src/main/java/org/opendaylight/vtn/manager/it/util/pathmap/PathMapSet.java b/manager/it/util/src/main/java/org/opendaylight/vtn/manager/it/util/pathmap/PathMapSet.java index 42cf2573..a52a30b1 100644 --- a/manager/it/util/src/main/java/org/opendaylight/vtn/manager/it/util/pathmap/PathMapSet.java +++ b/manager/it/util/src/main/java/org/opendaylight/vtn/manager/it/util/pathmap/PathMapSet.java @@ -16,10 +16,12 @@ import static org.opendaylight.vtn.manager.it.util.ModelDrivenTestBase.getRpcRes import static org.opendaylight.vtn.manager.it.util.TestBase.RANDOM_ADD_MAX; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; +import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Random; @@ -45,11 +47,11 @@ import org.opendaylight.yang.gen.v1.urn.opendaylight.vtn.types.rev150209.VtnUpda /** * {@code PathMapSet} describes a set of path map configurations. */ -public final class PathMapSet { +public final class PathMapSet implements Cloneable { /** * A map that keeps path map configurations. */ - private final Map pathMaps = new HashMap<>(); + private Map pathMaps = new HashMap<>(); /** * Remove all the path maps in the specified path map list. @@ -106,6 +108,37 @@ public final class PathMapSet { return getResultMap(output.getRemovePathMapResult()); } + /** + * Create a new input builder for set-path-map RPC. + * + * @param pmaps An array of {@link PathMap} instances. + * @return A {@link SetPathMapInputBuilder} instance. + */ + public static SetPathMapInputBuilder newInputBuilder(PathMap ... pmaps) { + List list = (pmaps == null) ? null : Arrays.asList(pmaps); + return newInputBuilder(list); + } + + /** + * Create a new input builder for set-path-map RPC. + * + * @param pmaps A collection of {@link PathMap} instances. + * @return A {@link SetPathMapInputBuilder} instance. + */ + public static SetPathMapInputBuilder newInputBuilder( + Collection pmaps) { + SetPathMapInputBuilder builder = new SetPathMapInputBuilder(); + if (pmaps != null) { + List list = new ArrayList<>(pmaps.size()); + for (PathMap pmap: pmaps) { + list.add(pmap.toPathMapList()); + } + builder.setPathMapList(list); + } + + return builder; + } + /** * Create a map that indicates the given result of path map RPC. * @@ -151,22 +184,52 @@ public final class PathMapSet { /** * Add the given path map configuration. * - * @param pmap A {@link PathMap} instance. + * @param pmaps An array of {@link PathMap} instances. * @return This instance. */ - public PathMapSet add(PathMap pmap) { - pathMaps.put(pmap.getIndex(), pmap); + public PathMapSet add(PathMap ... pmaps) { + for (PathMap pmap: pmaps) { + pathMaps.put(pmap.getIndex(), pmap); + } + return this; + } + + /** + * Add all the path maps in the given path map set. + * + * @param pmSet A {@link PathMapSet} instance. + * @return This instance. + */ + public PathMapSet add(PathMapSet pmSet) { + for (PathMap pmap: pmSet.pathMaps.values()) { + pathMaps.put(pmap.getIndex(), pmap); + } return this; } /** * Remove the path map configuration specified by the given index. * - * @param idx The index of the path map. + * @param pmaps An array of {@link PathMap} instances. + * @return This instance. + */ + public PathMapSet remove(PathMap ... pmaps) { + for (PathMap pmap: pmaps) { + pathMaps.remove(pmap.getIndex()); + } + return this; + } + + /** + * Remove the path map configuration specified by the given index. + * + * @param indices An array of the path map indices to be removed. * @return This instance. */ - public PathMapSet remove(Integer idx) { - pathMaps.remove(idx); + public PathMapSet remove(Integer ... indices) { + for (Integer idx: indices) { + pathMaps.remove(idx); + } return this; } @@ -206,17 +269,12 @@ public final class PathMapSet { * @return A {@link SetPathMapInputBuilder} instance. */ public SetPathMapInputBuilder newInputBuilder() { - List pmaps; - if (pathMaps.isEmpty()) { + Collection pmaps = pathMaps.values(); + if (pmaps.isEmpty()) { pmaps = null; - } else { - pmaps = new ArrayList<>(pathMaps.size()); - for (PathMap pmap: pathMaps.values()) { - pmaps.add(pmap.toPathMapList()); - } } - return new SetPathMapInputBuilder().setPathMapList(pmaps); + return newInputBuilder(pmaps); } /** @@ -247,6 +305,186 @@ public final class PathMapSet { return getResultMap(output.getSetPathMapResult()); } + /** + * Restore all the path maps in this instance. + * + *

+ * This method expects that this instance contains at least one path map, + * and all the path maps in this instance are not configured yet. + *

+ * + * @param service The vtn-path-map service. + * @param tname The name of the VTN. + * {@code null} implies the global path map. + */ + public void restore(VtnPathMapService service, String tname) { + assertEquals(false, pathMaps.isEmpty()); + + Map expected = new HashMap<>(); + for (Integer idx: pathMaps.keySet()) { + assertEquals(null, expected.put(idx, VtnUpdateType.CREATED)); + } + + assertEquals(expected, update(service, tname)); + } + + /** + * Add the given path map using set-path-map RPC, and then add it to + * this instance. + * + * @param service The vtn-path-map service. + * @param tname The name of the VTN. + * {@code null} implies the global path map. + * @param pmap A {@link PathMap} instance. + * @return A {@link VtnUpdateType} instance returned by the RPC. + */ + public VtnUpdateType addMap(VtnPathMapService service, String tname, + PathMap pmap) { + Map result = addMaps(service, tname, pmap); + Integer idx = pmap.getIndex(); + assertEquals(Collections.singleton(idx), result.keySet()); + return result.get(idx); + } + + /** + * Add the given path maps using set-path-map RPC, and then add them to + * this instance. + * + * @param service The vtn-path-map service. + * @param tname The name of the VTN. + * {@code null} implies the global path map. + * @param pmaps An array of {@link PathMap} instances to be added. + * @return A map that contains the RPC result. + */ + public Map addMaps( + VtnPathMapService service, String tname, PathMap ... pmaps) { + List list = (pmaps == null) ? null : Arrays.asList(pmaps); + return addMaps(service, tname, list); + } + + /** + * Add the given path maps using set-path-map RPC, and then add them to + * this instance. + * + * @param service The vtn-path-map service. + * @param tname The name of the VTN. + * {@code null} implies the global path map. + * @param pmaps A collection of {@link PathMap} instances to be added. + * @return A map that contains the RPC result. + */ + public Map addMaps( + VtnPathMapService service, String tname, Collection pmaps) { + SetPathMapInput input = newInputBuilder(pmaps). + setTenantName(tname). + build(); + SetPathMapOutput output = getRpcOutput(service.setPathMap(input)); + Map result = + getResultMap(output.getSetPathMapResult()); + + if (pmaps != null) { + for (PathMap pmap: pmaps) { + pathMaps.put(pmap.getIndex(), pmap); + } + } + + return result; + } + + /** + * Remove the given path map using remove-path-map RPC, and then remove it + * from this instance. + * + * @param service The vtn-path-map service. + * @param tname The name of the VTN. + * {@code null} implies the global path map. + * @param pmap A {@link PathMap} instance. + * @return A {@link VtnUpdateType} instance returned by the RPC. + */ + public VtnUpdateType removeMap(VtnPathMapService service, String tname, + PathMap pmap) { + return removeMap(service, tname, pmap.getIndex()); + } + + /** + * Remove the given path map using remove-path-map RPC, and then remove it + * from this instance. + * + * @param service The vtn-path-map service. + * @param tname The name of the VTN. + * {@code null} implies the global path map. + * @param idx The index for the path map to be removed. + * @return A {@link VtnUpdateType} instance returned by the RPC. + */ + public VtnUpdateType removeMap(VtnPathMapService service, String tname, + Integer idx) { + Map result = removeMaps(service, tname, idx); + assertEquals(Collections.singleton(idx), result.keySet()); + return result.get(idx); + } + + /** + * Remove the given path maps using remove-path-map RPC, and then remove + * them from this instance. + * + * @param service The vtn-path-map service. + * @param tname The name of the VTN. + * {@code null} implies the global path map. + * @param indices An array of path map indices. + * @return A map that contains the RPC result. + */ + public Map removeMaps( + VtnPathMapService service, String tname, Integer ... indices) { + List list = (indices == null) ? null : Arrays.asList(indices); + return removeMaps(service, tname, list); + } + + /** + * Remove the given path maps using remove-path-map RPC, and then remove + * them from this instance. + * + * @param service The vtn-path-map service. + * @param tname The name of the VTN. + * {@code null} implies the global path map. + * @param indices A list of path map indices. + * @return A map that contains the RPC result. + */ + public Map removeMaps( + VtnPathMapService service, String tname, List indices) { + Map result = + removePathMap(service, tname, indices); + + if (indices != null) { + for (Integer idx: indices) { + pathMaps.remove(idx); + } + } + + return result; + } + + /** + * Remove all the path maps in this instance using remove-path-map RPC. + * + * @param service The vtn-path-map service. + * @param tname The name of the VTN. + * {@code null} implies the global path map. + */ + public void removeAll(VtnPathMapService service, String tname) { + if (!pathMaps.isEmpty()) { + List indices = new ArrayList<>(); + Map resMap = new HashMap<>(); + for (Iterator it = pathMaps.keySet().iterator(); + it.hasNext();) { + Integer idx = it.next(); + indices.add(idx); + assertEquals(null, resMap.put(idx, VtnUpdateType.REMOVED)); + it.remove(); + } + + assertEquals(resMap, removePathMap(service, tname, indices)); + } + } + /** * Verify the given path map container. * @@ -316,21 +554,40 @@ public final class PathMapSet { clearPathMap(service, tname)); } else { List unwanted = new ArrayList<>(); + Map resMap = new HashMap<>(); for (VtnPathMap vpm: vpms) { Integer index = vpm.getIndex(); if (!pathMaps.containsKey(index)) { unwanted.add(index); + resMap.put(index, VtnUpdateType.REMOVED); } } - Map result = - removePathMap(service, tname, unwanted); - assertEquals(unwanted.size(), result.size()); - for (Integer index: unwanted) { - assertEquals(VtnUpdateType.REMOVED, result.get(index)); + if (!unwanted.isEmpty()) { + assertEquals(resMap, + removePathMap(service, tname, unwanted)); } } } } } + + // Object + + /** + * Create a shallow copy of this instance. + * + * @return A shallow copy of this instance. + */ + @Override + public PathMapSet clone() { + try { + PathMapSet pmSet = (PathMapSet)super.clone(); + pmSet.pathMaps = new HashMap<>(pathMaps); + return pmSet; + } catch (CloneNotSupportedException e) { + // This should never happen. + throw new IllegalStateException("clone() failed", e); + } + } }