This is an automated email from the ASF dual-hosted git repository. jasonhuynh pushed a commit to branch support/1.13 in repository https://gitbox.apache.org/repos/asf/geode.git
commit 717b3f4537f3fc5a87e203a3a144cf8e2dabdb60 Author: Joris Melchior <[email protected]> AuthorDate: Fri Jun 19 09:07:46 2020 -0400 GEODE-8283: Provide REST interface for disk-store creation - Provides create, get, list and delete operations - Can create with locator only running Co-Authored-By: Jason Huynh <[email protected]> (cherry picked from commit 180131002cdc93403e7274bd5323a448d6ed93c0) --- .../org/apache/geode/cache/AttributesFactory.java | 4 +- .../geode/cache/configuration/CacheConfig.java | 2 +- .../geode/internal/cache/DiskStoreAttributes.java | 60 +++++ .../geode/internal/cache/DiskStoreFactoryImpl.java | 84 ++---- .../xmlcache/DiskStoreAttributesCreation.java | 25 +- .../api/LocatorClusterManagementService.java | 5 + .../converters/DiskStoreConverter.java | 122 +++++++++ .../configuration/mutators/DiskStoreManager.java | 76 ++++++ .../configuration/realizers/DiskStoreRealizer.java | 120 +++++++++ .../validators/DiskStoreValidator.java | 90 +++++++ .../functions/CacheRealizationFunction.java | 3 + .../converters/DiskStoreConverterTest.java | 120 +++++++++ .../mutators/DiskStoreManagerTest.java | 109 ++++++++ .../realizers/DiskStoreRealizerTest.java | 117 ++++++++ .../validators/DiskStoreValidatorTest.java | 162 +++++++++++ .../geode/management/configuration/DiskDir.java | 54 ++++ .../geode/management/configuration/DiskStore.java | 166 ++++++++++++ .../geode/management/runtime/DiskStoreInfo.java | 25 ++ .../sanctioned-geode-management-serializables.txt | 3 + .../client/CreateDiskStoreDUnitTest.java | 299 +++++++++++++++++++++ .../rest/DiskStoreManagementIntegrationTest.java | 149 ++++++++++ .../rest/controllers/DiskStoreController.java | 113 ++++++++ 22 files changed, 1821 insertions(+), 87 deletions(-) diff --git a/geode-core/src/main/java/org/apache/geode/cache/AttributesFactory.java b/geode-core/src/main/java/org/apache/geode/cache/AttributesFactory.java index 5ee9b09..102cb4e 100644 --- a/geode-core/src/main/java/org/apache/geode/cache/AttributesFactory.java +++ b/geode-core/src/main/java/org/apache/geode/cache/AttributesFactory.java @@ -14,6 +14,8 @@ */ package org.apache.geode.cache; +import static org.apache.geode.internal.cache.DiskStoreAttributes.verifyNonNegativeDirSize; + import java.io.File; import java.util.ArrayList; import java.util.Arrays; @@ -1045,7 +1047,7 @@ public class AttributesFactory<K, V> { new Object[] {Integer.valueOf(diskSizes.length), Integer.valueOf(diskDirs.length)})); } - DiskStoreFactoryImpl.verifyNonNegativeDirSize(diskSizes); + verifyNonNegativeDirSize(diskSizes); this.regionAttributes.diskSizes = diskSizes; if (!this.regionAttributes.hasDiskWriteAttributes() && !this.regionAttributes.hasDiskSynchronous()) { diff --git a/geode-core/src/main/java/org/apache/geode/cache/configuration/CacheConfig.java b/geode-core/src/main/java/org/apache/geode/cache/configuration/CacheConfig.java index aebb836..9a950b0 100644 --- a/geode-core/src/main/java/org/apache/geode/cache/configuration/CacheConfig.java +++ b/geode-core/src/main/java/org/apache/geode/cache/configuration/CacheConfig.java @@ -600,7 +600,7 @@ public class CacheConfig { */ public List<DiskStoreType> getDiskStores() { if (diskStores == null) { - diskStores = new ArrayList<DiskStoreType>(); + diskStores = new ArrayList<>(); } return this.diskStores; } diff --git a/geode-core/src/main/java/org/apache/geode/internal/cache/DiskStoreAttributes.java b/geode-core/src/main/java/org/apache/geode/internal/cache/DiskStoreAttributes.java index 76d71c7..ba62f79 100644 --- a/geode-core/src/main/java/org/apache/geode/internal/cache/DiskStoreAttributes.java +++ b/geode-core/src/main/java/org/apache/geode/internal/cache/DiskStoreAttributes.java @@ -247,4 +247,64 @@ public class DiskStoreAttributes implements Serializable, DiskStore { this.diskDirSizesUnit = DEFAULT_DISK_DIR_SIZES_UNIT; } } + + public static void checkMinAndMaxOplogSize(long maxOplogSize) { + long MAX = Long.MAX_VALUE / (1024 * 1024); + if (maxOplogSize > MAX) { + throw new IllegalArgumentException( + String.format( + "%s has to be a number that does not exceed %s so the value given %s is not acceptable", + new Object[] {"max oplog size", maxOplogSize, MAX})); + } + checkMinOplogSize(maxOplogSize); + } + + public static void checkMinOplogSize(long maxOplogSize) { + if (maxOplogSize < 0) { + throw new IllegalArgumentException( + String.format( + "Maximum Oplog size specified has to be a non-negative number and the value given %s is not acceptable", + maxOplogSize)); + } + } + + public static void checkQueueSize(int queueSize) { + if (queueSize < 0) { + throw new IllegalArgumentException( + String.format( + "Queue size specified has to be a non-negative number and the value given %s is not acceptable", + queueSize)); + } + } + + public static void checkWriteBufferSize(int writeBufferSize) { + if (writeBufferSize < 0) { + throw new IllegalArgumentException( + String.format( + "Write buffer size specified has to be a non-negative number and the value given %s is not acceptable", + writeBufferSize)); + } + } + + /** + * Verify all directory sizes are positive + */ + public static void verifyNonNegativeDirSize(int[] sizes) { + for (int size : sizes) { + if (size < 0) { + throw new IllegalArgumentException( + String.format("Dir size cannot be negative : %s", + size)); + } + } + } + + public static void checkTimeInterval(long timeInterval) { + if (timeInterval < 0) { + throw new IllegalArgumentException( + String.format( + "Time Interval specified has to be a non-negative number and the value given %s is not acceptable", + timeInterval)); + } + } } diff --git a/geode-core/src/main/java/org/apache/geode/internal/cache/DiskStoreFactoryImpl.java b/geode-core/src/main/java/org/apache/geode/internal/cache/DiskStoreFactoryImpl.java index 10ead45..1a889e4 100755 --- a/geode-core/src/main/java/org/apache/geode/internal/cache/DiskStoreFactoryImpl.java +++ b/geode-core/src/main/java/org/apache/geode/internal/cache/DiskStoreFactoryImpl.java @@ -14,6 +14,13 @@ */ package org.apache.geode.internal.cache; +import static org.apache.geode.internal.cache.DiskStoreAttributes.checkMinAndMaxOplogSize; +import static org.apache.geode.internal.cache.DiskStoreAttributes.checkMinOplogSize; +import static org.apache.geode.internal.cache.DiskStoreAttributes.checkQueueSize; +import static org.apache.geode.internal.cache.DiskStoreAttributes.checkTimeInterval; +import static org.apache.geode.internal.cache.DiskStoreAttributes.checkWriteBufferSize; +import static org.apache.geode.internal.cache.DiskStoreAttributes.verifyNonNegativeDirSize; + import java.io.File; import java.util.Arrays; @@ -99,14 +106,12 @@ public class DiskStoreFactoryImpl implements DiskStoreFactory { if (compactionThreshold < 0) { throw new IllegalArgumentException( String.format("%s has to be positive number and the value given %s is not acceptable", - - new Object[] {CacheXml.COMPACTION_THRESHOLD, compactionThreshold})); + CacheXml.COMPACTION_THRESHOLD, compactionThreshold)); } else if (compactionThreshold > 100) { throw new IllegalArgumentException( String.format( "%s has to be a number that does not exceed %s so the value given %s is not acceptable", - - new Object[] {CacheXml.COMPACTION_THRESHOLD, compactionThreshold, 100})); + CacheXml.COMPACTION_THRESHOLD, compactionThreshold, 100)); } this.attrs.compactionThreshold = compactionThreshold; return this; @@ -114,12 +119,7 @@ public class DiskStoreFactoryImpl implements DiskStoreFactory { @Override public DiskStoreFactory setTimeInterval(long timeInterval) { - if (timeInterval < 0) { - throw new IllegalArgumentException( - String.format( - "Time Interval specified has to be a non-negative number and the value given %s is not acceptable", - timeInterval)); - } + checkTimeInterval(timeInterval); this.attrs.timeInterval = timeInterval; return this; } @@ -196,7 +196,7 @@ public class DiskStoreFactoryImpl implements DiskStoreFactory { } private DiskStore findExisting(String name) { - DiskStore existing = null; + DiskStore existing; if (this.cache instanceof GemFireCacheImpl) { existing = this.cache.findDiskStore(name); if (existing != null) { @@ -207,7 +207,7 @@ public class DiskStoreFactoryImpl implements DiskStoreFactory { } } } - return existing; + return null; } @Override @@ -216,7 +216,7 @@ public class DiskStoreFactoryImpl implements DiskStoreFactory { throw new IllegalArgumentException( String.format( "Number of diskSizes is %s which is not equal to number of disk Dirs which is %s", - new Object[] {diskDirSizes.length, diskDirs.length})); + diskDirSizes.length, diskDirs.length)); } verifyNonNegativeDirSize(diskDirSizes); checkIfDirectoriesExist(diskDirs); @@ -232,29 +232,18 @@ public class DiskStoreFactoryImpl implements DiskStoreFactory { * Checks if directories exist, if they don't then create those directories */ public static void checkIfDirectoriesExist(File[] diskDirs) { - for (int i = 0; i < diskDirs.length; i++) { - if (!diskDirs[i].isDirectory()) { - if (!diskDirs[i].mkdirs()) { + for (File diskDir : diskDirs) { + if (!diskDir.isDirectory()) { + if (!diskDir.mkdirs()) { throw new GemFireIOException( String.format("Unable to create directory : %s", - diskDirs[i])); + diskDir)); } } } } - /** - * Verify all directory sizes are positive - */ - public static void verifyNonNegativeDirSize(int[] sizes) { - for (int i = 0; i < sizes.length; i++) { - if (sizes[i] < 0) { - throw new IllegalArgumentException( - String.format("Dir size cannot be negative : %s", - sizes[i])); - } - } - } + @Override public DiskStoreFactory setDiskDirs(File[] diskDirs) { @@ -266,18 +255,7 @@ public class DiskStoreFactoryImpl implements DiskStoreFactory { @Override public DiskStoreFactory setMaxOplogSize(long maxOplogSize) { - long MAX = Long.MAX_VALUE / (1024 * 1024); - if (maxOplogSize > MAX) { - throw new IllegalArgumentException( - String.format( - "%s has to be a number that does not exceed %s so the value given %s is not acceptable", - new Object[] {"max oplog size", maxOplogSize, MAX})); - } else if (maxOplogSize < 0) { - throw new IllegalArgumentException( - String.format( - "Maximum Oplog size specified has to be a non-negative number and the value given %s is not acceptable", - maxOplogSize)); - } + checkMinAndMaxOplogSize(maxOplogSize); this.attrs.maxOplogSizeInBytes = maxOplogSize * (1024 * 1024); return this; } @@ -286,42 +264,26 @@ public class DiskStoreFactoryImpl implements DiskStoreFactory { * Used by unit tests */ public DiskStoreFactory setMaxOplogSizeInBytes(long maxOplogSizeInBytes) { - if (maxOplogSizeInBytes < 0) { - throw new IllegalArgumentException( - String.format( - "Maximum Oplog size specified has to be a non-negative number and the value given %s is not acceptable", - maxOplogSizeInBytes)); - } + checkMinOplogSize(maxOplogSizeInBytes); this.attrs.maxOplogSizeInBytes = maxOplogSizeInBytes; return this; } @Override public DiskStoreFactory setQueueSize(int queueSize) { - if (queueSize < 0) { - throw new IllegalArgumentException( - String.format( - "Queue size specified has to be a non-negative number and the value given %s is not acceptable", - queueSize)); - } + checkQueueSize(queueSize); this.attrs.queueSize = queueSize; return this; } @Override public DiskStoreFactory setWriteBufferSize(int writeBufferSize) { - if (writeBufferSize < 0) { - // TODO add a message for WriteBufferSize - throw new IllegalArgumentException( - String.format( - "Queue size specified has to be a non-negative number and the value given %s is not acceptable", - writeBufferSize)); - } + checkWriteBufferSize(writeBufferSize); this.attrs.writeBufferSize = writeBufferSize; return this; } - // used by hyda + // used by hydra public DiskStoreAttributes getDiskStoreAttributes() { return this.attrs; } diff --git a/geode-core/src/main/java/org/apache/geode/internal/cache/xmlcache/DiskStoreAttributesCreation.java b/geode-core/src/main/java/org/apache/geode/internal/cache/xmlcache/DiskStoreAttributesCreation.java index 3ff4376..da9bc88 100644 --- a/geode-core/src/main/java/org/apache/geode/internal/cache/xmlcache/DiskStoreAttributesCreation.java +++ b/geode-core/src/main/java/org/apache/geode/internal/cache/xmlcache/DiskStoreAttributesCreation.java @@ -292,8 +292,7 @@ public class DiskStoreAttributesCreation extends UserSpecifiedDiskStoreAttribute throw new IllegalArgumentException( String.format( "Number of diskSizes is %s which is not equal to number of disk Dirs which is %s", - - new Object[] {Integer.valueOf(sizes.length), Integer.valueOf(diskDirs.length)})); + sizes.length, diskDirs.length)); } verifyNonNegativeDirSize(sizes); this.diskDirSizes = sizes; @@ -317,28 +316,6 @@ public class DiskStoreAttributesCreation extends UserSpecifiedDiskStoreAttribute * */ private void checkIfDirectoriesExist(File[] disk_dirs) { - // for (int i=0; i < disk_dirs.length; i++) { - // if (! disk_dirs[i].isDirectory()) { - //// throw new - // IllegalArgumentException(String.format("%s was not an existing directory for disk store - // %s.",new - // Object[] {disk_dirs[i], name})); - // if (!diskDirs[i].mkdirs()) { - // throw new RuntimeException("Cannot create directory" + diskDirs[i].getAbsolutePath() + "Num - // disk dirs to be created : " + disk_dirs.length + " Dir Name " + disk_dirs[i].getName()); - // } - // } - // } DiskStoreFactoryImpl.checkIfDirectoriesExist(disk_dirs); } - - private void verifyNonNegativeDirSize(int[] sizes) { - for (int i = 0; i < sizes.length; i++) { - if (sizes[i] < 0) { - throw new IllegalArgumentException( - String.format("Dir size cannot be negative : %s for disk store %s", - new Object[] {Integer.valueOf(sizes[i]), name})); - } - } - } } diff --git a/geode-core/src/main/java/org/apache/geode/management/internal/api/LocatorClusterManagementService.java b/geode-core/src/main/java/org/apache/geode/management/internal/api/LocatorClusterManagementService.java index 55605ac..f20bc36 100644 --- a/geode-core/src/main/java/org/apache/geode/management/internal/api/LocatorClusterManagementService.java +++ b/geode-core/src/main/java/org/apache/geode/management/internal/api/LocatorClusterManagementService.java @@ -68,6 +68,7 @@ import org.apache.geode.management.api.EntityInfo; import org.apache.geode.management.api.RealizationResult; import org.apache.geode.management.configuration.AbstractConfiguration; import org.apache.geode.management.configuration.Deployment; +import org.apache.geode.management.configuration.DiskStore; import org.apache.geode.management.configuration.GatewayReceiver; import org.apache.geode.management.configuration.GroupableConfiguration; import org.apache.geode.management.configuration.HasFile; @@ -83,6 +84,7 @@ import org.apache.geode.management.internal.SystemManagementService; import org.apache.geode.management.internal.configuration.mutators.CacheConfigurationManager; import org.apache.geode.management.internal.configuration.mutators.ConfigurationManager; import org.apache.geode.management.internal.configuration.mutators.DeploymentManager; +import org.apache.geode.management.internal.configuration.mutators.DiskStoreManager; import org.apache.geode.management.internal.configuration.mutators.GatewayReceiverConfigManager; import org.apache.geode.management.internal.configuration.mutators.IndexConfigManager; import org.apache.geode.management.internal.configuration.mutators.PdxManager; @@ -90,6 +92,7 @@ import org.apache.geode.management.internal.configuration.mutators.RegionConfigM import org.apache.geode.management.internal.configuration.validators.CommonConfigurationValidator; import org.apache.geode.management.internal.configuration.validators.ConfigurationValidator; import org.apache.geode.management.internal.configuration.validators.DeploymentValidator; +import org.apache.geode.management.internal.configuration.validators.DiskStoreValidator; import org.apache.geode.management.internal.configuration.validators.GatewayReceiverConfigValidator; import org.apache.geode.management.internal.configuration.validators.IndexValidator; import org.apache.geode.management.internal.configuration.validators.MemberValidator; @@ -126,6 +129,7 @@ public class LocatorClusterManagementService implements ClusterManagementService managers.put(GatewayReceiver.class, new GatewayReceiverConfigManager(persistenceService)); managers.put(Index.class, new IndexConfigManager(persistenceService)); managers.put(Deployment.class, new DeploymentManager(persistenceService)); + managers.put(DiskStore.class, new DiskStoreManager(persistenceService)); // initialize the list of validators validators.put(Region.class, new RegionConfigValidator(cache)); @@ -133,6 +137,7 @@ public class LocatorClusterManagementService implements ClusterManagementService validators.put(Pdx.class, new PdxValidator()); validators.put(Index.class, new IndexValidator()); validators.put(Deployment.class, new DeploymentValidator()); + validators.put(DiskStore.class, new DiskStoreValidator()); } @VisibleForTesting diff --git a/geode-core/src/main/java/org/apache/geode/management/internal/configuration/converters/DiskStoreConverter.java b/geode-core/src/main/java/org/apache/geode/management/internal/configuration/converters/DiskStoreConverter.java new file mode 100644 index 0000000..d32f33a --- /dev/null +++ b/geode-core/src/main/java/org/apache/geode/management/internal/configuration/converters/DiskStoreConverter.java @@ -0,0 +1,122 @@ +/* + * 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.configuration.converters; + +import java.util.stream.Collectors; + +import org.apache.geode.cache.configuration.DiskDirType; +import org.apache.geode.cache.configuration.DiskStoreType; +import org.apache.geode.management.configuration.DiskDir; +import org.apache.geode.management.configuration.DiskStore; + +public class DiskStoreConverter extends ConfigurationConverter<DiskStore, DiskStoreType> { + @Override + protected DiskStore fromNonNullXmlObject(DiskStoreType xmlObject) { + DiskStore diskStore = new DiskStore(); + + diskStore.setName(xmlObject.getName()); + if (xmlObject.isAllowForceCompaction() != null) { + diskStore.setAllowForceCompaction(xmlObject.isAllowForceCompaction()); + } + if (xmlObject.isAutoCompact() != null) { + diskStore.setAutoCompact(xmlObject.isAutoCompact()); + } + + if (xmlObject.getCompactionThreshold() != null) { + diskStore.setCompactionThreshold(Integer.parseInt(xmlObject.getCompactionThreshold())); + } + if (xmlObject.getDiskUsageCriticalPercentage() != null) { + diskStore.setDiskUsageCriticalPercentage( + Float.parseFloat(xmlObject.getDiskUsageCriticalPercentage())); + } + if (xmlObject.getDiskUsageWarningPercentage() != null) { + diskStore + .setDiskUsageWarningPercentage( + Float.parseFloat(xmlObject.getDiskUsageWarningPercentage())); + } + if (xmlObject.getMaxOplogSize() != null) { + diskStore.setMaxOplogSizeInBytes(Long.parseLong(xmlObject.getMaxOplogSize())); + } + + if (xmlObject.getQueueSize() != null) { + diskStore.setQueueSize(Integer.parseInt(xmlObject.getQueueSize())); + } + if (xmlObject.getTimeInterval() != null) { + diskStore.setTimeInterval(Long.parseLong(xmlObject.getTimeInterval())); + } + if (xmlObject.getWriteBufferSize() != null) { + diskStore.setWriteBufferSize(Integer.parseInt(xmlObject.getWriteBufferSize())); + } + + + diskStore.setDirectories(xmlObject.getDiskDirs().stream().map(diskDirType -> { + DiskDir diskDir = new DiskDir(); + diskDir.setDirSize(diskDirType.getDirSize()); + diskDir.setName(diskDirType.getContent()); + return diskDir; + }).collect(Collectors.toList())); + + return diskStore; + } + + @Override + protected DiskStoreType fromNonNullConfigObject(DiskStore configObject) { + DiskStoreType diskStoreType = new DiskStoreType(); + + diskStoreType.setName(configObject.getName()); + if (configObject.isAllowForceCompaction() != null) { + diskStoreType.setAllowForceCompaction(configObject.isAllowForceCompaction()); + } + if (configObject.isAutoCompact() != null) { + diskStoreType.setAutoCompact(configObject.isAutoCompact()); + } + if (configObject.getCompactionThreshold() != null) { + diskStoreType.setCompactionThreshold(configObject.getCompactionThreshold().toString()); + } + if (configObject.getDiskUsageCriticalPercentage() != null) { + diskStoreType + .setDiskUsageCriticalPercentage(configObject.getDiskUsageCriticalPercentage().toString()); + } + if (configObject.getDiskUsageWarningPercentage() != null) { + diskStoreType + .setDiskUsageWarningPercentage(configObject.getDiskUsageWarningPercentage().toString()); + } + if (configObject.getMaxOplogSizeInBytes() != null) { + diskStoreType.setMaxOplogSize(configObject.getMaxOplogSizeInBytes().toString()); + } + if (configObject.getQueueSize() != null) { + diskStoreType.setQueueSize(configObject.getQueueSize().toString()); + } + if (configObject.getTimeInterval() != null) { + diskStoreType.setTimeInterval(configObject.getTimeInterval().toString()); + } + if (configObject.getWriteBufferSize() != null) { + diskStoreType.setWriteBufferSize(configObject.getWriteBufferSize().toString()); + } + diskStoreType.setDiskDirs(configObject.getDirectories().stream().map(diskDir -> { + DiskDirType diskDirType = new DiskDirType(); + diskDirType.setContent(diskDir.getName()); + diskDirType.setDirSize(diskDir.getDirSize()); + return diskDirType; + }).collect(Collectors.toList())); + + return diskStoreType; + } +} diff --git a/geode-core/src/main/java/org/apache/geode/management/internal/configuration/mutators/DiskStoreManager.java b/geode-core/src/main/java/org/apache/geode/management/internal/configuration/mutators/DiskStoreManager.java new file mode 100644 index 0000000..43e258e --- /dev/null +++ b/geode-core/src/main/java/org/apache/geode/management/internal/configuration/mutators/DiskStoreManager.java @@ -0,0 +1,76 @@ +/* + * 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.configuration.mutators; + +import java.util.List; +import java.util.stream.Collectors; + +import org.apache.commons.lang3.StringUtils; + +import org.apache.geode.cache.configuration.CacheConfig; +import org.apache.geode.cache.configuration.DiskStoreType; +import org.apache.geode.distributed.ConfigurationPersistenceService; +import org.apache.geode.management.configuration.DiskStore; +import org.apache.geode.management.internal.configuration.converters.DiskStoreConverter; + +public class DiskStoreManager extends CacheConfigurationManager<DiskStore> { + private final DiskStoreConverter diskStoreConverter = new DiskStoreConverter(); + + public DiskStoreManager(ConfigurationPersistenceService service) { + super(service); + } + + @Override + public void add(DiskStore config, CacheConfig existing) { + List<DiskStoreType> diskStoreTypes = existing.getDiskStores(); + if (diskStoreTypes.stream().noneMatch(diskStoreType -> diskStoreType.getName() + .equals(config.getName()))) { + diskStoreTypes.add(diskStoreConverter.fromConfigObject(config)); + } + } + + @Override + public void update(DiskStore config, CacheConfig existing) { + throw new IllegalStateException("Not implemented"); + } + + @Override + public void delete(DiskStore config, CacheConfig existing) { + existing.getDiskStores().stream() + .filter(diskStoreType -> config.getName().equals(diskStoreType.getName())).findFirst() + .ifPresent(diskStore -> existing.getDiskStores().remove(diskStore)); + } + + @Override + public List<DiskStore> list(DiskStore filterConfig, CacheConfig existing) { + return existing.getDiskStores().stream() + .filter(diskStoreType -> StringUtils.isEmpty(filterConfig.getName()) + || filterConfig.getName().equals(diskStoreType.getName())) + .map(diskStoreConverter::fromXmlObject).collect(Collectors.toList()); + } + + @Override + public DiskStore get(DiskStore config, CacheConfig existing) { + return existing.getDiskStores().stream() + .filter(diskStoreType -> diskStoreType.getName().equals(config.getName())).map( + diskStoreConverter::fromXmlObject) + .findFirst().orElse(null); + } +} diff --git a/geode-core/src/main/java/org/apache/geode/management/internal/configuration/realizers/DiskStoreRealizer.java b/geode-core/src/main/java/org/apache/geode/management/internal/configuration/realizers/DiskStoreRealizer.java new file mode 100644 index 0000000..cdc397a --- /dev/null +++ b/geode-core/src/main/java/org/apache/geode/management/internal/configuration/realizers/DiskStoreRealizer.java @@ -0,0 +1,120 @@ +/* + * 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.configuration.realizers; + +import java.io.File; +import java.util.List; +import java.util.stream.Collectors; + +import org.apache.commons.lang3.StringUtils; + +import org.apache.geode.cache.DiskStoreFactory; +import org.apache.geode.internal.cache.DiskStoreAttributes; +import org.apache.geode.internal.cache.InternalCache; +import org.apache.geode.management.api.RealizationResult; +import org.apache.geode.management.configuration.DiskStore; +import org.apache.geode.management.runtime.DiskStoreInfo; + +public class DiskStoreRealizer implements ConfigurationRealizer<DiskStore, DiskStoreInfo> { + @Override + public RealizationResult create(DiskStore config, InternalCache cache) throws Exception { + DiskStoreAttributes diskStoreAttributes = new DiskStoreAttributes(); + + if (config.isAllowForceCompaction() != null) { + diskStoreAttributes.allowForceCompaction = config.isAllowForceCompaction(); + } + if (config.isAutoCompact() != null) { + diskStoreAttributes.autoCompact = config.isAutoCompact(); + } + if (config.getCompactionThreshold() != null) { + diskStoreAttributes.compactionThreshold = config.getCompactionThreshold(); + } + if (config.getDiskUsageCriticalPercentage() != null) { + diskStoreAttributes.setDiskUsageCriticalPercentage(config.getDiskUsageCriticalPercentage()); + } + if (config.getDiskUsageWarningPercentage() != null) { + diskStoreAttributes.setDiskUsageWarningPercentage(config.getDiskUsageWarningPercentage()); + } + if (config.getMaxOplogSizeInBytes() != null) { + diskStoreAttributes.maxOplogSizeInBytes = config.getMaxOplogSizeInBytes(); + } + if (config.getQueueSize() != null) { + diskStoreAttributes.queueSize = config.getQueueSize(); + } + if (config.getTimeInterval() != null) { + diskStoreAttributes.timeInterval = config.getTimeInterval(); + } + if (config.getWriteBufferSize() != null) { + diskStoreAttributes.writeBufferSize = config.getWriteBufferSize(); + } + + List<File> fileList = + config.getDirectories().stream().map(diskDir -> new File(diskDir.getName())) + .collect(Collectors.toList()); + diskStoreAttributes.diskDirs = fileList.toArray(diskStoreAttributes.diskDirs); + diskStoreAttributes.diskDirSizes = config.getDirectories().stream().mapToInt(diskDir -> { + if (StringUtils.isEmpty(diskDir.getDirSize())) { + return Integer.MAX_VALUE; + } else { + return Integer.parseInt(diskDir.getDirSize()); + } + }).toArray(); + + DiskStoreFactory diskStoreFactory = cache.createDiskStoreFactory(diskStoreAttributes); + diskStoreFactory.create(config.getName()); + + return new RealizationResult() + .setMessage("DiskStore " + config.getName() + " created successfully."); + } + + @Override + public boolean exists(DiskStore config, InternalCache cache) { + return cache.listDiskStores().stream() + .anyMatch(diskStore -> diskStore.getName().equals(config.getName())); + } + + @Override + public DiskStoreInfo get(DiskStore config, InternalCache cache) { + return new DiskStoreInfo(); + } + + @Override + public RealizationResult update(DiskStore config, InternalCache cache) throws Exception { + return null; + } + + @Override + public RealizationResult delete(DiskStore config, InternalCache cache) throws Exception { + org.apache.geode.cache.DiskStore diskStore = cache.findDiskStore(config.getName()); + if (diskStore != null) { + diskStore.destroy(); + return new RealizationResult() + .setMessage("DiskStore " + config.getName() + " deleted successfully."); + } else { + return new RealizationResult().setMessage("DiskStore " + config.getName() + " not found.") + .setSuccess(false); + } + } + + @Override + public boolean isReadyOnly() { + return false; + } +} diff --git a/geode-core/src/main/java/org/apache/geode/management/internal/configuration/validators/DiskStoreValidator.java b/geode-core/src/main/java/org/apache/geode/management/internal/configuration/validators/DiskStoreValidator.java new file mode 100644 index 0000000..d239e7d --- /dev/null +++ b/geode-core/src/main/java/org/apache/geode/management/internal/configuration/validators/DiskStoreValidator.java @@ -0,0 +1,90 @@ +/* + * 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.configuration.validators; + +import static org.apache.geode.internal.cache.DiskStoreAttributes.checkMinOplogSize; +import static org.apache.geode.internal.cache.DiskStoreAttributes.checkQueueSize; +import static org.apache.geode.internal.cache.DiskStoreAttributes.checkWriteBufferSize; +import static org.apache.geode.internal.cache.DiskStoreAttributes.verifyNonNegativeDirSize; + +import org.apache.commons.lang3.StringUtils; + +import org.apache.geode.internal.cache.DiskStoreMonitor; +import org.apache.geode.management.configuration.DiskStore; +import org.apache.geode.management.internal.CacheElementOperation; + +public class DiskStoreValidator implements ConfigurationValidator<DiskStore> { + @Override + public void validate(CacheElementOperation operation, DiskStore config) + throws IllegalArgumentException { + switch (operation) { + case CREATE: + case UPDATE: + checkRequiredItems(config); + checkValueRanges(config); + } + } + + private void checkValueRanges(DiskStore config) { + if (config.getDiskUsageCriticalPercentage() != null) { + DiskStoreMonitor.checkCritical(config.getDiskUsageCriticalPercentage()); + } + if (config.getDiskUsageWarningPercentage() != null) { + DiskStoreMonitor.checkWarning(config.getDiskUsageWarningPercentage()); + } + if (config.getCompactionThreshold() != null) { + if (0 > config.getCompactionThreshold() || config.getCompactionThreshold() > 100) { + throw new IllegalArgumentException( + "CompactionThreshold has to be set to a value between 0-100."); + } + } + if (config.getMaxOplogSizeInBytes() != null) { + checkMinOplogSize(config.getMaxOplogSizeInBytes()); + } + if (config.getQueueSize() != null) { + checkQueueSize(config.getQueueSize()); + } + if (config.getWriteBufferSize() != null) { + checkWriteBufferSize(config.getWriteBufferSize()); + } + verifyNonNegativeDirSize(config.getDirectories().stream().mapToInt(diskDir -> { + if (StringUtils.isEmpty(diskDir.getDirSize())) { + return Integer.MAX_VALUE; + } else { + return Integer.parseInt(diskDir.getDirSize()); + } + }).toArray()); + } + + private void checkRequiredItems(DiskStore config) { + if (StringUtils.isEmpty(config.getName())) { + throw new IllegalArgumentException("Diskstore name is required."); + } + if (config.getDirectories() != null && config.getDirectories().size() > 0) { + if (config.getDirectories() + .stream() + .anyMatch(diskDir -> StringUtils.isEmpty(diskDir.getName()))) { + throw new IllegalArgumentException("Diskdir name is required."); + } + } else { + throw new IllegalArgumentException("At least one DiskDir element required."); + } + } +} diff --git a/geode-core/src/main/java/org/apache/geode/management/internal/functions/CacheRealizationFunction.java b/geode-core/src/main/java/org/apache/geode/management/internal/functions/CacheRealizationFunction.java index cfa56fc..c82ad90 100644 --- a/geode-core/src/main/java/org/apache/geode/management/internal/functions/CacheRealizationFunction.java +++ b/geode-core/src/main/java/org/apache/geode/management/internal/functions/CacheRealizationFunction.java @@ -43,6 +43,7 @@ import org.apache.geode.logging.internal.log4j.api.LogService; import org.apache.geode.management.api.RealizationResult; import org.apache.geode.management.configuration.AbstractConfiguration; import org.apache.geode.management.configuration.Deployment; +import org.apache.geode.management.configuration.DiskStore; import org.apache.geode.management.configuration.GatewayReceiver; import org.apache.geode.management.configuration.HasFile; import org.apache.geode.management.configuration.Index; @@ -53,6 +54,7 @@ import org.apache.geode.management.internal.CacheElementOperation; import org.apache.geode.management.internal.beans.FileUploader; import org.apache.geode.management.internal.configuration.realizers.ConfigurationRealizer; import org.apache.geode.management.internal.configuration.realizers.DeploymentRealizer; +import org.apache.geode.management.internal.configuration.realizers.DiskStoreRealizer; import org.apache.geode.management.internal.configuration.realizers.GatewayReceiverRealizer; import org.apache.geode.management.internal.configuration.realizers.IndexRealizer; import org.apache.geode.management.internal.configuration.realizers.MemberRealizer; @@ -72,6 +74,7 @@ public class CacheRealizationFunction implements InternalFunction<List> { realizers.put(Pdx.class, new PdxRealizer()); realizers.put(Deployment.class, new DeploymentRealizer()); realizers.put(Index.class, new IndexRealizer()); + realizers.put(DiskStore.class, new DiskStoreRealizer()); } @Override diff --git a/geode-core/src/test/java/org/apache/geode/management/internal/configuration/converters/DiskStoreConverterTest.java b/geode-core/src/test/java/org/apache/geode/management/internal/configuration/converters/DiskStoreConverterTest.java new file mode 100644 index 0000000..44d5eec --- /dev/null +++ b/geode-core/src/test/java/org/apache/geode/management/internal/configuration/converters/DiskStoreConverterTest.java @@ -0,0 +1,120 @@ +/* + * 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.configuration.converters; + +import static org.assertj.core.api.SoftAssertions.assertSoftly; + +import java.util.ArrayList; + +import org.junit.Test; + +import org.apache.geode.cache.configuration.DiskDirType; +import org.apache.geode.cache.configuration.DiskStoreType; +import org.apache.geode.management.configuration.DiskDir; +import org.apache.geode.management.configuration.DiskStore; + +public class DiskStoreConverterTest { + private final DiskStoreConverter diskStoreConverter = new DiskStoreConverter(); + + @Test + public void fromNonNullConfigObjectCopiesPropertiesCorrectly() { + DiskStore config = new DiskStore(); + config.setName("name"); + config.setAllowForceCompaction(false); + config.setAutoCompact(false); + config.setCompactionThreshold(50); + config.setDiskUsageCriticalPercentage(80F); + config.setDiskUsageWarningPercentage(70F); + config.setMaxOplogSizeInBytes(10L); + config.setQueueSize(5); + config.setTimeInterval(1L); + config.setWriteBufferSize(1); + + ArrayList<DiskDir> directories = new ArrayList<>(); + directories.add(new DiskDir("directoryName", "1")); + config.setDirectories(directories); + + DiskStoreType diskStoreType = diskStoreConverter.fromNonNullConfigObject(config); + + assertSoftly(softly -> { + softly.assertThat(diskStoreType.isAllowForceCompaction()) + .isEqualTo(config.isAllowForceCompaction()); + softly.assertThat(diskStoreType.isAutoCompact()) + .isEqualTo(config.isAutoCompact()); + softly.assertThat(diskStoreType.getCompactionThreshold()) + .isEqualTo(config.getCompactionThreshold().toString()); + softly.assertThat(diskStoreType.getDiskUsageCriticalPercentage()) + .isEqualTo(config.getDiskUsageCriticalPercentage().toString()); + softly.assertThat(diskStoreType.getDiskUsageWarningPercentage()) + .isEqualTo(config.getDiskUsageWarningPercentage().toString()); + softly.assertThat(diskStoreType.getMaxOplogSize()) + .isEqualTo(config.getMaxOplogSizeInBytes().toString()); + softly.assertThat(diskStoreType.getQueueSize()) + .isEqualTo(config.getQueueSize().toString()); + softly.assertThat(diskStoreType.getTimeInterval()) + .isEqualTo(config.getTimeInterval().toString()); + softly.assertThat(diskStoreType.getWriteBufferSize()) + .isEqualTo(config.getWriteBufferSize().toString()); + softly.assertThat(diskStoreType.getDiskDirs().size()) + .isEqualTo(config.getDirectories().size()); + + }); + } + + @Test + public void fromNonNullXmlObjectCopiesPropertiesCorrectly() { + DiskStoreType diskStoreType = new DiskStoreType(); + diskStoreType.setName("name"); + diskStoreType.setAllowForceCompaction(false); + diskStoreType.setAutoCompact(false); + diskStoreType.setCompactionThreshold("50"); + diskStoreType.setDiskUsageCriticalPercentage("80"); + diskStoreType.setDiskUsageWarningPercentage("70"); + diskStoreType.setMaxOplogSize("10"); + diskStoreType.setQueueSize("5"); + diskStoreType.setTimeInterval("1"); + diskStoreType.setWriteBufferSize("1"); + + ArrayList<DiskDirType> diskDirs = new ArrayList<>(); + diskDirs.add(new DiskDirType()); + diskStoreType.setDiskDirs(diskDirs); + + DiskStore config = diskStoreConverter.fromNonNullXmlObject(diskStoreType); + + assertSoftly(softly -> { + softly.assertThat(config.isAllowForceCompaction()) + .isEqualTo(diskStoreType.isAllowForceCompaction()); + softly.assertThat(config.isAutoCompact()) + .isEqualTo(diskStoreType.isAutoCompact()); + softly.assertThat(config.getCompactionThreshold().toString()) + .isEqualTo(diskStoreType.getCompactionThreshold()); + softly.assertThat(config.getDiskUsageCriticalPercentage().toString()) + .contains(diskStoreType.getDiskUsageCriticalPercentage()); + softly.assertThat(config.getDiskUsageWarningPercentage().toString()) + .contains(diskStoreType.getDiskUsageWarningPercentage()); + softly.assertThat(config.getMaxOplogSizeInBytes().toString()) + .isEqualTo(diskStoreType.getMaxOplogSize()); + softly.assertThat(config.getQueueSize().toString()) + .isEqualTo(diskStoreType.getQueueSize()); + softly.assertThat(config.getTimeInterval().toString()) + .isEqualTo(diskStoreType.getTimeInterval()); + softly.assertThat(config.getWriteBufferSize().toString()) + .isEqualTo(diskStoreType.getWriteBufferSize()); + softly.assertThat(config.getDirectories().size()) + .isEqualTo(diskStoreType.getDiskDirs().size()); + }); + } +} diff --git a/geode-core/src/test/java/org/apache/geode/management/internal/configuration/mutators/DiskStoreManagerTest.java b/geode-core/src/test/java/org/apache/geode/management/internal/configuration/mutators/DiskStoreManagerTest.java new file mode 100644 index 0000000..08cf610 --- /dev/null +++ b/geode-core/src/test/java/org/apache/geode/management/internal/configuration/mutators/DiskStoreManagerTest.java @@ -0,0 +1,109 @@ +/* + * 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.configuration.mutators; + +import static org.assertj.core.api.Assertions.assertThat; + +import java.util.ArrayList; +import java.util.List; + +import org.junit.Before; +import org.junit.Test; + +import org.apache.geode.cache.configuration.CacheConfig; +import org.apache.geode.cache.configuration.DiskStoreType; +import org.apache.geode.management.configuration.DiskStore; + +public class DiskStoreManagerTest { + + private CacheConfig cacheConfig; + private DiskStore diskStore; + private DiskStoreManager manager; + + @Before + public void before() throws Exception { + cacheConfig = new CacheConfig(); + diskStore = new DiskStore(); + diskStore.setName("diskStoreName"); + diskStore.setDirectories(new ArrayList()); + manager = new DiskStoreManager(null); + } + + @Test + public void addResultsInDiskStoreTypeAddedToCacheConfig() { + manager.add(diskStore, cacheConfig); + assertThat(cacheConfig.getDiskStores().size()).isEqualTo(1); + } + + @Test + public void deleteWhenDiskStoreTypeDoesNotExistShouldNotFail() { + manager.delete(diskStore, cacheConfig); + assertThat(cacheConfig.getDiskStores().size()).isEqualTo(0); + } + + @Test + public void deleteWhenDiskStoreExistsShouldSucceed() { + DiskStoreType diskStoreType = new DiskStoreType(); + diskStoreType.setName(diskStore.getName()); + cacheConfig.getDiskStores().add(diskStoreType); + manager.delete(diskStore, cacheConfig); + assertThat(cacheConfig.getDiskStores().size()).isEqualTo(0); + } + + @Test + public void unableToAddDuplicatesToCacheConfig() { + manager.add(diskStore, cacheConfig); + manager.add(diskStore, cacheConfig); + assertThat(cacheConfig.getDiskStores().size()).isEqualTo(1); + } + + @Test + public void listShouldIncludeAllKnownDiskStores() { + DiskStoreType diskStoreType = new DiskStoreType(); + diskStoreType.setName(diskStore.getName()); + cacheConfig.getDiskStores().add(diskStoreType); + List<DiskStore> diskStores = manager.list(diskStore, cacheConfig); + assertThat(diskStores.size()).isEqualTo(1); + assertThat(diskStores.contains(diskStoreType)); + } + + @Test + public void getShouldReturnDiskStoreMatchingByName() { + DiskStoreType diskStoreType = new DiskStoreType(); + diskStoreType.setName(diskStore.getName()); + cacheConfig.getDiskStores().add(diskStoreType); + DiskStore foundDiskStore = manager.get(diskStore, cacheConfig); + assertThat(foundDiskStore).isNotNull(); + assertThat(foundDiskStore.getName()).isEqualTo(diskStoreType.getName()); + } + + @Test + public void getShouldReturnNullIfNoDiskStoresExist() { + DiskStoreType diskStoreType = new DiskStoreType(); + diskStoreType.setName(diskStore.getName()); + DiskStore foundDiskStore = manager.get(diskStore, cacheConfig); + assertThat(foundDiskStore).isNull(); + } + + @Test + public void getShouldReturnNullIfDiskStoreDoesNotMatch() { + DiskStoreType diskStoreType = new DiskStoreType(); + diskStoreType.setName("notTheDiskStoreYouAreLookingFor"); + cacheConfig.getDiskStores().add(diskStoreType); + DiskStore foundDiskStore = manager.get(diskStore, cacheConfig); + assertThat(foundDiskStore).isNull(); + } +} diff --git a/geode-core/src/test/java/org/apache/geode/management/internal/configuration/realizers/DiskStoreRealizerTest.java b/geode-core/src/test/java/org/apache/geode/management/internal/configuration/realizers/DiskStoreRealizerTest.java new file mode 100644 index 0000000..59bf4ea --- /dev/null +++ b/geode-core/src/test/java/org/apache/geode/management/internal/configuration/realizers/DiskStoreRealizerTest.java @@ -0,0 +1,117 @@ +/* + * 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.configuration.realizers; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import java.util.ArrayList; +import java.util.Collection; + +import org.junit.Before; +import org.junit.Test; + +import org.apache.geode.cache.DiskStoreFactory; +import org.apache.geode.internal.cache.InternalCache; +import org.apache.geode.management.api.RealizationResult; +import org.apache.geode.management.configuration.DiskStore; + +public class DiskStoreRealizerTest { + InternalCache cache; + DiskStoreRealizer diskStoreRealizer; + DiskStoreFactory diskStoreFactory; + + @Before + public void init() { + cache = mock(InternalCache.class); + diskStoreFactory = mock(DiskStoreFactory.class); + when(cache.createDiskStoreFactory(any())).thenReturn(diskStoreFactory); + diskStoreRealizer = new DiskStoreRealizer(); + } + + + @Test + public void creatingDiskStoreWithNameShouldSucceed() throws Exception { + DiskStore config = new DiskStore(); + String name = "diskStoreName"; + config.setName(name); + config.setDirectories(new ArrayList()); + RealizationResult result = diskStoreRealizer.create(config, cache); + verify(diskStoreFactory, times(1)).create(name); + assertThat(result.getMessage()) + .isEqualTo("DiskStore " + config.getName() + " created successfully."); + assertThat(result.isSuccess()).isTrue(); + } + + @Test + public void existsReturnsTrueIfADiskStoreMatchesName() { + DiskStore config = new DiskStore(); + String name = "diskStoreName"; + Collection<org.apache.geode.cache.DiskStore> diskStores = new ArrayList(); + org.apache.geode.cache.DiskStore existingDiskStore = + mock(org.apache.geode.cache.DiskStore.class); + when(existingDiskStore.getName()).thenReturn(name); + diskStores.add(existingDiskStore); + when(cache.listDiskStores()).thenReturn(diskStores); + + config.setName(name); + assertThat(diskStoreRealizer.exists(config, cache)).isTrue(); + } + + @Test + public void existsReturnsFalseIfADiskStoreMatchesName() { + DiskStore config = new DiskStore(); + String name = "diskStoreName"; + Collection<org.apache.geode.cache.DiskStore> diskStores = new ArrayList(); + org.apache.geode.cache.DiskStore existingDiskStore = + mock(org.apache.geode.cache.DiskStore.class); + when(existingDiskStore.getName()).thenReturn("notTheDiskStoreYouAreLookingFor"); + diskStores.add(existingDiskStore); + when(cache.listDiskStores()).thenReturn(diskStores); + + config.setName(name); + assertThat(diskStoreRealizer.exists(config, cache)).isFalse(); + } + + @Test + public void deletingDiskStoreShouldFailIfDiskStoreNotFound() throws Exception { + DiskStore config = new DiskStore(); + String name = "diskStoreName"; + config.setName(name); + config.setDirectories(new ArrayList()); + RealizationResult result = diskStoreRealizer.delete(config, cache); + assertThat(result.getMessage()).isEqualTo("DiskStore " + config.getName() + " not found."); + assertThat(result.isSuccess()).isFalse(); + } + + @Test + public void deletingDiskStoreShouldSucceedIfDiskStoreFound() throws Exception { + DiskStore config = new DiskStore(); + config.setDirectories(new ArrayList()); + String name = "diskStoreName"; + config.setName(name); + when(cache.findDiskStore(name)).thenReturn(mock(org.apache.geode.cache.DiskStore.class)); + RealizationResult result = diskStoreRealizer.delete(config, cache); + assertThat(result.getMessage()) + .isEqualTo("DiskStore " + config.getName() + " deleted successfully."); + assertThat(result.isSuccess()).isTrue(); + } + +} diff --git a/geode-core/src/test/java/org/apache/geode/management/internal/configuration/validators/DiskStoreValidatorTest.java b/geode-core/src/test/java/org/apache/geode/management/internal/configuration/validators/DiskStoreValidatorTest.java new file mode 100644 index 0000000..c57fb6a --- /dev/null +++ b/geode-core/src/test/java/org/apache/geode/management/internal/configuration/validators/DiskStoreValidatorTest.java @@ -0,0 +1,162 @@ +/* + * 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.configuration.validators; + +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +import java.util.Collections; + +import org.junit.Before; +import org.junit.Test; + +import org.apache.geode.management.configuration.DiskDir; +import org.apache.geode.management.configuration.DiskStore; +import org.apache.geode.management.internal.CacheElementOperation; + +public class DiskStoreValidatorTest { + private final DiskStoreValidator diskStoreValidator = new DiskStoreValidator(); + private DiskStore diskStore; + private DiskDir diskDir; + + @Before + public void init() { + diskStore = new DiskStore(); + String storeName = "diskstore"; + diskStore.setName(storeName); + diskDir = new DiskDir(); + String dirName = "diskdir"; + diskDir.setName(dirName); + int dirSizeInteger = 1024; + diskDir.setDirSize(Integer.toString(dirSizeInteger)); + diskStore.setDirectories(Collections.singletonList(diskDir)); + } + + @Test + public void diskStoreNameIsRequired() { + diskStoreValidator.validate(CacheElementOperation.CREATE, diskStore); + + diskStore.setName(null); + assertThatThrownBy(() -> diskStoreValidator.validate(CacheElementOperation.CREATE, diskStore)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessageContaining("Diskstore name is required"); + } + + @Test + public void atLeastOneDirectoryIsDefined() { + diskStoreValidator.validate(CacheElementOperation.CREATE, diskStore); + + diskStore.setDirectories(null); + assertThatThrownBy(() -> diskStoreValidator.validate(CacheElementOperation.CREATE, diskStore)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessageContaining("At least one DiskDir element required"); + } + + @Test + public void diskUsageCriticalPercentageMustBeBetweenZeroAndOneHundred() { + diskStore.setDiskUsageCriticalPercentage(95F); + diskStoreValidator.validate(CacheElementOperation.CREATE, diskStore); + + diskStore.setDiskUsageCriticalPercentage(-1F); + assertThatThrownBy(() -> diskStoreValidator.validate(CacheElementOperation.CREATE, diskStore)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessageContaining("Disk usage critical percentage must be set to a value between 0-100"); + + diskStore.setDiskUsageCriticalPercentage(101F); + assertThatThrownBy(() -> diskStoreValidator.validate(CacheElementOperation.CREATE, diskStore)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessageContaining("Disk usage critical percentage must be set to a value between 0-100"); + } + + @Test + public void diskUsageWarningPercentageMustBeBetweenZeroAndOneHundred() { + diskStore.setDiskUsageWarningPercentage(95F); + diskStoreValidator.validate(CacheElementOperation.CREATE, diskStore); + + diskStore.setDiskUsageWarningPercentage(-1F); + assertThatThrownBy(() -> diskStoreValidator.validate(CacheElementOperation.CREATE, diskStore)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessageContaining("Disk usage warning percentage must be set to a value between 0-100"); + + diskStore.setDiskUsageWarningPercentage(101F); + assertThatThrownBy(() -> diskStoreValidator.validate(CacheElementOperation.CREATE, diskStore)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessageContaining("Disk usage warning percentage must be set to a value between 0-100"); + } + + @Test + public void compactionThresholdMustBeBetweenZeroAndOneHundres() { + diskStore.setCompactionThreshold(95); + diskStoreValidator.validate(CacheElementOperation.CREATE, diskStore); + + diskStore.setCompactionThreshold(-1); + assertThatThrownBy(() -> diskStoreValidator.validate(CacheElementOperation.CREATE, diskStore)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessageContaining("CompactionThreshold has to be set to a value between 0-100."); + + diskStore.setCompactionThreshold(101); + assertThatThrownBy(() -> diskStoreValidator.validate(CacheElementOperation.CREATE, diskStore)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessageContaining("CompactionThreshold has to be set to a value between 0-100."); + } + + @Test + public void maxOplogSizeInBytesMustBePositiveNumber() { + diskStore.setMaxOplogSizeInBytes(Long.MAX_VALUE); + diskStoreValidator.validate(CacheElementOperation.CREATE, diskStore); + + diskStore.setMaxOplogSizeInBytes(-1L); + assertThatThrownBy(() -> diskStoreValidator.validate(CacheElementOperation.CREATE, diskStore)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessageContaining("Maximum Oplog size specified has to be a non-negative number and the value given"); + } + + @Test + public void queueSizeMustBePositiveNumber() { + diskStore.setQueueSize(Integer.MAX_VALUE); + diskStoreValidator.validate(CacheElementOperation.CREATE, diskStore); + + diskStore.setQueueSize(-1); + assertThatThrownBy(() -> diskStoreValidator.validate(CacheElementOperation.CREATE, diskStore)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessageContaining("Queue size specified has to be a non-negative number and the value given"); + } + + @Test + public void writeBufferSizeMustBePositiveNumber() { + diskStore.setWriteBufferSize(Integer.MAX_VALUE); + diskStoreValidator.validate(CacheElementOperation.CREATE, diskStore); + + diskStore.setWriteBufferSize(-1); + assertThatThrownBy(() -> diskStoreValidator.validate(CacheElementOperation.CREATE, diskStore)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessageContaining("Write buffer size specified has to be a non-negative number and the value given"); + } + + @Test + public void dirSizesMustBePositiveNumber() { + diskStoreValidator.validate(CacheElementOperation.CREATE, diskStore); + + diskDir.setDirSize("-1"); + diskStore.setDirectories(Collections.singletonList(diskDir)); + assertThatThrownBy(() -> diskStoreValidator.validate(CacheElementOperation.CREATE, diskStore)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessageContaining("Dir size cannot be negative :"); + } +} \ No newline at end of file diff --git a/geode-management/src/main/java/org/apache/geode/management/configuration/DiskDir.java b/geode-management/src/main/java/org/apache/geode/management/configuration/DiskDir.java new file mode 100644 index 0000000..0c125bb --- /dev/null +++ b/geode-management/src/main/java/org/apache/geode/management/configuration/DiskDir.java @@ -0,0 +1,54 @@ +/* + * 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.configuration; + +import java.io.Serializable; + +import org.apache.geode.management.api.JsonSerializable; + +public class DiskDir implements Serializable, JsonSerializable { + private String name; + private String dirSize; + + public DiskDir() { + + } + + public DiskDir(String name, String dirSize) { + this.name = name; + this.dirSize = dirSize; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getDirSize() { + return dirSize; + } + + public void setDirSize(String dirSize) { + this.dirSize = dirSize; + } +} diff --git a/geode-management/src/main/java/org/apache/geode/management/configuration/DiskStore.java b/geode-management/src/main/java/org/apache/geode/management/configuration/DiskStore.java new file mode 100644 index 0000000..cc9ff04 --- /dev/null +++ b/geode-management/src/main/java/org/apache/geode/management/configuration/DiskStore.java @@ -0,0 +1,166 @@ +/* + * 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.configuration; + +import java.util.List; +import java.util.Objects; + +import com.fasterxml.jackson.annotation.JsonIgnore; + +import org.apache.geode.annotations.Experimental; +import org.apache.geode.management.runtime.DiskStoreInfo; + +@Experimental +public class DiskStore extends GroupableConfiguration<DiskStoreInfo> { + public static final String DISK_STORE_CONFIG_ENDPOINT = "/diskstores"; + + @JsonIgnore + private String id; + private String name; + private Integer compactionThreshold; + private Float diskUsageCriticalPercentage; + private Float diskUsageWarningPercentage; + private Long maxOplogSizeInBytes; + private Integer queueSize; + private Long timeInterval; + private Integer writeBufferSize; + private List<DiskDir> directories; + private Boolean allowForceCompaction; + private Boolean autoCompact; + + public Boolean isAutoCompact() { + return autoCompact; + } + + public void setAutoCompact(Boolean autoCompact) { + this.autoCompact = autoCompact; + } + + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public List<DiskDir> getDirectories() { + return directories; + } + + public void setDirectories(List<DiskDir> directories) { + this.directories = directories; + } + + @Override + public String getId() { + return name; + } + + public Integer getCompactionThreshold() { + return compactionThreshold; + } + + public void setCompactionThreshold(Integer compactionThreshold) { + this.compactionThreshold = compactionThreshold; + } + + public Float getDiskUsageCriticalPercentage() { + return diskUsageCriticalPercentage; + } + + public void setDiskUsageCriticalPercentage(Float diskUsageCriticalPercentage) { + this.diskUsageCriticalPercentage = diskUsageCriticalPercentage; + } + + public Float getDiskUsageWarningPercentage() { + return diskUsageWarningPercentage; + } + + public void setDiskUsageWarningPercentage(Float diskUsageWarningPercentage) { + this.diskUsageWarningPercentage = diskUsageWarningPercentage; + } + + public Long getMaxOplogSizeInBytes() { + return maxOplogSizeInBytes; + } + + public void setMaxOplogSizeInBytes(Long maxOplogSize) { + this.maxOplogSizeInBytes = maxOplogSize; + } + + public Integer getQueueSize() { + return queueSize; + } + + public void setQueueSize(Integer queueSize) { + this.queueSize = queueSize; + } + + public Long getTimeInterval() { + return timeInterval; + } + + public void setTimeInterval(Long timeInterval) { + this.timeInterval = timeInterval; + } + + public Integer getWriteBufferSize() { + return writeBufferSize; + } + + public void setWriteBufferSize(Integer writeBufferSize) { + this.writeBufferSize = writeBufferSize; + } + + public Boolean isAllowForceCompaction() { + return allowForceCompaction; + } + + public void setAllowForceCompaction(Boolean allowForceCompaction) { + this.allowForceCompaction = allowForceCompaction; + } + + @Override + public Links getLinks() { + return new Links(getId(), DISK_STORE_CONFIG_ENDPOINT); + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + if (!super.equals(o)) { + return false; + } + DiskStore diskStore = (DiskStore) o; + return Objects.equals(name, diskStore.name); + } + + @Override + public int hashCode() { + return Objects.hash(super.hashCode(), name); + } +} diff --git a/geode-management/src/main/java/org/apache/geode/management/runtime/DiskStoreInfo.java b/geode-management/src/main/java/org/apache/geode/management/runtime/DiskStoreInfo.java new file mode 100644 index 0000000..77121fc --- /dev/null +++ b/geode-management/src/main/java/org/apache/geode/management/runtime/DiskStoreInfo.java @@ -0,0 +1,25 @@ +/* + * 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.runtime; + +import org.apache.geode.management.api.JsonSerializable; + +public class DiskStoreInfo extends RuntimeInfo implements JsonSerializable { +} diff --git a/geode-management/src/main/resources/org/apache/geode/internal/sanctioned-geode-management-serializables.txt b/geode-management/src/main/resources/org/apache/geode/internal/sanctioned-geode-management-serializables.txt index 8e13504..9293e03 100644 --- a/geode-management/src/main/resources/org/apache/geode/internal/sanctioned-geode-management-serializables.txt +++ b/geode-management/src/main/resources/org/apache/geode/internal/sanctioned-geode-management-serializables.txt @@ -7,6 +7,8 @@ org/apache/geode/management/configuration/AbstractConfiguration,true,-6612840641 org/apache/geode/management/configuration/AutoSerializer,true,1,patterns:java/util/List,portable:java/lang/Boolean org/apache/geode/management/configuration/ClassName,true,1,className:java/lang/String,initProperties:java/util/Properties org/apache/geode/management/configuration/Deployment,true,6992732279452865384,deployedBy:java/lang/String,deployedTime:java/lang/String,jarFileName:java/lang/String +org/apache/geode/management/configuration/DiskDir,false,dirSize:java/lang/String,name:java/lang/String +org/apache/geode/management/configuration/DiskStore,false,allowForceCompaction:java/lang/Boolean,autoCompact:java/lang/Boolean,compactionThreshold:java/lang/Integer,directories:java/util/List,diskUsageCriticalPercentage:java/lang/Float,diskUsageWarningPercentage:java/lang/Float,id:java/lang/String,maxOplogSizeInBytes:java/lang/Long,name:java/lang/String,queueSize:java/lang/Integer,timeInterval:java/lang/Long,writeBufferSize:java/lang/Integer org/apache/geode/management/configuration/GatewayReceiver,false,endPort:java/lang/Integer,gatewayTransportFilters:java/util/List,manualStart:java/lang/Boolean,maximumTimeBetweenPings:java/lang/Integer,socketBufferSize:java/lang/Integer,startPort:java/lang/Integer org/apache/geode/management/configuration/GroupableConfiguration,false,group:java/lang/String org/apache/geode/management/configuration/Index,false,expression:java/lang/String,indexType:org/apache/geode/management/configuration/IndexType,name:java/lang/String,regionPath:java/lang/String @@ -29,6 +31,7 @@ org/apache/geode/management/operation/RebalanceOperation,false,excludeRegions:ja org/apache/geode/management/operation/RestoreRedundancyRequest,true,-3896185413062876188,excludeRegions:java/util/List,includeRegions:java/util/List,operator:java/lang/String,reassignPrimaries:boolean org/apache/geode/management/runtime/CacheServerInfo,true,1,bindAddress:java/lang/String,isRunning:boolean,maxConnections:int,maxThreads:int,port:int org/apache/geode/management/runtime/DeploymentInfo,false,jarLocation:java/lang/String,lastModified:java/lang/String +org/apache/geode/management/runtime/DiskStoreInfo,false org/apache/geode/management/runtime/GatewayReceiverInfo,false,bindAddress:java/lang/String,connectedSenders:java/lang/String[],hostnameForSenders:java/lang/String,port:int,running:boolean,senderCount:int org/apache/geode/management/runtime/IndexInfo,false org/apache/geode/management/runtime/MemberInformation,true,1,cacheServerList:java/util/List,cacheXmlFilePath:java/lang/String,clientCount:int,cpuUsage:double,groups:java/lang/String,heapUsage:long,host:java/lang/String,hostedRegions:java/util/Set,httpServiceBindAddress:java/lang/String,httpServicePort:int,id:java/lang/String,initHeapSize:long,isCoordinator:boolean,isSecured:boolean,isServer:boolean,locatorPort:int,locators:java/lang/String,logFilePath:java/lang/String,maxHeapSize:long,of [...] diff --git a/geode-web-management/src/distributedTest/java/org/apache/geode/management/client/CreateDiskStoreDUnitTest.java b/geode-web-management/src/distributedTest/java/org/apache/geode/management/client/CreateDiskStoreDUnitTest.java new file mode 100644 index 0000000..1afd160 --- /dev/null +++ b/geode-web-management/src/distributedTest/java/org/apache/geode/management/client/CreateDiskStoreDUnitTest.java @@ -0,0 +1,299 @@ +/* + * 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.client; + + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +import java.util.ArrayList; +import java.util.List; + +import org.junit.After; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.test.annotation.DirtiesContext; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringRunner; +import org.springframework.test.context.web.WebAppConfiguration; +import org.springframework.web.client.RestTemplate; +import org.springframework.web.context.WebApplicationContext; + +import org.apache.geode.distributed.internal.InternalConfigurationPersistenceService; +import org.apache.geode.distributed.internal.InternalLocator; +import org.apache.geode.management.api.ClusterManagementException; +import org.apache.geode.management.api.ClusterManagementGetResult; +import org.apache.geode.management.api.ClusterManagementRealizationResult; +import org.apache.geode.management.api.ClusterManagementResult; +import org.apache.geode.management.api.ClusterManagementService; +import org.apache.geode.management.api.RealizationResult; +import org.apache.geode.management.api.RestTemplateClusterManagementServiceTransport; +import org.apache.geode.management.configuration.DiskDir; +import org.apache.geode.management.configuration.DiskStore; +import org.apache.geode.management.configuration.Region; +import org.apache.geode.management.configuration.RegionType; +import org.apache.geode.management.internal.rest.LocatorWebContext; +import org.apache.geode.management.internal.rest.PlainLocatorContextLoader; +import org.apache.geode.management.runtime.DiskStoreInfo; +import org.apache.geode.test.dunit.IgnoredException; +import org.apache.geode.test.dunit.rules.ClusterStartupRule; +import org.apache.geode.test.dunit.rules.MemberVM; + +@RunWith(SpringRunner.class) +@ContextConfiguration(locations = {"classpath*:WEB-INF/management-servlet.xml"}, + loader = PlainLocatorContextLoader.class) +@WebAppConfiguration +@DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_CLASS) +public class CreateDiskStoreDUnitTest { + + @Autowired + private WebApplicationContext webApplicationContext; + + @Rule + public ClusterStartupRule cluster = new ClusterStartupRule(1); + + private ClusterManagementService client; + private LocatorWebContext webContext; + private DiskStore diskStore; + private final String diskStoreName = "testDiskStore"; + private MemberVM server; + + + + @Before + public void before() { + cluster.setSkipLocalDistributedSystemCleanup(true); + webContext = new LocatorWebContext(webApplicationContext); + client = new ClusterManagementServiceBuilder().setTransport( + new RestTemplateClusterManagementServiceTransport( + new RestTemplate(webContext.getRequestFactory()))) + .build(); + diskStore = createDiskStoreConfigObject(diskStoreName); + } + + @After + public void after() { + // for the test to be run multiple times, we need to clean out the cluster config + InternalConfigurationPersistenceService cps = getLocator().getConfigurationPersistenceService(); + cps.updateCacheConfig("cluster", config -> { + config.getDiskStores().clear(); + return config; + }); + if (server != null) { + server.stop(); + } + } + + private DiskStore createDiskStoreConfigObject(String diskStoreName) { + DiskStore diskStore = new DiskStore(); + diskStore.setName(diskStoreName); + DiskDir diskDir = new DiskDir("DiskStoreDirectory", null); + List<DiskDir> directories = new ArrayList<>(); + directories.add(diskDir); + diskStore.setDirectories(directories); + return diskStore; + } + + InternalLocator getLocator() { + return ((PlainLocatorContextLoader) webContext.getLocator()).getLocatorStartupRule() + .getLocator(); + } + + @Test + public void createDiskStoreWithNoServer() { + assertThatThrownBy(() -> client.get(diskStore)).isInstanceOf(ClusterManagementException.class) + .hasMessageContaining("ENTITY_NOT_FOUND"); + + ClusterManagementRealizationResult result = client.create(diskStore); + assertThat(result.isSuccessful()).isTrue(); + assertThat(result.getStatusCode()).isEqualTo(ClusterManagementResult.StatusCode.OK); + assertThat(result.getMemberStatuses()).hasSize(0); + } + + @Test + public void createDuplicateDiskStoreFails() { + assertThatThrownBy(() -> client.get(diskStore)).isInstanceOf(ClusterManagementException.class) + .hasMessageContaining("ENTITY_NOT_FOUND"); + + client.create(diskStore); + + // call create the 2nd time + assertThatThrownBy(() -> client.create(diskStore)) + .isInstanceOf(ClusterManagementException.class) + .hasMessageContaining( + "ENTITY_EXISTS: DiskStore '" + diskStoreName + "' already exists in group cluster"); + + } + + @Test + public void getReturnsDesiredDiskStore() { + assertThatThrownBy(() -> client.get(diskStore)).isInstanceOf(ClusterManagementException.class) + .hasMessageContaining("ENTITY_NOT_FOUND"); + + client.create(diskStore); + // verify the get + ClusterManagementGetResult<DiskStore, DiskStoreInfo> getResult = client.get(diskStore); + DiskStore configResult = getResult.getResult().getConfigurations().get(0); + assertThat(configResult.getName()).isEqualTo(diskStoreName); + assertThat(configResult.getDirectories().size()).isEqualTo(1); + assertThat(getResult.getResult().getRuntimeInfos()).hasSize(0); + } + + @Test + public void createDiskStoreWithARunningServerShouldSucceed() { + assertThatThrownBy(() -> client.get(diskStore)).isInstanceOf(ClusterManagementException.class) + .hasMessageContaining("ENTITY_NOT_FOUND"); + + cluster.startServerVM(1, webContext.getLocator().getPort()); + + ClusterManagementRealizationResult result = client.create(diskStore); + assertThat(result.isSuccessful()).isTrue(); + assertThat(result.getStatusCode()).isEqualTo(ClusterManagementResult.StatusCode.OK); + + RealizationResult status = result.getMemberStatuses().get(0); + assertThat(status.getMemberName()).isEqualTo("server-1"); + assertThat(status.getMessage()).contains( + "DiskStore " + diskStoreName + " created successfully."); + } + + @Test + public void creatingADuplicateDiskStoreWhileServerRunningShouldThrowException() { + assertThatThrownBy(() -> client.get(diskStore)).isInstanceOf(ClusterManagementException.class) + .hasMessageContaining("ENTITY_NOT_FOUND"); + + cluster.startServerVM(1, webContext.getLocator().getPort()); + + client.create(diskStore); + + // create the 2nd time + assertThatThrownBy(() -> client.create(diskStore)) + .isInstanceOf(ClusterManagementException.class) + .hasMessageContaining( + "ENTITY_EXISTS: DiskStore '" + diskStoreName + "' already exists in group cluster."); + } + + @Test + public void shouldBeAbleToGetACreatedDiskStore() { + assertThatThrownBy(() -> client.get(diskStore)).isInstanceOf(ClusterManagementException.class) + .hasMessageContaining("ENTITY_NOT_FOUND"); + + cluster.startServerVM(1, webContext.getLocator().getPort()); + + client.create(diskStore); + + ClusterManagementGetResult<DiskStore, DiskStoreInfo> getResult = client.get(diskStore); + DiskStore configResult = getResult.getResult().getConfigurations().get(0); + assertThat(configResult.getName()).isEqualTo(diskStoreName); + List<DiskStoreInfo> runtimeResults = getResult.getResult().getRuntimeInfos(); + assertThat(runtimeResults).hasSize(1); + } + + @Test + public void createDiskStoreWithoutAServerThenStartServerShouldCreateDiskStore() { + assertThatThrownBy(() -> client.get(diskStore)).isInstanceOf(ClusterManagementException.class) + .hasMessageContaining("ENTITY_NOT_FOUND"); + + client.create(diskStore); + server = cluster.startServerVM(1, webContext.getLocator().getPort()); + + ClusterManagementGetResult<DiskStore, DiskStoreInfo> getResult = client.get(diskStore); + DiskStore configResult = getResult.getResult().getConfigurations().get(0); + assertThat(configResult.getName()).isEqualTo(diskStoreName); + List<DiskStoreInfo> runtimeResults = getResult.getResult().getRuntimeInfos(); + assertThat(runtimeResults).hasSize(1); + } + + @Test + public void destroyingADiskStoreAfterAServerHasBeenTakenOffline() { + assertThatThrownBy(() -> client.get(diskStore)).isInstanceOf(ClusterManagementException.class) + .hasMessageContaining("ENTITY_NOT_FOUND"); + + client.create(diskStore); + server = cluster.startServerVM(1, webContext.getLocator().getPort()); + + server.stop(); + ClusterManagementRealizationResult deleteResult = client.delete(diskStore); + assertThat(deleteResult.getStatusMessage()) + .isEqualTo("Successfully updated configuration for cluster."); + + assertThatThrownBy(() -> client.get(diskStore)).isInstanceOf(ClusterManagementException.class) + .hasMessageContaining("ENTITY_NOT_FOUND"); + } + + @Test + public void destroyingDiskStoreBeforeDiskStoresActuallyCreatedShouldSucceed() { + assertThatThrownBy(() -> client.get(diskStore)).isInstanceOf(ClusterManagementException.class) + .hasMessageContaining("ENTITY_NOT_FOUND"); + + client.create(diskStore); + ClusterManagementRealizationResult deleteResult = client.delete(diskStore); + assertThat(deleteResult.getStatusMessage()) + .isEqualTo("Successfully updated configuration for cluster."); + + assertThatThrownBy(() -> client.get(diskStore)).isInstanceOf(ClusterManagementException.class) + .hasMessageContaining("ENTITY_NOT_FOUND"); + } + + @Test + public void listDiskStoresShouldReturnAllConfiguredDiskStores() { + assertThatThrownBy(() -> client.get(diskStore)).isInstanceOf(ClusterManagementException.class) + .hasMessageContaining("ENTITY_NOT_FOUND"); + + client.create(diskStore); + client.create(createDiskStoreConfigObject("DiskStore2")); + client.create(createDiskStoreConfigObject("DiskStore3")); + assertThat(client.list(new DiskStore()).getResult().size()).isEqualTo(3); + } + + + @Test + public void listDiskStoresShouldReturnNonDeletedDiskStores() { + assertThatThrownBy(() -> client.get(diskStore)).isInstanceOf(ClusterManagementException.class) + .hasMessageContaining("ENTITY_NOT_FOUND"); + + client.create(diskStore); + client.create(createDiskStoreConfigObject("DiskStore2")); + client.create(createDiskStoreConfigObject("DiskStore3")); + client.delete(diskStore); + assertThat(client.list(new DiskStore()).getResult().size()).isEqualTo(2); + } + + @Test + public void cannotRemoveDiskstoreWhileUsedByRegion() { + IgnoredException.addIgnoredException(".*Disk store is currently in use by these regions.*"); + assertThatThrownBy(() -> client.get(diskStore)).isInstanceOf(ClusterManagementException.class) + .hasMessageContaining("ENTITY_NOT_FOUND"); + + client.create(diskStore); + server = cluster.startServerVM(1, webContext.getLocator().getPort()); + + Region region = new Region(); + region.setName("testregion"); + region.setType(RegionType.PARTITION_PERSISTENT); + region.setDiskStoreName(diskStoreName); + client.create(region); + + assertThatThrownBy(() -> client.delete(diskStore)) + .hasMessageContaining("Disk store is currently in use by these regions"); + + client.delete(region); + ClusterManagementRealizationResult deleteResult = client.delete(diskStore); + assertThat(deleteResult.isSuccessful()).isTrue(); + } +} diff --git a/geode-web-management/src/integrationTest/java/org/apache/geode/management/internal/rest/DiskStoreManagementIntegrationTest.java b/geode-web-management/src/integrationTest/java/org/apache/geode/management/internal/rest/DiskStoreManagementIntegrationTest.java new file mode 100644 index 0000000..dcfd131 --- /dev/null +++ b/geode-web-management/src/integrationTest/java/org/apache/geode/management/internal/rest/DiskStoreManagementIntegrationTest.java @@ -0,0 +1,149 @@ +/* + * 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.rest; + +import static org.apache.geode.test.junit.assertions.ClusterManagementRealizationResultAssert.assertManagementResult; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +import java.util.Collections; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.test.context.support.WithMockUser; +import org.springframework.test.annotation.DirtiesContext; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringRunner; +import org.springframework.test.context.web.WebAppConfiguration; +import org.springframework.web.client.RestTemplate; +import org.springframework.web.context.WebApplicationContext; + +import org.apache.geode.management.api.ClusterManagementGetResult; +import org.apache.geode.management.api.ClusterManagementResult; +import org.apache.geode.management.api.ClusterManagementService; +import org.apache.geode.management.api.RestTemplateClusterManagementServiceTransport; +import org.apache.geode.management.client.ClusterManagementServiceBuilder; +import org.apache.geode.management.configuration.DiskDir; +import org.apache.geode.management.configuration.DiskStore; +import org.apache.geode.management.runtime.DiskStoreInfo; + +@RunWith(SpringRunner.class) +@ContextConfiguration(locations = {"classpath*:WEB-INF/management-servlet.xml"}, + loader = PlainLocatorContextLoader.class) +@WebAppConfiguration +@DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_CLASS) +public class DiskStoreManagementIntegrationTest { + + @Autowired + private WebApplicationContext webApplicationContext; + + private ClusterManagementService client; + + private DiskStore diskStore; + private DiskDir diskDir; + + @Before + public void before() { + // needs to be used together with any BaseLocatorContextLoader + LocatorWebContext context = new LocatorWebContext(webApplicationContext); + client = new ClusterManagementServiceBuilder().setTransport( + new RestTemplateClusterManagementServiceTransport( + new RestTemplate(context.getRequestFactory()))) + .build(); + diskStore = new DiskStore(); + diskDir = new DiskDir(); + } + + @Test + @WithMockUser + public void sanityCheck() { + diskStore.setName("storeone"); + diskDir.setName("diskdirone"); + diskStore.setDirectories(Collections.singletonList(diskDir)); + + assertManagementResult(client.create(diskStore)) + .hasStatusCode(ClusterManagementResult.StatusCode.OK); + assertManagementResult(client.delete(diskStore)) + .hasStatusCode(ClusterManagementResult.StatusCode.OK); + } + + @Test + @WithMockUser + public void createWithInvalidGroupFails() { + diskStore.setName("storeone"); + diskDir.setName("diskdirone"); + diskStore.setDirectories(Collections.singletonList(diskDir)); + diskStore.setGroup("cluster"); + + assertThatThrownBy(() -> client.create(diskStore)) + .hasMessageContaining("ILLEGAL_ARGUMENT: 'cluster' is a reserved group name"); + } + + @Test + public void createWithMissingDiskDirFails() { + diskStore.setName("storeone"); + + assertThatThrownBy(() -> client.create(diskStore)) + .hasMessageContaining("ILLEGAL_ARGUMENT: At least one DiskDir element required"); + } + + @Test + public void createDuplicateDiskStoreFails() { + diskStore.setName("storeone"); + diskDir.setName("diskdirone"); + diskStore.setDirectories(Collections.singletonList(diskDir)); + + // trying to create a duplicate diskstore, reusing existing + assertManagementResult(client.create(diskStore)) + .hasStatusCode(ClusterManagementResult.StatusCode.OK); + + assertThatThrownBy(() -> client.create(diskStore)) + .hasMessageContaining("ENTITY_EXISTS: DiskStore 'storeone' already exists in group cluster"); + + assertManagementResult(client.delete(diskStore)) + .hasStatusCode(ClusterManagementResult.StatusCode.OK); + } + + @Test + public void createWithIllegalParamFails() { + diskStore.setName("storeone"); + diskDir.setName("diskdirone"); + diskStore.setDirectories(Collections.singletonList(diskDir)); + diskStore.setDiskUsageCriticalPercentage(120.0F); + + assertThatThrownBy(() -> client.create(diskStore)) + .hasMessageContaining("ILLEGAL_ARGUMENT: Disk usage critical percentage must be set to a value between 0-100. The value 120.0 is invalid"); + } + + @Test + public void diskStoresCanBeDeleted() { + diskStore.setName("storeone"); + diskDir.setName("diskdirone"); + diskStore.setDirectories(Collections.singletonList(diskDir)); + + assertManagementResult(client.create(diskStore)) + .hasStatusCode(ClusterManagementResult.StatusCode.OK); + + ClusterManagementGetResult<DiskStore, DiskStoreInfo> clusterManagementGetResult = client.get(diskStore); + assertThat(clusterManagementGetResult.isSuccessful()); + assertThat(clusterManagementGetResult.getResult().getId()).isEqualTo("storeone"); + + assertManagementResult(client.delete(diskStore)) + .hasStatusCode(ClusterManagementResult.StatusCode.OK); + } +} diff --git a/geode-web-management/src/main/java/org/apache/geode/management/internal/rest/controllers/DiskStoreController.java b/geode-web-management/src/main/java/org/apache/geode/management/internal/rest/controllers/DiskStoreController.java new file mode 100644 index 0000000..707e2e4 --- /dev/null +++ b/geode-web-management/src/main/java/org/apache/geode/management/internal/rest/controllers/DiskStoreController.java @@ -0,0 +1,113 @@ +/* + * 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.rest.controllers; + +import static org.apache.geode.management.configuration.DiskStore.DISK_STORE_CONFIG_ENDPOINT; +import static org.apache.geode.management.configuration.Links.URI_VERSION; + +import io.swagger.annotations.ApiOperation; +import io.swagger.annotations.ApiResponse; +import io.swagger.annotations.ApiResponses; +import io.swagger.annotations.Extension; +import io.swagger.annotations.ExtensionProperty; +import org.apache.commons.lang3.StringUtils; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +import org.apache.geode.management.api.ClusterManagementGetResult; +import org.apache.geode.management.api.ClusterManagementListResult; +import org.apache.geode.management.api.ClusterManagementResult; +import org.apache.geode.management.configuration.DiskStore; +import org.apache.geode.management.runtime.DiskStoreInfo; +import org.apache.geode.security.ResourcePermission; + +@RestController("diskStoreManagement") +@RequestMapping(URI_VERSION) +public class DiskStoreController extends AbstractManagementController { + + @ApiOperation(value = "create disk-store") + @ApiResponses({ + @ApiResponse(code = 400, message = "Bad request."), + @ApiResponse(code = 409, message = "Diskstore already exists."), + @ApiResponse(code = 500, message = "Internal error.")}) + @PreAuthorize("@securityService.authorize('CLUSTER', 'MANAGE')") + @PostMapping(DISK_STORE_CONFIG_ENDPOINT) + public ResponseEntity<ClusterManagementResult> createDiskStore( + @RequestBody DiskStore diskStoreConfig) { + ClusterManagementResult result = clusterManagementService.create(diskStoreConfig); + return new ResponseEntity<>(result, HttpStatus.CREATED); + } + + @ApiOperation(value = "list disk-stores", + extensions = {@Extension(properties = { + @ExtensionProperty(name = "jqFilter", + value = ".result[] | .groups[] | .runtimeInfo[] + .configuration | {name:.name,type:.type,entryCount:.entryCount}")})}) + @PreAuthorize("@securityService.authorize('CLUSTER', 'READ')") + @GetMapping(DISK_STORE_CONFIG_ENDPOINT) + public ClusterManagementListResult<DiskStore, DiskStoreInfo> listDiskStores( + @RequestParam(required = false) String id, + @RequestParam(required = false) String group) { + DiskStore filter = new DiskStore(); + if (StringUtils.isNotBlank(id)) { + filter.setName(id); + } + if (StringUtils.isNotBlank(group)) { + filter.setGroup(group); + } + return clusterManagementService.list(filter); + } + + @ApiOperation(value = "get disk-store", + extensions = {@Extension(properties = { + @ExtensionProperty(name = "jqFilter", + value = ".result | .groups[] | .runtimeInfo[] + .configuration | {name:.name,type:.type,entryCount:.entryCount}")})}) + @GetMapping(DISK_STORE_CONFIG_ENDPOINT + "/{id}") + public ClusterManagementGetResult<DiskStore, DiskStoreInfo> getDiskStore( + @PathVariable(name = "id") String id) { + securityService.authorize(ResourcePermission.Resource.CLUSTER, + ResourcePermission.Operation.READ, id); + DiskStore config = new DiskStore(); + config.setName(id); + return clusterManagementService.get(config); + } + + @ApiOperation(value = "delete disk-store") + @PreAuthorize("@securityService.authorize('CLUSTER', 'MANAGE')") + @DeleteMapping(DISK_STORE_CONFIG_ENDPOINT + "/{id}") + public ClusterManagementResult deleteDiskStore( + @PathVariable(name = "id") String id, + @RequestParam(required = false) String group) { + DiskStore config = new DiskStore(); + config.setName(id); + if (StringUtils.isNotBlank(group)) { + config.setGroup(group); + } + return clusterManagementService.delete(config); + } +}
