supplier)
{
- kernelVersionSupplier = supplier;
+ systemInfoSupplier = supplier;
}
public static int getAvailableProcessors()
@@ -1449,50 +1448,11 @@ public class FBUtilities
public static Semver getKernelVersion()
{
- return kernelVersionSupplier.get();
+ return systemInfoSupplier.get().getKernelVersion();
}
- @VisibleForTesting
- static Semver getKernelVersionFromUname()
+ public static SystemInfo getSystemInfo()
{
- // TODO rewrite this method with Oshi when it is eventually included in the project
- if (!isLinux)
- return null;
-
- try
- {
- String output = exec(Map.of(), Duration.ofSeconds(5), 1024, 1024, "uname", "-r");
-
- if (output.isEmpty())
- throw new RuntimeException("Error while trying to get kernel version, 'uname -r' returned empty output");
-
- return parseKernelVersion(output);
- }
- catch (IOException | TimeoutException e)
- {
- throw new RuntimeException("Error while trying to get kernel version", e);
- }
- catch (InterruptedException e)
- {
- Thread.currentThread().interrupt();
- throw new RuntimeException(e);
- }
- }
-
- @VisibleForTesting
- static Semver parseKernelVersion(String versionString)
- {
- Preconditions.checkNotNull(versionString, "kernel version cannot be null");
- try (Scanner scanner = new Scanner(versionString))
- {
- while (scanner.hasNextLine())
- {
- String version = scanner.nextLine().trim();
- if (version.isEmpty())
- continue;
- return new Semver(version, Semver.SemverType.LOOSE);
- }
- }
- throw new IllegalArgumentException("Error while trying to parse kernel version - no version found");
+ return systemInfoSupplier.get();
}
}
\ No newline at end of file
diff --git a/src/java/org/apache/cassandra/utils/SigarLibrary.java b/src/java/org/apache/cassandra/utils/SigarLibrary.java
deleted file mode 100644
index 830f7cab8e..0000000000
--- a/src/java/org/apache/cassandra/utils/SigarLibrary.java
+++ /dev/null
@@ -1,187 +0,0 @@
-/*
- * 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.cassandra.utils;
-
-import org.hyperic.sigar.*;
-import org.slf4j.LoggerFactory;
-import org.slf4j.Logger;
-
-import org.apache.cassandra.config.CassandraRelevantProperties;
-
-@Shared
-public class SigarLibrary
-{
- private Logger logger = LoggerFactory.getLogger(SigarLibrary.class);
-
- public static final SigarLibrary instance = new SigarLibrary();
-
- private Sigar sigar;
- private FileSystemMap mounts = null;
- private boolean initialized = false;
- private long INFINITY = -1;
- private long EXPECTED_MIN_NOFILE = 10000l; // number of files that can be opened
- private long EXPECTED_NPROC = 32768l; // number of processes
- private long EXPECTED_AS = INFINITY; // address space
-
- // TODO: Determine memlock limits if possible
- // TODO: Determine if file system is remote or local
- // TODO: Determine if disk latency is within acceptable limits
-
- private SigarLibrary()
- {
- logger.info("Initializing SIGAR library");
- try
- {
- sigar = new Sigar();
- mounts = sigar.getFileSystemMap();
- initialized = true;
- }
- catch (SigarException e)
- {
- logger.info("Could not initialize SIGAR library {} ", e.getMessage());
- }
- catch (UnsatisfiedLinkError linkError)
- {
- logger.info("Could not initialize SIGAR library {} ", linkError.getMessage());
- }
- }
-
- /**
- *
- * @return true or false indicating if sigar was successfully initialized
- */
- public boolean initialized()
- {
- return initialized;
- }
-
- private boolean hasAcceptableProcNumber()
- {
- try
- {
- long fileMax = sigar.getResourceLimit().getProcessesMax();
- if (fileMax >= EXPECTED_NPROC || fileMax == INFINITY)
- {
- return true;
- }
- else
- {
- return false;
- }
- }
- catch (SigarException sigarException)
- {
- logger.warn("Could not determine if max processes was acceptable. Error message: {}", sigarException);
- return false;
- }
- }
-
- private boolean hasAcceptableFileLimits()
- {
- try
- {
- long fileMax = sigar.getResourceLimit().getOpenFilesMax();
- if (fileMax >= EXPECTED_MIN_NOFILE || fileMax == INFINITY)
- {
- return true;
- }
- else
- {
- return false;
- }
- }
- catch (SigarException sigarException)
- {
- logger.warn("Could not determine if max open file handle limit is correctly configured. Error message: {}", sigarException);
- return false;
- }
- }
-
- private boolean hasAcceptableAddressSpace()
- {
- try
- {
- long fileMax = sigar.getResourceLimit().getVirtualMemoryMax();
- if (fileMax == EXPECTED_AS)
- {
- return true;
- }
- else
- {
- return false;
- }
- }
- catch (SigarException sigarException)
- {
- logger.warn("Could not determine if VirtualMemoryMax was acceptable. Error message: {}", sigarException);
- return false;
- }
- }
-
- private boolean isSwapEnabled()
- {
- try
- {
- Swap swap = sigar.getSwap();
- long swapSize = swap.getTotal();
- if (swapSize > 0)
- {
- return true;
- }
- else
- {
- return false;
- }
- }
- catch (SigarException sigarException)
- {
- logger.warn("Could not determine if swap configuration is acceptable. Error message: {}", sigarException);
- return false;
- }
- }
-
- public long getPid()
- {
- return initialized ? sigar.getPid() : -1;
- }
-
- public void warnIfRunningInDegradedMode()
- {
- if (initialized)
- {
- boolean swapEnabled = isSwapEnabled();
- boolean goodAddressSpace = hasAcceptableAddressSpace();
- boolean goodFileLimits = hasAcceptableFileLimits();
- boolean goodProcNumber = hasAcceptableProcNumber();
- if (swapEnabled || !goodAddressSpace || !goodFileLimits || !goodProcNumber || CassandraRelevantProperties.TEST_IGNORE_SIGAR.getBoolean())
- {
- logger.warn("Cassandra server running in degraded mode. Is swap disabled? : {}, Address space adequate? : {}, " +
- " nofile limit adequate? : {}, nproc limit adequate? : {} ", !swapEnabled, goodAddressSpace,
- goodFileLimits, goodProcNumber );
- }
- else
- {
- logger.info("Checked OS settings and found them configured for optimal performance.");
- }
- }
- else
- {
- logger.info("Sigar could not be initialized, test for checking degraded mode omitted.");
- }
- }
-}
diff --git a/src/java/org/apache/cassandra/utils/SystemInfo.java b/src/java/org/apache/cassandra/utils/SystemInfo.java
new file mode 100644
index 0000000000..ec793a692b
--- /dev/null
+++ b/src/java/org/apache/cassandra/utils/SystemInfo.java
@@ -0,0 +1,228 @@
+/*
+ * 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.cassandra.utils;
+
+import java.util.List;
+import java.util.Optional;
+import java.util.function.Supplier;
+import java.util.regex.Pattern;
+
+import com.vdurmont.semver4j.Semver;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import org.apache.cassandra.io.util.File;
+import org.apache.cassandra.io.util.FileUtils;
+import oshi.PlatformEnum;
+
+import static java.lang.String.format;
+import static java.util.Optional.empty;
+import static java.util.Optional.of;
+
+/**
+ * An abstraction of System information, this class provides access to system information without specifying how
+ * it is retrieved.
+ */
+public class SystemInfo
+{
+ // TODO: Determine memlock limits if possible
+ // TODO: Determine if file system is remote or local
+ // TODO: Determine if disk latency is within acceptable limits
+
+ private static final Logger logger = LoggerFactory.getLogger(SystemInfo.class);
+
+ private static final long INFINITY = -1L;
+ static final long EXPECTED_MIN_NUMBER_OF_OPENED_FILES = 10000L; // number of files that can be opened
+ static final long EXPECTED_MIN_NUMBER_OF_PROCESSES = 32768L; // number of processes
+ static final long EXPECTED_ADDRESS_SPACE = 0x7FFFFFFFL; // address space
+
+ static final String OPEN_FILES_VIOLATION_MESSAGE = format("Minimum value for max open files should be >= %s. ", EXPECTED_MIN_NUMBER_OF_OPENED_FILES);
+ static final String NUMBER_OF_PROCESSES_VIOLATION_MESSAGE = format("Number of processes should be >= %s. ", EXPECTED_MIN_NUMBER_OF_PROCESSES);
+ static final String ADDRESS_SPACE_VIOLATION_MESSAGE = format("Amount of available address space should be >= %s. ", EXPECTED_ADDRESS_SPACE);
+ static final String SWAP_VIOLATION_MESSAGE = "Swap should be disabled. ";
+
+ /**
+ * The default number of processes that are reported if the actual value can not be retrieved.
+ */
+ private static final long DEFAULT_MAX_PROCESSES = 1024;
+
+ private static final Pattern SPACES_PATTERN = Pattern.compile("\\s+");
+
+ /**
+ * The oshi.SystemInfo has the following note:
+ * Platform-specific Hardware and Software objects are retrieved via memoized suppliers. To conserve memory at the
+ * cost of additional processing time, create a new version of SystemInfo() for subsequent calls. To conserve
+ * processing time at the cost of additional memory usage, re-use the same {@link SystemInfo} object for future
+ * queries.
+ *
+ * We are opting for minimal memory footprint.
+ */
+ private final oshi.SystemInfo si;
+
+ public SystemInfo()
+ {
+ si = new oshi.SystemInfo();
+ }
+
+ /**
+ * @return The PlatformEnum for the current platform. (e.g. Linux, Windows, AIX, etc.)
+ */
+ public PlatformEnum platform()
+ {
+ return oshi.SystemInfo.getCurrentPlatform();
+ }
+
+ /**
+ * Gets the maximum number of processes the user can create.
+ * Note: if not on a Linux system this always return the
+ *
+ * @return The maximum number of processes.
+ * @see #DEFAULT_MAX_PROCESSES
+ */
+ public long getMaxProcess()
+ {
+ // this check only works on Linux systems. Errors fall through to return default.
+ if (platform() == PlatformEnum.LINUX)
+ {
+ String path = format("/proc/%s/limits", getPid());
+ try
+ {
+ List lines = FileUtils.readLines(new File(path));
+ for (String line : lines)
+ {
+ if (line.startsWith("Max processes"))
+ {
+ String[] parts = SPACES_PATTERN.split(line);
+
+ if (parts.length < 3)
+ continue;
+
+ String limit = parts[2];
+ return "unlimited".equals(limit) ? INFINITY : Long.parseLong(limit);
+ }
+ }
+ logger.error("'Max processes' not found in {}", path);
+ }
+ catch (Exception t)
+ {
+ logger.error(format("Unable to read %s", path), t);
+ }
+ }
+
+ /* return the default value for non-Linux systems or parsing error.
+ * Can not return 0 as we know there is at least 1 process (this one) and
+ * -1 historically represents infinity.
+ */
+ return DEFAULT_MAX_PROCESSES;
+ }
+
+ /**
+ * @return The maximum number of open files allowd to the current process/user.
+ */
+ public long getMaxOpenFiles()
+ {
+ // ulimit -H -n
+ return si.getOperatingSystem().getCurrentProcess().getHardOpenFileLimit();
+ }
+
+ /**
+ * Gets the Virtual Memory Size (VSZ). Includes all memory that the process can access,
+ * including memory that is swapped out and memory that is from shared libraries.
+ *
+ * @return The amount of virtual memory allowed to be allocatedby the current process/user.
+ */
+ public long getVirtualMemoryMax()
+ {
+ return si.getOperatingSystem().getCurrentProcess().getVirtualSize();
+ }
+
+ /**
+ * @return The amount of swap space allocated on the system.
+ */
+ public long getSwapSize()
+ {
+ return si.getHardware().getMemory().getVirtualMemory().getSwapTotal();
+ }
+
+ /**
+ * @return the PID of the current system.
+ */
+ public long getPid()
+ {
+ return si.getOperatingSystem().getProcessId();
+ }
+
+ /**
+ * @return the Semver for the kernel version of the OS.
+ */
+ public Semver getKernelVersion()
+ {
+ return new Semver(si.getOperatingSystem().getVersionInfo().getBuildNumber(), Semver.SemverType.LOOSE);
+ }
+
+ /**
+ * Tests if the system is running in degraded mode.
+ *
+ * @return non-empty optional with degradation messages if the system is in degraded mode, empty optional otherwise.
+ */
+ public Optional isDegraded()
+ {
+ Supplier expectedNumProc = () -> {
+ // only check proc on nproc linux
+ if (platform() == PlatformEnum.LINUX)
+ return invalid(getMaxProcess(), EXPECTED_MIN_NUMBER_OF_PROCESSES) ? NUMBER_OF_PROCESSES_VIOLATION_MESSAGE
+ : null;
+ else
+ return format("System is running %s, Linux OS is recommended. ", platform());
+ };
+
+ Supplier swapShouldBeDisabled = () -> (getSwapSize() > 0) ? SWAP_VIOLATION_MESSAGE : null;
+
+ Supplier expectedAddressSpace = () -> invalid(getVirtualMemoryMax(), EXPECTED_ADDRESS_SPACE)
+ ? ADDRESS_SPACE_VIOLATION_MESSAGE
+ : null;
+
+ Supplier expectedMinNoFile = () -> invalid(getMaxOpenFiles(), EXPECTED_MIN_NUMBER_OF_OPENED_FILES)
+ ? OPEN_FILES_VIOLATION_MESSAGE
+ : null;
+
+ StringBuilder sb = new StringBuilder();
+
+ for (Supplier check : List.of(expectedNumProc, swapShouldBeDisabled, expectedAddressSpace, expectedMinNoFile))
+ Optional.ofNullable(check.get()).map(sb::append);
+
+ String message = sb.toString();
+ return message.isEmpty() ? empty() : of(message);
+ }
+
+ /**
+ * Checks if a value is invalid.
+ *
+ * Value is invalid if it is smaller than {@code min} and it is not {@code INFINITY},
+ * here represented as a value of -1;
+ *
+ * @param value the value to check
+ * @param min the minimum value
+ * @return true if value is valid
+ */
+ private boolean invalid(long value, long min)
+ {
+ return value < min && value != INFINITY;
+ }
+}
diff --git a/test/conf/logback-simulator.xml b/test/conf/logback-simulator.xml
index 3a9fabc380..d0082d43fa 100644
--- a/test/conf/logback-simulator.xml
+++ b/test/conf/logback-simulator.xml
@@ -43,7 +43,7 @@
-
+
diff --git a/test/distributed/org/apache/cassandra/distributed/test/ResourceLeakTest.java b/test/distributed/org/apache/cassandra/distributed/test/ResourceLeakTest.java
index b90b895662..736112c6d4 100644
--- a/test/distributed/org/apache/cassandra/distributed/test/ResourceLeakTest.java
+++ b/test/distributed/org/apache/cassandra/distributed/test/ResourceLeakTest.java
@@ -41,7 +41,7 @@ import org.apache.cassandra.distributed.api.IInstanceConfig;
import org.apache.cassandra.distributed.api.IInvokableInstance;
import org.apache.cassandra.distributed.shared.JMXUtil;
import org.apache.cassandra.io.util.File;
-import org.apache.cassandra.utils.SigarLibrary;
+import org.apache.cassandra.utils.FBUtilities;
import static org.apache.cassandra.distributed.api.Feature.GOSSIP;
import static org.apache.cassandra.distributed.api.Feature.JMX;
@@ -91,8 +91,7 @@ public class ResourceLeakTest extends TestBaseImpl
*/
private static Long getProcessId()
{
- // Once Java 9 is ready the process API should provide a better way to get the process ID.
- long pid = SigarLibrary.instance.getPid();
+ long pid = FBUtilities.getSystemInfo().getPid();
if (pid >= 0)
return Long.valueOf(pid);
diff --git a/test/distributed/org/apache/cassandra/distributed/test/TestBaseImpl.java b/test/distributed/org/apache/cassandra/distributed/test/TestBaseImpl.java
index 6bd41a7e1b..72ba9d4f04 100644
--- a/test/distributed/org/apache/cassandra/distributed/test/TestBaseImpl.java
+++ b/test/distributed/org/apache/cassandra/distributed/test/TestBaseImpl.java
@@ -81,7 +81,6 @@ public class TestBaseImpl extends DistributedTestBase
{
ICluster.setup();
SKIP_GC_INSPECTOR.setBoolean(true);
- System.setProperty("sigar.nativeLogging", "false");
}
@Override
diff --git a/test/distributed/org/apache/cassandra/distributed/upgrade/UpgradeTestBase.java b/test/distributed/org/apache/cassandra/distributed/upgrade/UpgradeTestBase.java
index 4b2d0ae374..017c1c5a44 100644
--- a/test/distributed/org/apache/cassandra/distributed/upgrade/UpgradeTestBase.java
+++ b/test/distributed/org/apache/cassandra/distributed/upgrade/UpgradeTestBase.java
@@ -71,7 +71,6 @@ public class UpgradeTestBase extends DistributedTestBase
{
ICluster.setup();
SKIP_GC_INSPECTOR.setBoolean(true);
- System.setProperty("sigar.nativeLogging", "false");
}
diff --git a/test/simulator/main/org/apache/cassandra/simulator/SimulationRunner.java b/test/simulator/main/org/apache/cassandra/simulator/SimulationRunner.java
index 71acd6d95d..026078c9b8 100644
--- a/test/simulator/main/org/apache/cassandra/simulator/SimulationRunner.java
+++ b/test/simulator/main/org/apache/cassandra/simulator/SimulationRunner.java
@@ -74,7 +74,6 @@ import static org.apache.cassandra.config.CassandraRelevantProperties.PAXOS_REPA
import static org.apache.cassandra.config.CassandraRelevantProperties.RING_DELAY;
import static org.apache.cassandra.config.CassandraRelevantProperties.SHUTDOWN_ANNOUNCE_DELAY_IN_MS;
import static org.apache.cassandra.config.CassandraRelevantProperties.SYSTEM_AUTH_DEFAULT_RF;
-import static org.apache.cassandra.config.CassandraRelevantProperties.TEST_IGNORE_SIGAR;
import static org.apache.cassandra.config.CassandraRelevantProperties.DISABLE_GOSSIP_ENDPOINT_REMOVAL;
import static org.apache.cassandra.config.CassandraRelevantProperties.TEST_JVM_DTEST_DISABLE_SSL;
import static org.apache.cassandra.simulator.debug.Reconcile.reconcileWith;
@@ -121,7 +120,6 @@ public class SimulationRunner
DISABLE_SSTABLE_ACTIVITY_TRACKING.setBoolean(false);
DETERMINISM_SSTABLE_COMPRESSION_DEFAULT.setBoolean(false); // compression causes variation in file size for e.g. UUIDs, IP addresses, random file paths
CONSISTENT_DIRECTORY_LISTINGS.setBoolean(true);
- TEST_IGNORE_SIGAR.setBoolean(true);
SYSTEM_AUTH_DEFAULT_RF.setInt(3);
DISABLE_GOSSIP_ENDPOINT_REMOVAL.setBoolean(true);
MEMTABLE_OVERHEAD_SIZE.setInt(100);
diff --git a/test/unit/org/apache/cassandra/config/DatabaseDescriptorRefTest.java b/test/unit/org/apache/cassandra/config/DatabaseDescriptorRefTest.java
index b4da790360..8df889a5bd 100644
--- a/test/unit/org/apache/cassandra/config/DatabaseDescriptorRefTest.java
+++ b/test/unit/org/apache/cassandra/config/DatabaseDescriptorRefTest.java
@@ -276,6 +276,7 @@ public class DatabaseDescriptorRefTest
"org.apache.cassandra.utils.CloseableIterator",
"org.apache.cassandra.utils.FBUtilities",
"org.apache.cassandra.utils.FBUtilities$1",
+ "org.apache.cassandra.utils.SystemInfo",
"org.apache.cassandra.utils.Pair",
"org.apache.cassandra.utils.binlog.BinLogOptions",
"org.apache.cassandra.utils.concurrent.RefCounted",
diff --git a/test/unit/org/apache/cassandra/service/StartupChecksTest.java b/test/unit/org/apache/cassandra/service/StartupChecksTest.java
index 6916f35424..d5ed25a89f 100644
--- a/test/unit/org/apache/cassandra/service/StartupChecksTest.java
+++ b/test/unit/org/apache/cassandra/service/StartupChecksTest.java
@@ -57,6 +57,7 @@ import org.apache.cassandra.schema.SchemaConstants;
import org.apache.cassandra.service.DataResurrectionCheck.Heartbeat;
import org.apache.cassandra.utils.Clock;
import org.apache.cassandra.utils.FBUtilities;
+import org.apache.cassandra.utils.SystemInfo;
import static java.util.Collections.singletonList;
import static org.apache.cassandra.config.CassandraRelevantProperties.TEST_INVALID_LEGACY_SSTABLE_ROOT;
@@ -312,14 +313,21 @@ public class StartupChecksTest
String savedCommitLogLocation = DatabaseDescriptor.getCommitLogLocation();
DiskAccessMode savedCommitLogWriteDiskAccessMode = DatabaseDescriptor.getCommitLogWriteDiskAccessMode();
- Semver savedKernelVersion = FBUtilities.getKernelVersion();
+ SystemInfo savedSystemInfo = FBUtilities.getSystemInfo();
try
{
DatabaseDescriptor.setCommitLogLocation(commitLogLocation);
DatabaseDescriptor.setCommitLogWriteDiskAccessMode(diskAccessMode);
DatabaseDescriptor.initializeCommitLogDiskAccessMode();
assertThat(DatabaseDescriptor.getCommitLogWriteDiskAccessMode()).isEqualTo(diskAccessMode);
- FBUtilities.setKernelVersionSupplier(() -> kernelVersion);
+ FBUtilities.setSystemInfoSupplier(() -> new SystemInfo()
+ {
+ @Override
+ public Semver getKernelVersion()
+ {
+ return kernelVersion;
+ }
+ });
withPathOverriddingFileSystem(Map.of(commitLogLocation, fsType), () -> {
if (expectToFail)
assertThatExceptionOfType(StartupException.class).isThrownBy(() -> StartupChecks.checkKernelBug1057843.execute(options));
@@ -333,7 +341,7 @@ public class StartupChecksTest
DatabaseDescriptor.setCommitLogLocation(savedCommitLogLocation);
DatabaseDescriptor.setCommitLogWriteDiskAccessMode(savedCommitLogWriteDiskAccessMode);
DatabaseDescriptor.initializeCommitLogDiskAccessMode();
- FBUtilities.setKernelVersionSupplier(() -> savedKernelVersion);
+ FBUtilities.setSystemInfoSupplier(() -> savedSystemInfo);
}
}
diff --git a/test/unit/org/apache/cassandra/utils/FBUtilitiesTest.java b/test/unit/org/apache/cassandra/utils/FBUtilitiesTest.java
index 28059f5784..17b59400e7 100644
--- a/test/unit/org/apache/cassandra/utils/FBUtilitiesTest.java
+++ b/test/unit/org/apache/cassandra/utils/FBUtilitiesTest.java
@@ -59,9 +59,7 @@ import org.apache.cassandra.dht.Murmur3Partitioner;
import org.apache.cassandra.dht.OrderPreservingPartitioner;
import org.apache.cassandra.dht.RandomPartitioner;
-import static org.apache.cassandra.utils.FBUtilities.parseKernelVersion;
import static org.assertj.core.api.Assertions.assertThat;
-import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.fail;
@@ -409,20 +407,6 @@ public class FBUtilitiesTest
Assert.assertEquals("Infinity", FBUtilities.prettyPrintAverage(Double.POSITIVE_INFINITY));
}
- @Test
- public void testParseKernelVersion()
- {
- assertThat(parseKernelVersion("4.4.0-21-generic").toString()).isEqualTo("4.4.0-21-generic");
- assertThat(parseKernelVersion("4.4.0-pre21-generic").toString()).isEqualTo("4.4.0-pre21-generic");
- assertThat(parseKernelVersion("4.4-pre21-generic").toString()).isEqualTo("4.4-pre21-generic");
- assertThat(parseKernelVersion("4.4.0-21-generic\n").toString()).isEqualTo("4.4.0-21-generic");
- assertThat(parseKernelVersion("\n4.4.0-21-generic\n").toString()).isEqualTo("4.4.0-21-generic");
- assertThat(parseKernelVersion("\n 4.4.0-21-generic \n").toString()).isEqualTo("4.4.0-21-generic");
-
- assertThatExceptionOfType(IllegalArgumentException.class).isThrownBy(() -> parseKernelVersion("\n \n"))
- .withMessageContaining("no version found");
- }
-
@Test
public void testGetKernelVersion()
{
diff --git a/test/unit/org/apache/cassandra/utils/SystemInfoTest.java b/test/unit/org/apache/cassandra/utils/SystemInfoTest.java
new file mode 100644
index 0000000000..61fec64818
--- /dev/null
+++ b/test/unit/org/apache/cassandra/utils/SystemInfoTest.java
@@ -0,0 +1,180 @@
+/*
+ * 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.cassandra.utils;
+
+import java.util.Optional;
+
+import org.junit.Assume;
+import org.junit.Test;
+
+import com.vdurmont.semver4j.Semver;
+import oshi.PlatformEnum;
+
+import static org.apache.cassandra.utils.SystemInfo.ADDRESS_SPACE_VIOLATION_MESSAGE;
+import static org.apache.cassandra.utils.SystemInfo.NUMBER_OF_PROCESSES_VIOLATION_MESSAGE;
+import static org.apache.cassandra.utils.SystemInfo.OPEN_FILES_VIOLATION_MESSAGE;
+import static org.apache.cassandra.utils.SystemInfo.SWAP_VIOLATION_MESSAGE;
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+public class SystemInfoTest
+{
+ private static class TestSystemInfo extends SystemInfo
+ {
+ @Override
+ public PlatformEnum platform()
+ {
+ return PlatformEnum.LINUX;
+ }
+
+ @Override
+ public long getMaxProcess()
+ {
+ return EXPECTED_MIN_NUMBER_OF_PROCESSES;
+ }
+
+ @Override
+ public long getMaxOpenFiles()
+ {
+ return EXPECTED_MIN_NUMBER_OF_OPENED_FILES;
+ }
+
+ @Override
+ public long getVirtualMemoryMax()
+ {
+ return EXPECTED_ADDRESS_SPACE;
+ }
+
+ @Override
+ public long getSwapSize()
+ {
+ return 0;
+ }
+ }
+
+ @Test
+ public void testSystemInfo()
+ {
+ SystemInfo oldSystemInfo = FBUtilities.getSystemInfo();
+
+ try
+ {
+ // valid testing system info does not violate anything
+ TestSystemInfo testSystemInfo = new TestSystemInfo();
+ assertFalse(testSystemInfo.isDegraded().isPresent());
+
+ // platform
+
+ assertDegradation(new TestSystemInfo()
+ {
+ @Override
+ public PlatformEnum platform()
+ {
+ return PlatformEnum.FREEBSD;
+ }
+ }, "System is running FREEBSD, Linux OS is recommended. ");
+
+ // swap
+
+
+ assertDegradation(new TestSystemInfo()
+ {
+ @Override
+ public long getSwapSize()
+ {
+ return 100;
+ }
+ }, SWAP_VIOLATION_MESSAGE);
+
+ // address space
+
+ assertDegradation(new TestSystemInfo()
+ {
+ @Override
+ public long getVirtualMemoryMax()
+ {
+ return 1234;
+ }
+ }, ADDRESS_SPACE_VIOLATION_MESSAGE);
+
+ // expected minimal number of opened files
+
+ assertDegradation(new TestSystemInfo()
+ {
+ @Override
+ public long getMaxOpenFiles()
+ {
+ return 10;
+ }
+ }, OPEN_FILES_VIOLATION_MESSAGE);
+
+ // expected number of processes
+
+ assertDegradation(new TestSystemInfo()
+ {
+ @Override
+ public long getMaxProcess()
+ {
+ return 5;
+ }
+ }, NUMBER_OF_PROCESSES_VIOLATION_MESSAGE);
+
+ // test multiple violations
+
+ assertDegradation(new TestSystemInfo()
+ {
+ @Override
+ public PlatformEnum platform()
+ {
+ return PlatformEnum.FREEBSD;
+ }
+
+ @Override
+ public long getSwapSize()
+ {
+ return 10;
+ }
+ }, "System is running FREEBSD, Linux OS is recommended. " + SWAP_VIOLATION_MESSAGE);
+ }
+ finally
+ {
+ FBUtilities.setSystemInfoSupplier(() -> oldSystemInfo);
+ }
+ }
+
+ @Test
+ public void testGetKernelVersion()
+ {
+ Assume.assumeTrue(FBUtilities.isLinux);
+ Semver kernelVersion = FBUtilities.getSystemInfo().getKernelVersion();
+ assertThat(kernelVersion).isGreaterThan(new Semver("0.0.0", Semver.SemverType.LOOSE))
+ .isLessThan(new Semver("100.0.0", Semver.SemverType.LOOSE));
+ }
+
+ private void assertDegradation(final SystemInfo systemInfo, String expectedDegradation)
+ {
+ FBUtilities.setSystemInfoSupplier(() -> systemInfo);
+ Optional degradations = FBUtilities.getSystemInfo().isDegraded();
+
+ assertTrue(degradations.isPresent());
+ assertEquals(expectedDegradation, degradations.get());
+ }
+}