Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions conf/globalConfig/kvm.xml
Original file line number Diff line number Diff line change
Expand Up @@ -273,4 +273,12 @@
<defaultValue>10737418240</defaultValue>
<type>java.lang.Long</type>
</config>

<config>
<category>kvm</category>
<name>kvmagent.autorestart.window</name>
<description>Daily time window during which the automatic restart of zstack-kvmagent (triggered by physical memory hard limit alarm) is allowed. Format: HH:MM-HH:MM in 24-hour server local time, e.g. 02:00-04:00. Cross-midnight windows are supported, e.g. 22:00-02:00. Empty value means always allowed (no time restriction).</description>
<defaultValue>02:00-04:00</defaultValue>
<type>java.lang.String</type>
</config>
</globalConfig>
3 changes: 3 additions & 0 deletions plugin/kvm/src/main/java/org/zstack/kvm/KVMGlobalConfig.java
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,9 @@ public class KVMGlobalConfig {
@GlobalConfigValidation
public static GlobalConfig KVMAGENT_PHYSICAL_MEMORY_USAGE_HARD_LIMIT = new GlobalConfig(CATEGORY, "kvmagent.physicalmemory.usage.hardlimit");

@GlobalConfigValidation(notEmpty = false)
public static GlobalConfig KVMAGENT_AUTO_RESTART_WINDOW = new GlobalConfig(CATEGORY, "kvmagent.autorestart.window");
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Comment from jin.ma:

已修复,commit 2d6e1b0

@GlobalConfigValidation 默认 notEmpty=true,会在 installValidateExtension 之前拒空值,与本字段"空值=任意时间允许"的语义冲突。改为 @GlobalConfigValidation(notEmpty = false),把空值放行给已注册的扩展校验器(KVMHostFactory#start 中对 null/empty 直接 return 通过)。


@GlobalConfigDef(defaultValue = "0G", description = "minimum free memory size to start vm, size in GB")
@BindResourceConfig({HostVO.class, ClusterVO.class})
public static GlobalConfig MINIMUM_MEMORY_SIZE_BEFORE_START_VM = new GlobalConfig(CATEGORY, "min.free.memory.size");
Expand Down
47 changes: 47 additions & 0 deletions plugin/kvm/src/main/java/org/zstack/kvm/KVMHostFactory.java
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,7 @@
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.time.LocalTime;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
Expand Down Expand Up @@ -459,6 +460,14 @@ private void processKvmagentPhysicalMemUsageAbnormal(HostProcessPhysicalMemoryUs
return;
}

String window = KVMGlobalConfig.KVMAGENT_AUTO_RESTART_WINDOW.value();
if (!isNowInAutoRestartWindow(window, LocalTime.now())) {
logger.info(String.format("zstack-kvmagent on host %s exceeded physical memory hard limit, " +
"but current time is outside auto-restart window [%s]; skip auto-restart, will retry on next alarm",
cmd.getHostUuid(), window));
return;
}

logger.debug("The zstack-kvmagent service has exceeded the hard limit for physical memory usage, " +
"and we will try restart it later");
RestartKvmAgentMsg restartKvmAgentMsg = new RestartKvmAgentMsg();
Expand All @@ -467,6 +476,19 @@ private void processKvmagentPhysicalMemUsageAbnormal(HostProcessPhysicalMemoryUs
bus.send(restartKvmAgentMsg);
}

public static boolean isNowInAutoRestartWindow(String configValue, LocalTime now) {
if (configValue == null || configValue.trim().isEmpty()) {
return true;
}
String[] parts = configValue.trim().split("-");
LocalTime start = LocalTime.parse(parts[0]);
LocalTime end = LocalTime.parse(parts[1]);
if (start.isBefore(end)) {
return !now.isBefore(start) && now.isBefore(end);
}
return !now.isBefore(start) || now.isBefore(end);
}

private void initLibvirtTlsCA() {
if (CoreGlobalProperty.UNIT_TEST_ON) {
return;
Expand Down Expand Up @@ -558,6 +580,31 @@ public void validateGlobalConfig(String category, String name, String oldValue,
}
}
});
KVMGlobalConfig.KVMAGENT_AUTO_RESTART_WINDOW.installValidateExtension(new GlobalConfigValidatorExtensionPoint() {
@Override
public void validateGlobalConfig(String category, String name, String oldValue, String value) throws GlobalConfigException {
if (value == null || value.trim().isEmpty()) {
return;
}
String[] parts = value.trim().split("-");
if (parts.length != 2 || !parts[0].matches("\\d{2}:\\d{2}") || !parts[1].matches("\\d{2}:\\d{2}")) {
throw new GlobalConfigException(String.format("%s must be in format HH:MM-HH:MM, but got %s",
KVMGlobalConfig.KVMAGENT_AUTO_RESTART_WINDOW.getCanonicalName(), value));
}
int sh = Integer.parseInt(parts[0].substring(0, 2));
int sm = Integer.parseInt(parts[0].substring(3, 5));
int eh = Integer.parseInt(parts[1].substring(0, 2));
int em = Integer.parseInt(parts[1].substring(3, 5));
if (sh > 23 || eh > 23 || sm > 59 || em > 59) {
throw new GlobalConfigException(String.format("%s has out-of-range hour/minute, but got %s",
KVMGlobalConfig.KVMAGENT_AUTO_RESTART_WINDOW.getCanonicalName(), value));
}
if (sh == eh && sm == em) {
throw new GlobalConfigException(String.format("%s start equals end, but got %s",
KVMGlobalConfig.KVMAGENT_AUTO_RESTART_WINDOW.getCanonicalName(), value));
}
}
});
ResourceConfig resourceConfig = rcf.getResourceConfig(KVMGlobalConfig.VM_CPU_HYPERVISOR_FEATURE.getIdentity());
resourceConfig.installValidatorExtension((resourceUuid, oldValue, newValue) -> {
if (Boolean.TRUE.toString().equals(newValue)) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import org.junit.runner.RunWith
import org.junit.runner.notification.Failure
import org.junit.runners.Suite
import org.zstack.configuration.OfferingUserConfigUtils
import org.zstack.test.unittest.utils.KVMAutoRestartWindowCase
import org.zstack.test.unittest.utils.NetworkUtilsCase
import org.zstack.test.unittest.utils.OfferingUserConfigUtilsCase
import org.zstack.test.unittest.utils.SizeUnitUtilsCase
Expand All @@ -20,7 +21,8 @@ import java.util.stream.Collectors
@Suite.SuiteClasses([
NetworkUtilsCase.class,
OfferingUserConfigUtilsCase.class,
SizeUnitUtilsCase.class
SizeUnitUtilsCase.class,
KVMAutoRestartWindowCase.class
])
class JUnitTestSuite {

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
package org.zstack.test.unittest.utils;

import org.junit.Test;
import org.zstack.kvm.KVMHostFactory;

import java.time.LocalTime;

import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;

public class KVMAutoRestartWindowCase {

private boolean inWindow(String configValue, String now) {
return KVMHostFactory.isNowInAutoRestartWindow(configValue, LocalTime.parse(now));
}

@Test
public void emptyOrNullValueAlwaysAllowed() {
assertTrue(inWindow("", "00:00"));
assertTrue(inWindow("", "12:00"));
assertTrue(inWindow("", "23:59"));
assertTrue(inWindow(null, "12:00"));
assertTrue(inWindow(" ", "12:00"));
}

@Test
public void normalWindowMembership() {
assertTrue(inWindow("02:00-04:00", "02:30"));
assertTrue(inWindow("02:00-04:00", "03:59"));
assertFalse(inWindow("02:00-04:00", "00:00"));
assertFalse(inWindow("02:00-04:00", "14:00"));
}

@Test
public void normalWindowBoundary() {
assertTrue(inWindow("02:00-04:00", "02:00"));
assertFalse(inWindow("02:00-04:00", "04:00"));
assertTrue(inWindow("02:00-04:00", "03:59"));
}

@Test
public void crossMidnightWindowMembership() {
assertTrue(inWindow("22:00-02:00", "23:30"));
assertTrue(inWindow("22:00-02:00", "22:00"));
assertTrue(inWindow("22:00-02:00", "00:30"));
assertTrue(inWindow("22:00-02:00", "01:59"));
assertFalse(inWindow("22:00-02:00", "14:00"));
assertFalse(inWindow("22:00-02:00", "02:00"));
assertFalse(inWindow("22:00-02:00", "21:59"));
}

@Test
public void wholeDayMinusOneMinute() {
assertTrue(inWindow("00:00-23:59", "00:00"));
assertTrue(inWindow("00:00-23:59", "12:00"));
assertTrue(inWindow("00:00-23:59", "23:58"));
assertFalse(inWindow("00:00-23:59", "23:59"));
}
}
8 changes: 8 additions & 0 deletions test/src/test/resources/globalConfig/kvm.xml
Original file line number Diff line number Diff line change
Expand Up @@ -272,4 +272,12 @@
<defaultValue>10737418240</defaultValue>
<type>java.lang.Long</type>
</config>

<config>
<category>kvm</category>
<name>kvmagent.autorestart.window</name>
<description>Daily time window during which the automatic restart of zstack-kvmagent (triggered by physical memory hard limit alarm) is allowed. Format: HH:MM-HH:MM in 24-hour server local time, e.g. 02:00-04:00. Cross-midnight windows are supported, e.g. 22:00-02:00. Empty value means always allowed (no time restriction).</description>
<defaultValue>02:00-04:00</defaultValue>
<type>java.lang.String</type>
</config>
</globalConfig>