blob: 22977542fa624a271161cb54a62eb4266d009bbe [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more contributor license
* agreements. See the NOTICE file distributed with this work for additional information regarding
* copyright ownership. The ASF licenses this file to You 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.apache.geode.management.internal.cli.commands;
import static org.apache.geode.cache.PartitionAttributesFactory.GLOBAL_MAX_BUCKETS_DEFAULT;
import static org.apache.geode.cache.Region.SEPARATOR;
import static org.apache.geode.internal.cache.control.SerializableRestoreRedundancyResultsImpl.NO_REDUNDANT_COPIES_FOR_REGIONS;
import static org.apache.geode.internal.cache.control.SerializableRestoreRedundancyResultsImpl.REDUNDANCY_NOT_SATISFIED_FOR_REGIONS;
import static org.apache.geode.internal.cache.control.SerializableRestoreRedundancyResultsImpl.REDUNDANCY_SATISFIED_FOR_REGIONS;
import static org.apache.geode.management.internal.cli.commands.RedundancyCommand.FULLY_SATISFIED_REDUNDANCY;
import static org.apache.geode.management.internal.cli.commands.RedundancyCommand.NO_MEMBERS_FOR_REGION_HEADER;
import static org.apache.geode.management.internal.cli.commands.RedundancyCommand.NO_MEMBERS_FOR_REGION_SECTION;
import static org.apache.geode.management.internal.cli.commands.RedundancyCommand.NO_MEMBERS_HEADER;
import static org.apache.geode.management.internal.cli.commands.RedundancyCommand.NO_MEMBERS_SECTION;
import static org.apache.geode.management.internal.cli.commands.RedundancyCommand.PARTIALLY_SATISFIED_REDUNDANCY;
import static org.apache.geode.management.internal.cli.commands.RedundancyCommand.PRIMARIES_INFO_SECTION;
import static org.apache.geode.management.internal.cli.commands.RedundancyCommand.SATISFIED_REDUNDANCY_SECTION;
import static org.apache.geode.management.internal.cli.commands.RedundancyCommand.SUMMARY_SECTION;
import static org.apache.geode.management.internal.cli.commands.RedundancyCommand.UNDER_REDUNDANCY_SECTION;
import static org.apache.geode.management.internal.cli.commands.RedundancyCommand.ZERO_REDUNDANCY_SECTION;
import static org.apache.geode.management.internal.cli.commands.RedundancyCommand.ZERO_REDUNDANT_COPIES;
import static org.apache.geode.management.internal.i18n.CliStrings.REDUNDANCY_EXCLUDE_REGION;
import static org.apache.geode.management.internal.i18n.CliStrings.REDUNDANCY_INCLUDE_REGION;
import static org.apache.geode.management.internal.i18n.CliStrings.STATUS_REDUNDANCY;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
import java.util.stream.IntStream;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.apache.geode.cache.Region;
import org.apache.geode.cache.RegionShortcut;
import org.apache.geode.internal.cache.InternalCache;
import org.apache.geode.internal.cache.PartitionAttributesImpl;
import org.apache.geode.management.internal.cli.util.CommandStringBuilder;
import org.apache.geode.test.dunit.rules.ClusterStartupRule;
import org.apache.geode.test.dunit.rules.MemberVM;
import org.apache.geode.test.junit.assertions.CommandResultAssert;
import org.apache.geode.test.junit.assertions.InfoResultModelAssert;
import org.apache.geode.test.junit.rules.GfshCommandRule;
public class StatusRedundancyCommandDUnitTest {
@Rule
public ClusterStartupRule cluster = new ClusterStartupRule();
@Rule
public GfshCommandRule gfsh = new GfshCommandRule();
private int locatorPort;
private MemberVM locator;
private List<MemberVM> servers;
private static final int SERVERS_TO_START = 2;
private static final String SATISFIED_REGION = "satisfiedRedundancy";
private static final int SATISFIABLE_COPIES = SERVERS_TO_START - 1;
private static final String UNSATISFIED_REGION = "unsatisfiedRedundancy";
private static final int UNSATISFIABLE_COPIES = SERVERS_TO_START;
private static final String NO_CONFIGURED_REDUNDANCY_REGION = "noConfiguredRedundancy";
private static final String ZERO_COPIES_REGION = "zeroRedundantCopies";
private static final List<String> regionNames = Arrays.asList(SATISFIED_REGION,
UNSATISFIED_REGION, NO_CONFIGURED_REDUNDANCY_REGION, ZERO_COPIES_REGION);
@Before
public void setUp() throws Exception {
servers = new ArrayList<>();
locator = cluster.startLocatorVM(0);
locatorPort = locator.getPort();
IntStream.range(0, SERVERS_TO_START)
.forEach(i -> servers.add(cluster.startServerVM(i + 1, locatorPort)));
gfsh.connectAndVerify(locator);
}
@Test
public void statusRedundancyWithNoArgumentsReturnsStatusForAllRegions() {
createAndPopulateRegions();
String command = new CommandStringBuilder(STATUS_REDUNDANCY).getCommandString();
CommandResultAssert commandResult = gfsh.executeAndAssertThat(command).statusIsSuccess();
List<String> satisfiedRegions =
Arrays.asList(SATISFIED_REGION, NO_CONFIGURED_REDUNDANCY_REGION);
verifyGfshOutput(commandResult, Collections.singletonList(ZERO_COPIES_REGION),
Collections.singletonList(UNSATISFIED_REGION), satisfiedRegions);
}
@Test
public void statusRedundancyWithIncludeRegionArgumentReturnsStatusForOnlyThatRegion() {
createAndPopulateRegions();
String includedRegion = SATISFIED_REGION;
List<String> nonIncludedRegions = new ArrayList<>(regionNames);
nonIncludedRegions.remove(includedRegion);
String command = new CommandStringBuilder(STATUS_REDUNDANCY)
.addOption(REDUNDANCY_INCLUDE_REGION, includedRegion)
.getCommandString();
CommandResultAssert commandResult = gfsh.executeAndAssertThat(command).statusIsSuccess()
.doesNotContainOutput(nonIncludedRegions.toArray(new String[0]));
verifyGfshOutput(commandResult, new ArrayList<>(), new ArrayList<>(),
Collections.singletonList(includedRegion));
}
@Test
public void statusRedundancyWithExcludeRegionArgumentReturnsStatusForAllExceptThatRegion() {
createAndPopulateRegions();
String excludedRegion = ZERO_COPIES_REGION;
String command = new CommandStringBuilder(STATUS_REDUNDANCY)
.addOption(REDUNDANCY_EXCLUDE_REGION, excludedRegion)
.getCommandString();
CommandResultAssert commandResult = gfsh.executeAndAssertThat(command).statusIsSuccess()
.doesNotContainOutput(excludedRegion);
List<String> satisfiedRegions =
Arrays.asList(SATISFIED_REGION, NO_CONFIGURED_REDUNDANCY_REGION);
verifyGfshOutput(commandResult, new ArrayList<>(),
Collections.singletonList(UNSATISFIED_REGION), satisfiedRegions);
}
@Test
public void statusRedundancyWithMatchingIncludeAndExcludeRegionArgumentsReturnsStatusForIncludedRegion() {
createAndPopulateRegions();
String includedAndExcludedRegion = SATISFIED_REGION;
List<String> nonIncludedRegions = new ArrayList<>(regionNames);
nonIncludedRegions.remove(includedAndExcludedRegion);
String command = new CommandStringBuilder(STATUS_REDUNDANCY)
.addOption(REDUNDANCY_INCLUDE_REGION, includedAndExcludedRegion)
.addOption(REDUNDANCY_EXCLUDE_REGION, includedAndExcludedRegion)
.getCommandString();
CommandResultAssert commandResult = gfsh.executeAndAssertThat(command).statusIsSuccess()
.doesNotContainOutput(nonIncludedRegions.toArray(new String[0]));
verifyGfshOutput(commandResult, new ArrayList<>(), new ArrayList<>(),
Collections.singletonList(includedAndExcludedRegion));
}
@Test
public void statusRedundancyWithNoArgumentsReturnsSuccessWhenNoRegionsArePresent() {
String command = new CommandStringBuilder(STATUS_REDUNDANCY).getCommandString();
gfsh.executeAndAssertThat(command).statusIsSuccess().hasInfoSection(NO_MEMBERS_SECTION)
.hasHeader()
.isEqualTo(NO_MEMBERS_HEADER);
}
@Test
public void statusRedundancyWithIncludeRegionReturnsErrorWhenAtLeastOneIncludedRegionIsNotPresent() {
createAndPopulateRegions();
String nonexistentRegion = "fakeRegion";
String includedRegion = SATISFIED_REGION;
List<String> nonIncludedRegions = new ArrayList<>(regionNames);
nonIncludedRegions.remove(includedRegion);
String command = new CommandStringBuilder(STATUS_REDUNDANCY)
.addOption(REDUNDANCY_INCLUDE_REGION, nonexistentRegion + "," + includedRegion)
.getCommandString();
CommandResultAssert commandResult = gfsh.executeAndAssertThat(command).statusIsError()
.doesNotContainOutput(nonIncludedRegions.toArray(new String[0]));
InfoResultModelAssert noMembersForRegion =
commandResult.hasInfoSection(NO_MEMBERS_FOR_REGION_SECTION);
noMembersForRegion.hasHeader().isEqualTo(NO_MEMBERS_FOR_REGION_HEADER);
noMembersForRegion.hasLines().containsExactly(nonexistentRegion);
verifyGfshOutput(commandResult, new ArrayList<>(), new ArrayList<>(),
Collections.singletonList(includedRegion));
}
private void createAndPopulateRegions() {
servers.forEach(s -> s.invoke(() -> {
PartitionAttributesImpl attributes = new PartitionAttributesImpl();
attributes.setStartupRecoveryDelay(-1);
attributes.setRecoveryDelay(-1);
InternalCache cache = Objects.requireNonNull(ClusterStartupRule.getCache());
// Create a region whose redundancy cannot be satisfied due to not enough members
attributes.setRedundantCopies(UNSATISFIABLE_COPIES);
cache.createRegionFactory(RegionShortcut.PARTITION).setPartitionAttributes(attributes)
.create(UNSATISFIED_REGION);
// Create a region whose redundancy can be satisfied
attributes.setRedundantCopies(SATISFIABLE_COPIES);
cache.createRegionFactory(RegionShortcut.PARTITION).setPartitionAttributes(attributes)
.create(SATISFIED_REGION);
// Create a region configured to have zero redundant copies
attributes.setRedundantCopies(0);
cache.createRegionFactory(RegionShortcut.PARTITION).setPartitionAttributes(attributes)
.create(NO_CONFIGURED_REDUNDANCY_REGION);
}));
servers.get(0).invoke(() -> {
// Create a region on only server1 so that it will not be able to create any redundant copies
PartitionAttributesImpl attributes = new PartitionAttributesImpl();
attributes.setStartupRecoveryDelay(-1);
attributes.setRecoveryDelay(-1);
attributes.setRedundantCopies(SATISFIABLE_COPIES);
InternalCache cache = Objects.requireNonNull(ClusterStartupRule.getCache());
cache.createRegionFactory(RegionShortcut.PARTITION).setPartitionAttributes(attributes).create(
ZERO_COPIES_REGION);
// Populate all the regions
regionNames.forEach(regionName -> {
Region<Object, Object> region = cache.getRegion(regionName);
IntStream.range(0, 5 * GLOBAL_MAX_BUCKETS_DEFAULT)
.forEach(i -> region.put("key" + i, "value" + i));
});
});
// Wait for the regions to be ready
regionNames.forEach(region -> {
int expectedServers = region.equals(ZERO_COPIES_REGION) ? 1 : SERVERS_TO_START;
locator.waitUntilRegionIsReadyOnExactlyThisManyServers(SEPARATOR + region, expectedServers);
});
}
private void verifyGfshOutput(CommandResultAssert result, List<String> expectedZeroCopiesRegions,
List<String> expectedPartiallySatisfiedRegions, List<String> expectedFullySatisfiedRegions) {
// Verify summary section
InfoResultModelAssert summary = result.hasInfoSection(SUMMARY_SECTION);
summary.hasOutput().contains(ZERO_REDUNDANT_COPIES + expectedZeroCopiesRegions.size());
summary.hasOutput()
.contains(PARTIALLY_SATISFIED_REDUNDANCY + expectedPartiallySatisfiedRegions.size());
summary.hasOutput().contains(FULLY_SATISFIED_REDUNDANCY + expectedFullySatisfiedRegions.size());
// Verify zero redundancy section
if (!expectedZeroCopiesRegions.isEmpty()) {
InfoResultModelAssert zeroRedundancy = result.hasInfoSection(ZERO_REDUNDANCY_SECTION);
zeroRedundancy.hasHeader().isEqualTo(NO_REDUNDANT_COPIES_FOR_REGIONS);
zeroRedundancy.hasOutput().contains(expectedZeroCopiesRegions);
} else {
result.hasNoSection(ZERO_REDUNDANCY_SECTION);
}
// Verify under redundancy section
if (!expectedPartiallySatisfiedRegions.isEmpty()) {
InfoResultModelAssert zeroRedundancy = result.hasInfoSection(UNDER_REDUNDANCY_SECTION);
zeroRedundancy.hasHeader().isEqualTo(REDUNDANCY_NOT_SATISFIED_FOR_REGIONS);
zeroRedundancy.hasOutput().contains(expectedPartiallySatisfiedRegions);
} else {
result.hasNoSection(UNDER_REDUNDANCY_SECTION);
}
// Verify fully satisfied section
if (!expectedFullySatisfiedRegions.isEmpty()) {
InfoResultModelAssert zeroRedundancy = result.hasInfoSection(SATISFIED_REDUNDANCY_SECTION);
zeroRedundancy.hasHeader().isEqualTo(REDUNDANCY_SATISFIED_FOR_REGIONS);
zeroRedundancy.hasOutput().contains(expectedFullySatisfiedRegions);
} else {
result.hasNoSection(SATISFIED_REDUNDANCY_SECTION);
}
// Verify primaries section is not included
result.hasNoSection(PRIMARIES_INFO_SECTION);
}
}