/* * Copyright (c) 2016 EMC Corporation * All Rights Reserved */ package com.emc.storageos.volumecontroller.impl.block; import static com.emc.storageos.volumecontroller.impl.block.ExportMaskPolicy.EXPORT_TYPE.REGULAR; import static com.emc.storageos.volumecontroller.impl.block.ExportMaskPolicy.IG_TYPE.CASCADED; import static com.emc.storageos.volumecontroller.impl.block.ExportMaskPolicy.IG_TYPE.SIMPLE; import java.net.URI; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Random; import java.util.Set; import java.util.UUID; import org.junit.Assert; import org.junit.BeforeClass; import org.junit.Test; import org.springframework.context.support.ClassPathXmlApplicationContext; import com.emc.storageos.db.client.model.BlockObject; import com.emc.storageos.db.client.model.ExportMask; import com.emc.storageos.db.client.model.Host; import com.emc.storageos.db.client.model.Initiator; import com.emc.storageos.db.client.model.StorageSystem; import com.emc.storageos.db.client.model.StringSet; import com.emc.storageos.db.client.model.Volume; import com.emc.storageos.services.util.StorageDriverManager; /** * Tester class for ExportMaskPlacementDescriptor usage. */ public class ExportMaskPlacementDescriptorTest { private static final Random RANDOMIZER = new Random(System.currentTimeMillis() % 997); private static final String URI_FORMAT = "%s:%s"; private static List<String> ALL_SLOS = new ArrayList<>(); @BeforeClass public static void setup() { mockStorageDriverManager(); ALL_SLOS.add("Bronze"); ALL_SLOS.add("Silver"); ALL_SLOS.add("Gold"); ALL_SLOS.add("Platinum"); ALL_SLOS.add("Diamond"); ALL_SLOS.add("Unobtainium"); } private static void mockStorageDriverManager() { StorageDriverManager storageDriverManager = new StorageDriverManager(); storageDriverManager.setApplicationContext(new ClassPathXmlApplicationContext("driver-conf.xml")); } /** * ==================================================================================================================================== * Test: validate that we can use the ExportMaskPlacementDescriptor place a set of volumes against a set of ExportMasks * ==================================================================================================================================== */ @Test public void testPlaceAllVolumes() { // Create a simple descriptor with some ExportMasks to place ExportMaskPlacementDescriptor descriptor = createDescriptor(2, 2); Map<URI, ExportMask> masks = mockExportMasks(2); descriptor.setMasks(masks); // Place all the volumes for each mask Iterator<URI> iterator = masks.keySet().iterator(); descriptor.placeVolumes(iterator.next(), descriptor.getVolumesToPlace()); descriptor.placeVolumes(iterator.next(), descriptor.getVolumesToPlace()); // ---- Test validation ---- // After placing all the volumes to each ExportMask, check the values // returned by the descriptor. Assert.assertTrue(descriptor.hasMasks()); Assert.assertTrue(descriptor.getPlacedMasks().containsAll(masks.keySet())); Assert.assertFalse(descriptor.hasUnPlacedVolumes()); Assert.assertFalse(descriptor.getUnplacedVolumes().size() != 0); System.out.printf("Placed all volumes:\n%s", descriptor); } /** * ==================================================================================================================================== * Test: Randomized placement. * - Create a random set of Volumes * - Create a random set of ExportMasks * - Determine a random set of Volumes to place * - Determine a random set of ExportMasks to use * - Place all the volumes against the chosen set of ExportMasks * - Test the accuracy of placed and unplaced volumes * ==================================================================================================================================== */ @Test public void testPlaceVolumesRandomly() { // Create descriptor with random number of volumes between 2 and 10 ExportMaskPlacementDescriptor descriptor = createDescriptor(2, RANDOMIZER.nextInt(8) + 2); // Create mock ExportMasks (between 2 and 10 of them) Map<URI, ExportMask> masks = mockExportMasks(RANDOMIZER.nextInt(8) + 2); descriptor.setMasks(masks); // Select the number of masks to use for placement int numMasksToUse = (RANDOMIZER.nextInt(masks.size()) / 2) + 1; // Create random assortment of volumes to place Map<URI, Volume> volumes = randomVolumeMap(descriptor); System.out.printf("We're picking %d volumes from the descriptor that has %d volumes\n", volumes.size(), descriptor.getVolumesToPlace().size()); System.out.printf("We will be placing the volumes into %d out of %d masks\n", numMasksToUse, masks.size()); // Place all the volumes Iterator<URI> iterator = masks.keySet().iterator(); for (int i = 0; i < numMasksToUse; i++) { descriptor.placeVolumes(iterator.next(), volumes); } // ---- Test validation ---- // After placing the volumes, check that the numbers returned by the descriptor. Map<URI, Volume> volumesToPlace = descriptor.getVolumesToPlace(); Map<URI, Volume> unplacedVolumes = descriptor.getUnplacedVolumes(); if (volumes.size() != volumesToPlace.size()) { Assert.assertTrue(descriptor.hasUnPlacedVolumes()); Assert.assertTrue(unplacedVolumes.size() != 0); int unplacedVolumeCount = volumesToPlace.size() - volumes.size(); Assert.assertEquals(unplacedVolumeCount, unplacedVolumes.size()); System.out.printf("We placed %d volumes and the descriptor shows that there are %d unplaced volumes\n", volumes.size(), unplacedVolumes.size()); } else { Assert.assertFalse(descriptor.hasUnPlacedVolumes()); Assert.assertTrue(unplacedVolumes.size() == 0); System.out.printf("All volumes were placed"); } } /** * ==================================================================================================================================== * Test: ExportMaskPlacementDescriptor#getEquivalentExportMasks * - Create volumes and a set of ExportMasks * - Create a single ExportMaskPolicy * - Use ExportMaskPlacementDescriptor#addToEquivalentMasks to associate the same policy for all the created ExportMasks * - Validate getEquivalentExportMasks results * ==================================================================================================================================== */ @Test public void testAddToEquivalentMasksSamePolicy() { // Create a simple descriptor with some ExportMasks to place ExportMaskPlacementDescriptor descriptor = createDescriptor(2, 2); Map<URI, ExportMask> masks = mockExportMasks(2); descriptor.setMasks(masks); Set<String> slos = new HashSet<>(); slos.add("Gold"); slos.add("Green"); ExportMaskPolicy policy = mockExportMaskPolicy(REGULAR.name(), CASCADED.name(), null, "mask1SgName", false, slos, 0, 0); List<ExportMask> xp = new ArrayList<>(masks.values()); descriptor.addToEquivalentMasks(xp.get(0), policy); descriptor.addToEquivalentMasks(xp.get(1), policy); Set<URI> eqToMask1 = descriptor.getEquivalentExportMasks(xp.get(0).getId()); Set<URI> eqToMask2 = descriptor.getEquivalentExportMasks(xp.get(1).getId()); System.out.printf("ExportMasks %s and %s have been set as equivalent exports\n", xp.get(0).getMaskName(), xp.get(1).getMaskName()); // ---- Test validation ---- // Since we created a single ExportMaskPolicy and used that for each ExportMask, // there should be equivalent ExportMasks returned, but make sure that it does // not contain the ExportMask that your checking for equivalence. Assert.assertTrue(eqToMask1.contains(xp.get(1).getId())); Assert.assertFalse(eqToMask1.contains(xp.get(0).getId())); System.out.printf("Equivalent exports for %s is correct\n", xp.get(0).getMaskName()); Assert.assertTrue(eqToMask2.contains(xp.get(0).getId())); Assert.assertFalse(eqToMask2.contains(xp.get(1).getId())); System.out.printf("Equivalent exports for %s is correct\n", xp.get(1).getMaskName()); // Place all volumes in one mask Iterator<URI> iterator = masks.keySet().iterator(); descriptor.placeVolumes(iterator.next(), descriptor.getVolumesToPlace()); Assert.assertFalse(descriptor.hasUnPlacedVolumes()); Assert.assertTrue(descriptor.hasMasks()); System.out.printf("Descriptor showed that we could get the correct set of equivalent exportMasks"); } /** * ==================================================================================================================================== * Test: ExportMaskPlacementDescriptor#getEquivalentExportMasks * - Create volumes and a set of ExportMasks * - Create different ExportMaskPolicies * - Use ExportMaskPlacementDescriptor#addToEquivalentMasks to associate different ExportMaskPolicy * - Validate getEquivalentExportMasks results * ==================================================================================================================================== */ @Test public void testAddToEquivalentMasksDiffPolicy() { // Create a simple descriptor with some ExportMasks to place ExportMaskPlacementDescriptor descriptor = createDescriptor(2, 2); Map<URI, ExportMask> masks = mockExportMasks(2); descriptor.setMasks(masks); Set<String> slos = new HashSet<>(); slos.add("Gold"); slos.add("Green"); ExportMaskPolicy policy1 = mockExportMaskPolicy(REGULAR.name(), CASCADED.name(), null, "mask1SgName", false, slos, 0, 0); ExportMaskPolicy policy2 = mockExportMaskPolicy(REGULAR.name(), SIMPLE.name(), null, "mask2SgName", true, slos, 0, 0); List<ExportMask> xp = new ArrayList<>(masks.values()); descriptor.addToEquivalentMasks(xp.get(0), policy1); descriptor.addToEquivalentMasks(xp.get(1), policy2); Set<URI> eqToMask1 = descriptor.getEquivalentExportMasks(xp.get(0).getId()); Set<URI> eqToMask2 = descriptor.getEquivalentExportMasks(xp.get(1).getId()); // ---- Test validation ---- // We have different policies for each ExportMask, so we shouldn't get any equivalent masks Assert.assertTrue(eqToMask1.isEmpty()); System.out.printf("Equivalent exports for %s is correct\n", xp.get(0).getMaskName()); Assert.assertTrue(eqToMask2.isEmpty()); System.out.printf("Equivalent exports for %s is correct\n", xp.get(1).getMaskName()); // Place all volumes in one mask Iterator<URI> iterator = masks.keySet().iterator(); descriptor.placeVolumes(iterator.next(), descriptor.getVolumesToPlace()); Assert.assertFalse(descriptor.hasUnPlacedVolumes()); Assert.assertTrue(descriptor.hasMasks()); System.out.printf("Descriptor showed that equivalentExportMasks when queried"); } /** * ==================================================================================================================================== * Test: Randomized ExportMaskPlacementDescriptor#getEquivalentExportMasks * - Create volumes and a set of ExportMasks * - Create a randomized set of ExportMaskPolicies * - Use ExportMaskPlacementDescriptor#addToEquivalentMasks to associate to the policies created * - Validate getEquivalentExportMasks results * ==================================================================================================================================== */ @Test public void testAddToEquivalentMasksRandomPolicy() { // Create a simple descriptor with some ExportMasks to place ExportMaskPlacementDescriptor descriptor = createDescriptor(2, 2); Map<URI, ExportMask> masks = mockExportMasks(4); descriptor.setMasks(masks); Set<String> slos = new HashSet<>(); int size = RANDOMIZER.nextInt(ALL_SLOS.size()); for (int i = 0; i < size; i++) { slos.add(ALL_SLOS.get(i)); } List<ExportMask> xp = new ArrayList<>(masks.values()); ExportMaskPolicy.EXPORT_TYPE[] exportTypes = ExportMaskPolicy.EXPORT_TYPE.values(); ExportMaskPolicy.IG_TYPE[] exportIgTypes = ExportMaskPolicy.IG_TYPE.values(); for (int j = 0; j < 4; j++) { String xpType = exportTypes[RANDOMIZER.nextInt(exportTypes.length)].name(); String igType = exportIgTypes[RANDOMIZER.nextInt(exportIgTypes.length)].name(); ExportMaskPolicy policy = mockExportMaskPolicy(xpType, igType, null, String.format("mask%dSg", j), RANDOMIZER.nextBoolean(), slos, 0, 0); ExportMask mask = xp.get(j); descriptor.addToEquivalentMasks(mask, policy); System.out.println(String.format("ExportMask %s has policy %s", mask.getMaskName(), policy.toString())); } Set<URI> eqToMask1 = descriptor.getEquivalentExportMasks(xp.get(0).getId()); Set<URI> eqToMask2 = descriptor.getEquivalentExportMasks(xp.get(1).getId()); Set<URI> eqToMask3 = descriptor.getEquivalentExportMasks(xp.get(2).getId()); Set<URI> eqToMask4 = descriptor.getEquivalentExportMasks(xp.get(3).getId()); // ---- Test validation ---- // We did a random creation of ExportMaskPolicy for each ExportMask. // Check that each ExportMask does not show up in its equivalence set. if (!eqToMask1.isEmpty()) { System.out.printf("There are equivalent masks for %s\n", xp.get(0).getMaskName()); Assert.assertFalse(eqToMask1.contains(xp.get(0).getId())); } else { System.out.printf("There are no equivalent masks for %s\n", xp.get(0).getMaskName()); } if (!eqToMask2.isEmpty()) { System.out.printf("There are equivalent masks for %s\n", xp.get(1).getMaskName()); Assert.assertFalse(eqToMask2.contains(xp.get(1).getId())); } else { System.out.printf("There are no equivalent masks for %s\n", xp.get(1).getMaskName()); } if (!eqToMask3.isEmpty()) { System.out.printf("There are equivalent masks for %s\n", xp.get(2).getMaskName()); Assert.assertFalse(eqToMask3.contains(xp.get(2).getId())); } else { System.out.printf("There are no equivalent masks for %s\n", xp.get(2).getMaskName()); } if (!eqToMask4.isEmpty()) { System.out.printf("There are equivalent masks for %s\n", xp.get(3).getMaskName()); Assert.assertFalse(eqToMask4.contains(xp.get(3).getId())); } else { System.out.printf("There are no equivalent masks for %s\n", xp.get(3).getMaskName()); } // Place all volumes in one mask Iterator<URI> iterator = masks.keySet().iterator(); descriptor.placeVolumes(iterator.next(), descriptor.getVolumesToPlace()); Assert.assertFalse(descriptor.hasUnPlacedVolumes()); Assert.assertTrue(descriptor.hasMasks()); } /** * ==================================================================================================================================== * Test: ExportMaskPlacementDescriptor#invalidateExportMask * - Create volumes and a set of ExportMasks * - Create a single ExportMaskPolicy * - Use ExportMaskPlacementDescriptor#addToEquivalentMasks to associate the same policy for all the created ExportMasks * - Place all volumes to a single ExportMask * - Invalidate the ExportMask used for placement * - Validate that ExportMask shows unplaced volumes and other attributes related to the invalidation are correct. * ==================================================================================================================================== */ @Test public void testInvalidateExportMask1() { // Create a simple descriptor with some ExportMasks to place ExportMaskPlacementDescriptor descriptor = createDescriptor(2, 2); Map<URI, ExportMask> masks = mockExportMasks(2); descriptor.setMasks(masks); Set<String> slos = new HashSet<>(); slos.add("Gold"); slos.add("Green"); ExportMaskPolicy policy = mockExportMaskPolicy(REGULAR.name(), CASCADED.name(), null, "mask1SgName", false, slos, 0, 0); List<ExportMask> xp = new ArrayList<>(masks.values()); descriptor.addToEquivalentMasks(xp.get(0), policy); descriptor.addToEquivalentMasks(xp.get(1), policy); // Place volumes into **one** ExportMask descriptor.placeVolumes(xp.get(0).getId(), descriptor.getVolumesToPlace()); // ---- Test validation ---- // Preliminary validation of the placement Assert.assertTrue(descriptor.hasMasks()); Assert.assertEquals(2, descriptor.getMasks().size()); Assert.assertTrue(descriptor.getPlacedMasks().contains(xp.get(0).getId())); Assert.assertFalse(descriptor.hasUnPlacedVolumes()); Assert.assertFalse(descriptor.getUnplacedVolumes().size() != 0); System.out.printf("Descriptor before invalidation:\n%s\n", descriptor); // Invalidate the ExportMask descriptor.invalidateExportMask(xp.get(0).getId()); // ---- Test validation ---- // The volumes were placed against a single ExportMask, which we invalidated. We should // see that the volumes were unplaced afterwords. Assert.assertTrue(descriptor.hasMasks()); Assert.assertEquals(1, descriptor.getMasks().size()); Assert.assertFalse(descriptor.getPlacedMasks().contains(xp.get(0).getId())); Assert.assertTrue(descriptor.hasUnPlacedVolumes()); Assert.assertTrue(descriptor.getUnplacedVolumes().size() != 0); System.out.printf("Descriptor after invalidation:\n%s", descriptor); } /** * ==================================================================================================================================== * Test: ExportMaskPlacementDescriptor#invalidateExportMask * - Create volumes and a set of ExportMasks * - Create a single ExportMaskPolicy * - Use ExportMaskPlacementDescriptor#addToEquivalentMasks to associate the same policy for all the created ExportMasks * - Place all volumes to *all* ExportMasks * - Invalidate the ExportMask used for placement * - Validate that ExportMask does not show unplaced volumes and other attributes related to that are correct * ==================================================================================================================================== */ @Test public void testInvalidateExportMask2() { // Create a simple descriptor with some ExportMasks to place ExportMaskPlacementDescriptor descriptor = createDescriptor(2, 2); Map<URI, ExportMask> masks = mockExportMasks(2); descriptor.setMasks(masks); Set<String> slos = new HashSet<>(); slos.add("Gold"); slos.add("Green"); ExportMaskPolicy policy = mockExportMaskPolicy(REGULAR.name(), CASCADED.name(), null, "mask1SgName", false, slos, 0, 0); List<ExportMask> xp = new ArrayList<>(masks.values()); descriptor.addToEquivalentMasks(xp.get(0), policy); descriptor.addToEquivalentMasks(xp.get(1), policy); // Place volumes into **all** ExportMasks descriptor.placeVolumes(xp.get(0).getId(), descriptor.getVolumesToPlace()); descriptor.placeVolumes(xp.get(1).getId(), descriptor.getVolumesToPlace()); // ---- Test validation ---- // Preliminary validation of the placement Assert.assertTrue(descriptor.hasMasks()); Assert.assertEquals(2, descriptor.getMasks().size()); Assert.assertTrue(descriptor.getPlacedMasks().contains(xp.get(0).getId())); Assert.assertFalse(descriptor.hasUnPlacedVolumes()); Assert.assertFalse(descriptor.getUnplacedVolumes().size() != 0); System.out.printf("Descriptor before invalidation:\n%s\n", descriptor); // Invalidate the ExportMask descriptor.invalidateExportMask(xp.get(0).getId()); // ---- Test validation ---- // Volumes were placed against both ExportMasks. When we invalidated one of the ExportMasks, // the other ExportMask should still have a mapping for the volumes Assert.assertTrue(descriptor.hasMasks()); Assert.assertEquals(1, descriptor.getMasks().size()); Assert.assertFalse(descriptor.getPlacedMasks().contains(xp.get(0).getId())); Assert.assertFalse(descriptor.hasUnPlacedVolumes()); Assert.assertFalse(descriptor.getUnplacedVolumes().size() != 0); System.out.printf("Descriptor after invalidation:\n%s", descriptor); } /** * ==================================================================================================================================== * Test: ExportMaskPlacementDescriptorHelper#getExportMaskWithLeastVolumes * - Create volumes * - Create 3 ExportMasks * - Create a single ExportMaskPolicy * - Use ExportMaskPlacementDescriptor#addToEquivalentMasks to associate the same policy for all the created ExportMasks * - Place all volumes into 1st ExportMask * - Add different volumes into 2nd ExportMask and 3rd ExportMask, making sure that 3rd ExportMask has more volumes than 2nd. * - Invalidate the 1st ExportMask used for placement * - Validate that ExportMask shows unplaced volumes and other attributes related to the invalidation are correct. * - Call ExportMaskPlacementDescriptorHelper#getExportMaskWithLeastVolumes * - Make sure that it shows that 2nd ExportMask is selected * ==================================================================================================================================== */ @Test public void testGetAlternateExportMasksForVolume1() { // Create a simple descriptor with some ExportMasks to place ExportMaskPlacementDescriptor descriptor = createDescriptor(2, 2); Map<URI, ExportMask> masks = mockExportMasks(3); descriptor.setMasks(masks); Set<String> slos = new HashSet<>(); slos.add("Gold"); slos.add("Green"); ExportMaskPolicy policy = mockExportMaskPolicy(REGULAR.name(), CASCADED.name(), null, "mask1SgName", false, slos, 0, 0); List<ExportMask> xp = new ArrayList<>(masks.values()); descriptor.addToEquivalentMasks(xp.get(0), policy); descriptor.addToEquivalentMasks(xp.get(1), policy); descriptor.addToEquivalentMasks(xp.get(2), policy); int volumeCount1 = RANDOMIZER.nextInt(10) + 1; Map<URI, Volume> volumes1 = mockSetOfVolumes(descriptor.getBackendArray(), volumeCount1); Map<URI, Integer> volToHLU1 = mockVolumeToHLUs(volumes1); xp.get(1).addVolumes(volToHLU1); xp.get(1).addToUserCreatedVolumes(mockVolumesToBlockObjects(volumes1)); // Make the 3rd ExportMask always have more volumes, that way xp.get(1) should be selected over xp.get(2) int volumeCount2 = volumeCount1 + RANDOMIZER.nextInt(5) + 1; Map<URI, Volume> volumes2 = mockSetOfVolumes(descriptor.getBackendArray(), volumeCount2); Map<URI, Integer> volToHLU2 = mockVolumeToHLUs(volumes2); xp.get(2).addVolumes(volToHLU2); xp.get(2).addToUserCreatedVolumes(mockVolumesToBlockObjects(volumes2)); Map<URI, Volume> volumesToPlace = descriptor.getVolumesToPlace(); // Place volumes into **one** ExportMask descriptor.placeVolumes(xp.get(0).getId(), volumesToPlace); // Add the other ExportMasks as alternates since they use the same ExportMaskPolicy for (URI volumeURI : volumesToPlace.keySet()) { descriptor.addAsAlternativeExportForVolume(volumeURI, xp.get(1).getId()); descriptor.addAsAlternativeExportForVolume(volumeURI, xp.get(2).getId()); } // ---- Test validation ---- // Preliminary validation of the placement Assert.assertTrue(descriptor.hasMasks()); Assert.assertEquals(3, descriptor.getMasks().size()); Assert.assertTrue(descriptor.getPlacedMasks().contains(xp.get(0).getId())); Assert.assertTrue(descriptor.getPlacedVolumes(xp.get(1).getId()).isEmpty()); Assert.assertTrue(descriptor.getPlacedVolumes(xp.get(2).getId()).isEmpty()); Assert.assertFalse(descriptor.hasUnPlacedVolumes()); Assert.assertFalse(descriptor.getUnplacedVolumes().size() != 0); System.out.printf("Descriptor before invalidation:\n%s\n", descriptor); // Invalidate the ExportMask that was placed descriptor.invalidateExportMask(xp.get(0).getId()); // Validate that we have unplaced volumes Assert.assertTrue(descriptor.hasUnPlacedVolumes()); Assert.assertTrue(descriptor.getUnplacedVolumes().size() != 0); // Find the one with the least number of volumes Set<URI> equivalentMasks = descriptor.getEquivalentExportMasks(xp.get(0).getId()); URI leastVolumeExportMaskURI = ExportMaskPlacementDescriptorHelper.getExportMaskWithLeastVolumes(descriptor, equivalentMasks); int xp1VolCount = xp.get(1).returnTotalVolumeCount(); int xp2VolCount = xp.get(2).returnTotalVolumeCount(); System.out.printf("Mask %s has %d volumes, Mask %s has %d volumes\n", xp.get(1).getMaskName(), xp1VolCount, xp.get(2).getMaskName(), xp2VolCount); // ---- Test validation ---- // See which export got the least number of volumes Assert.assertNotNull(leastVolumeExportMaskURI); URI expected = (xp1VolCount < xp2VolCount) ? xp.get(1).getId() : xp.get(2).getId(); Assert.assertEquals(expected, leastVolumeExportMaskURI); System.out.printf("Mask %s was correctly selected, since it has less volumes", xp.get(1).getMaskName()); } /** * ==================================================================================================================================== * Test: ExportMaskPlacementDescriptorHelper#getExportMaskWithLeastVolumes * - Create volumes * - Create 3 ExportMasks * - Create a single ExportMaskPolicy * - Use ExportMaskPlacementDescriptor#addToEquivalentMasks to associate the same policy for all the created ExportMasks * - Place all volumes into 1st ExportMask * - Add the same number of volumes into the 2nd and 3rd ExportMasks * - Invalidate the 1st ExportMask used for placement * - Validate that ExportMask shows unplaced volumes and other attributes related to the invalidation are correct. * - Call ExportMaskPlacementDescriptorHelper#getExportMaskWithLeastVolumes * - Make sure that it chose one of the equivalent ExportMasks * ==================================================================================================================================== */ @Test public void testGetAlternateExportMasksForVolume2() { // Create a simple descriptor with some ExportMasks to place ExportMaskPlacementDescriptor descriptor = createDescriptor(2, 2); Map<URI, ExportMask> masks = mockExportMasks(3); descriptor.setMasks(masks); Set<String> slos = new HashSet<>(); slos.add("Gold"); slos.add("Green"); ExportMaskPolicy policy = mockExportMaskPolicy(REGULAR.name(), CASCADED.name(), null, "mask1SgName", false, slos, 0, 0); List<ExportMask> xp = new ArrayList<>(masks.values()); descriptor.addToEquivalentMasks(xp.get(0), policy); descriptor.addToEquivalentMasks(xp.get(1), policy); descriptor.addToEquivalentMasks(xp.get(2), policy); int volumeCount = RANDOMIZER.nextInt(10) + 1; Map<URI, Volume> volumes1 = mockSetOfVolumes(descriptor.getBackendArray(), volumeCount); Map<URI, Integer> volToHLU1 = mockVolumeToHLUs(volumes1); xp.get(1).addVolumes(volToHLU1); xp.get(1).addToUserCreatedVolumes(mockVolumesToBlockObjects(volumes1)); // Make the 3rd ExportMask have same number of volumes Map<URI, Volume> volumes2 = mockSetOfVolumes(descriptor.getBackendArray(), volumeCount); Map<URI, Integer> volToHLU2 = mockVolumeToHLUs(volumes2); xp.get(2).addVolumes(volToHLU2); xp.get(2).addToUserCreatedVolumes(mockVolumesToBlockObjects(volumes2)); Map<URI, Volume> volumesToPlace = descriptor.getVolumesToPlace(); // Place volumes into **one** ExportMask descriptor.placeVolumes(xp.get(0).getId(), volumesToPlace); // Add the other ExportMasks as alternates since they use the same ExportMaskPolicy for (URI volumeURI : volumesToPlace.keySet()) { descriptor.addAsAlternativeExportForVolume(volumeURI, xp.get(1).getId()); descriptor.addAsAlternativeExportForVolume(volumeURI, xp.get(2).getId()); } // ---- Test validation ---- // Preliminary validation of the placement Assert.assertTrue(descriptor.hasMasks()); Assert.assertEquals(3, descriptor.getMasks().size()); Assert.assertTrue(descriptor.getPlacedMasks().contains(xp.get(0).getId())); Assert.assertTrue(descriptor.getPlacedVolumes(xp.get(1).getId()).isEmpty()); Assert.assertTrue(descriptor.getPlacedVolumes(xp.get(2).getId()).isEmpty()); Assert.assertFalse(descriptor.hasUnPlacedVolumes()); Assert.assertFalse(descriptor.getUnplacedVolumes().size() != 0); System.out.printf("Descriptor before invalidation:\n%s\n", descriptor); // Invalidate the ExportMask that was placed descriptor.invalidateExportMask(xp.get(0).getId()); // Validate that we have unplaced volumes Assert.assertTrue(descriptor.hasUnPlacedVolumes()); Assert.assertTrue(descriptor.getUnplacedVolumes().size() != 0); // Find the one with the least number of volumes Set<URI> equivalentMasks = descriptor.getEquivalentExportMasks(xp.get(0).getId()); URI leastVolumeExportMaskURI = ExportMaskPlacementDescriptorHelper.getExportMaskWithLeastVolumes(descriptor, equivalentMasks); int xp1VolCount = xp.get(1).returnTotalVolumeCount(); int xp2VolCount = xp.get(2).returnTotalVolumeCount(); System.out.printf("Mask %s has %d volumes, Mask %s has %d volumes\n", xp.get(1).getMaskName(), xp1VolCount, xp.get(2).getMaskName(), xp2VolCount); // ---- Test validation ---- // See which export got the least number of volumes Assert.assertNotNull(leastVolumeExportMaskURI); Assert.assertTrue(equivalentMasks.contains(leastVolumeExportMaskURI)); System.out.printf("Mask selected was %s", leastVolumeExportMaskURI); } /** * ==================================================================================================================================== * Test: ExportMaskPlacementDescriptor#addAsAlternativeExportForVolume * - Create volumes * - Create 2 ExportMasks * - Create a single ExportMaskPolicy * - Use ExportMaskPlacementDescriptor#addToEquivalentMasks to associate the same policy for all the created ExportMasks * - Place all volumes into 1st ExportMask * - Call ExportMaskPlacementDescriptor#addAsAlternativeExportForVolume * - Validate that correct alternatives are shown for the given volumes * ==================================================================================================================================== */ @Test public void testGetAlternateExportMasksForVolume3() { // Create a simple descriptor with some ExportMasks to place ExportMaskPlacementDescriptor descriptor = createDescriptor(2, 2); Map<URI, ExportMask> masks = mockExportMasks(2); descriptor.setMasks(masks); Set<String> slos = new HashSet<>(); slos.add("Gold"); slos.add("Green"); ExportMaskPolicy policy = mockExportMaskPolicy(REGULAR.name(), CASCADED.name(), null, "mask1SgName", false, slos, 0, 0); List<ExportMask> xp = new ArrayList<>(masks.values()); descriptor.addToEquivalentMasks(xp.get(0), policy); descriptor.addToEquivalentMasks(xp.get(1), policy); Map<URI, Volume> volumesToPlace = descriptor.getVolumesToPlace(); // Place volumes into **one** ExportMask descriptor.placeVolumes(xp.get(0).getId(), volumesToPlace); // Provide a way for say that the volume could have equally been placed // in the other ExportMask for (URI volumeURI : volumesToPlace.keySet()) { descriptor.addAsAlternativeExportForVolume(volumeURI, xp.get(1).getId()); } // ---- Test validation ---- // Preliminary validation of the placement Assert.assertTrue(descriptor.hasMasks()); Assert.assertEquals(2, descriptor.getMasks().size()); Assert.assertTrue(descriptor.getPlacedMasks().contains(xp.get(0).getId())); Assert.assertFalse(descriptor.hasUnPlacedVolumes()); Assert.assertFalse(descriptor.getUnplacedVolumes().size() != 0); System.out.printf("Descriptor:\n%s\n", descriptor); // Validate that we can find equivalent ExportMasks for each of the volumes for (URI volumeURI : volumesToPlace.keySet()) { Set<URI> alternates = descriptor.getAlternativeExportsForVolume(volumeURI); Assert.assertFalse(alternates.isEmpty()); Assert.assertFalse(alternates.contains(xp.get(0).getId())); Assert.assertTrue(alternates.contains(xp.get(1).getId())); } System.out.printf("Descriptor showed that we got the expected results when querying the alternative exports for the volumes"); } /** * ==================================================================================================================================== * Test: ExportMaskPlacementDescriptorHelper#putUnplacedVolumesIntoAlternativeMask * - Create volumes * - Create 2 ExportMasks * - Create a single ExportMaskPolicy * - Use ExportMaskPlacementDescriptor#addToEquivalentMasks to associate the same policy for all the created ExportMasks * - Place all volumes into 1st ExportMask * - Call ExportMaskPlacementDescriptor#addAsAlternativeExportForVolume to associate 2nd ExportMask as an alternate * - Call putUnplacedVolumesIntoAlternativeMask * - Validate the descriptor shows that an an alternative ExportMask was used for volume placement * ==================================================================================================================================== */ @Test public void testGetAlternateExportMasksForVolume4() { // Create a simple descriptor with some ExportMasks to place ExportMaskPlacementDescriptor descriptor = createDescriptor(2, 2); Map<URI, ExportMask> masks = mockExportMasks(2); descriptor.setMasks(masks); Set<String> slos = new HashSet<>(); slos.add("Gold"); slos.add("Green"); ExportMaskPolicy policy = mockExportMaskPolicy(REGULAR.name(), CASCADED.name(), null, "mask1SgName", false, slos, 0, 0); List<ExportMask> xp = new ArrayList<>(masks.values()); descriptor.addToEquivalentMasks(xp.get(0), policy); descriptor.addToEquivalentMasks(xp.get(1), policy); Map<URI, Volume> volumesToPlace = descriptor.getVolumesToPlace(); // Place volumes into **one** ExportMask descriptor.placeVolumes(xp.get(0).getId(), volumesToPlace); // Provide a way for say that the volume could have equally been placed // in the other ExportMask for (URI volumeURI : volumesToPlace.keySet()) { descriptor.addAsAlternativeExportForVolume(volumeURI, xp.get(1).getId()); } // ---- Test validation ---- // Preliminary validation of the placement Assert.assertTrue(descriptor.hasMasks()); Assert.assertEquals(2, descriptor.getMasks().size()); Assert.assertTrue(descriptor.getPlacedMasks().contains(xp.get(0).getId())); Assert.assertTrue(descriptor.getPlacedVolumes(xp.get(1).getId()).isEmpty()); Assert.assertFalse(descriptor.hasUnPlacedVolumes()); Assert.assertFalse(descriptor.getUnplacedVolumes().size() != 0); System.out.printf("Descriptor before invalidation:\n%s\n", descriptor); // Invalidate the ExportMask that was placed Set<URI> invalidMasks = new HashSet<>(); invalidMasks.add(xp.get(0).getId()); descriptor.invalidateExportMask(xp.get(0).getId()); // Validate that we have unplaced volumes Assert.assertTrue(descriptor.hasUnPlacedVolumes()); Assert.assertTrue(descriptor.getUnplacedVolumes().size() != 0); // Validate that we can find equivalent ExportMasks for each of the volumes for (URI volumeURI : volumesToPlace.keySet()) { Set<URI> alternates = descriptor.getAlternativeExportsForVolume(volumeURI); Assert.assertFalse(alternates.isEmpty()); Assert.assertFalse(alternates.contains(xp.get(0).getId())); Assert.assertTrue(alternates.contains(xp.get(1).getId())); } // Run the helper method to replace volumes to alternative ExportMasks ExportMaskPlacementDescriptorHelper.putUnplacedVolumesIntoAlternativeMask(descriptor); // Now we should have placed volumes Assert.assertFalse(descriptor.hasUnPlacedVolumes()); Assert.assertFalse(descriptor.getUnplacedVolumes().size() != 0); Assert.assertFalse(descriptor.getPlacedVolumes(xp.get(1).getId()).isEmpty()); Assert.assertTrue(descriptor.getPlacedVolumes(xp.get(0).getId()).isEmpty()); Map<URI, Volume> volumeMapForAlternateXP = descriptor.getPlacedVolumes(xp.get(1).getId()); Assert.assertTrue(volumesToPlace.keySet().containsAll(volumeMapForAlternateXP.keySet())); System.out.printf("Descriptor after putUnplacedVolumesIntoAlternativeMask:\n%s\n", descriptor); } // =================== HELPER & MOCK OBJECT CREATION METHODS =================== private Set<BlockObject> mockVolumesToBlockObjects(Map<URI, Volume> volumes) { Set<BlockObject> set = new HashSet<>(); for (Volume volume : volumes.values()) { set.add(volume); } return set; } private Map<URI, Integer> mockVolumeToHLUs(Map<URI, Volume> volumeMap) { Map<URI, Integer> map = new HashMap<>(); int i = 0; for (URI volumeURI : volumeMap.keySet()) { map.put(volumeURI, i++); } return map; } private Map<URI, Volume> randomVolumeMap(ExportMaskPlacementDescriptor descriptor) { Map<URI, Volume> map = new HashMap<>(); Map<URI, Volume> volumes = descriptor.getVolumesToPlace(); int count = RANDOMIZER.nextInt(volumes.size()) + 1; Iterator<URI> iterator = volumes.keySet().iterator(); for (int i = 0; i < count; i++) { URI uri = iterator.next(); map.put(uri, volumes.get(uri)); } return map; } private URI mockURI(String type) { return URI.create(String.format(URI_FORMAT, type, UUID.randomUUID())); } private ExportMaskPlacementDescriptor createDescriptor(int numInitiators, int numVolumes) { URI tenant = mockURI("TenantOrg"); URI project = mockURI("Project"); URI virtualArray = mockURI("VirtualArray"); Host host = mockHost("host.somewhere.com"); StorageSystem vplex = mockStorageSystem("vplex"); StorageSystem array = mockStorageSystem("backend-array"); Set<Initiator> initiators = mockSetOfInitiators(host, numInitiators); Map<URI, Volume> volumes = mockSetOfVolumes(array, numVolumes); return ExportMaskPlacementDescriptor.create(tenant, project, vplex, array, virtualArray, volumes, initiators); } private StorageSystem mockStorageSystem(String label) { StorageSystem storageSystem = new StorageSystem(); storageSystem.setId(mockURI("StorageSystem")); storageSystem.setLabel(label); return storageSystem; } private Volume mockVolume(StorageSystem storageSystem, String label) { Volume volume = new Volume(); volume.setId(mockURI("Volume")); volume.setLabel(label); volume.setStorageController(storageSystem.getId()); volume.setSystemType(storageSystem.getSystemType()); volume.setWWN(UUID.randomUUID().toString().toUpperCase()); return volume; } private Host mockHost(String label) { Host host = new Host(); host.setId(mockURI("Host")); host.setLabel(label); host.setHostName(label); return host; } private Initiator mockInitiator(Host host, String nwwn, String pwwn) { Initiator initiator = new Initiator(); initiator.setId(mockURI("Initiator")); initiator.setHost(host.getId()); initiator.setInitiatorNode(nwwn); initiator.setInitiatorPort(pwwn); return initiator; } private Map<URI, Volume> mockSetOfVolumes(StorageSystem storageSystem, int count) { Map<URI, Volume> map = new HashMap<>(); for (int i = 0; i < count; i++) { Volume volume = mockVolume(storageSystem, String.format("vol%d", i)); map.put(volume.getId(), volume); } return map; } private Set<Initiator> mockSetOfInitiators(Host host, int count) { Set<Initiator> set = new HashSet<>(); for (int i = 0; i < count; i++) { Initiator initiator = mockInitiator(host, "21:22:33:44:55:66:77:00", String.format("11:22:33:44:55:66:77:%02X", i)); set.add(initiator); } return set; } private ExportMaskPolicy mockExportMaskPolicy(String exportType, String igType, String localTierPolicy, String sgName, boolean isSimple, Set<String> tierPolicies, int ioBWLimit, int iopsLimit) { ExportMaskPolicy policy = new ExportMaskPolicy(); policy.setExportType(exportType); policy.setIgType(igType); policy.setLocalTierPolicy(localTierPolicy); policy.setSgName(sgName); policy.setSimpleMask(isSimple); policy.setTierPolicies(new StringSet(tierPolicies)); policy.setHostIOLimitBandwidth(ioBWLimit); policy.setHostIOLimitIOPs(iopsLimit); policy.setMaxVolumesAllowed(4000); return policy; } private ExportMask mockExportMask(String maskName) { ExportMask mask = new ExportMask(); mask.setId(mockURI("ExportMask")); mask.setMaskName(maskName); return mask; } private Map<URI, ExportMask> mockExportMasks(int number) { Map<URI, ExportMask> map = new HashMap<>(); for (int i = 0; i < number; i++) { ExportMask mask = mockExportMask(String.format("mask%d", i)); map.put(mask.getId(), mask); } return map; } }