Merge branch '4.20'

This commit is contained in:
Daan Hoogland 2025-05-01 15:44:09 +02:00
commit d7d9d131b2
14 changed files with 79 additions and 27 deletions

View File

@ -37,7 +37,7 @@ import com.cloud.resource.ServerResource;
* AgentManager manages hosts. It directly coordinates between the DAOs and the connections it manages. * AgentManager manages hosts. It directly coordinates between the DAOs and the connections it manages.
*/ */
public interface AgentManager { public interface AgentManager {
static final ConfigKey<Integer> Wait = new ConfigKey<Integer>("Advanced", Integer.class, "wait", "1800", "Time in seconds to wait for control commands to return", ConfigKey<Integer> Wait = new ConfigKey<Integer>("Advanced", Integer.class, "wait", "1800", "Time in seconds to wait for control commands to return",
true); true);
ConfigKey<Boolean> EnableKVMAutoEnableDisable = new ConfigKey<>(Boolean.class, ConfigKey<Boolean> EnableKVMAutoEnableDisable = new ConfigKey<>(Boolean.class,
"enable.kvm.host.auto.enable.disable", "enable.kvm.host.auto.enable.disable",
@ -54,7 +54,7 @@ public interface AgentManager {
"This timeout overrides the wait global config. This holds a comma separated key value pairs containing timeout (in seconds) for specific commands. " + "This timeout overrides the wait global config. This holds a comma separated key value pairs containing timeout (in seconds) for specific commands. " +
"For example: DhcpEntryCommand=600, SavePasswordCommand=300, VmDataCommand=300", false); "For example: DhcpEntryCommand=600, SavePasswordCommand=300, VmDataCommand=300", false);
public enum TapAgentsAction { enum TapAgentsAction {
Add, Del, Contains, Add, Del, Contains,
} }

View File

@ -55,8 +55,8 @@ import org.apache.cloudstack.managed.context.ManagedContextRunnable;
import org.apache.cloudstack.management.ManagementServerHost; import org.apache.cloudstack.management.ManagementServerHost;
import org.apache.cloudstack.outofbandmanagement.dao.OutOfBandManagementDao; import org.apache.cloudstack.outofbandmanagement.dao.OutOfBandManagementDao;
import org.apache.cloudstack.utils.identity.ManagementServerNode; import org.apache.cloudstack.utils.identity.ManagementServerNode;
import org.apache.commons.collections.MapUtils;
import org.apache.cloudstack.utils.reflectiontostringbuilderutils.ReflectionToStringBuilderUtils; import org.apache.cloudstack.utils.reflectiontostringbuilderutils.ReflectionToStringBuilderUtils;
import org.apache.commons.collections.MapUtils;
import org.apache.commons.lang3.BooleanUtils; import org.apache.commons.lang3.BooleanUtils;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.apache.logging.log4j.ThreadContext; import org.apache.logging.log4j.ThreadContext;
@ -223,6 +223,8 @@ public class AgentManagerImpl extends ManagerBase implements AgentManager, Handl
"Number of maximum concurrent new connections server allows for remote (indirect) agents. " + "Number of maximum concurrent new connections server allows for remote (indirect) agents. " +
"If set to zero (default value) then no limit will be enforced on concurrent new connections", "If set to zero (default value) then no limit will be enforced on concurrent new connections",
false); false);
protected final ConfigKey<Integer> RemoteAgentNewConnectionsMonitorInterval = new ConfigKey<>("Advanced", Integer.class, "agent.connections.monitor.interval", "1800",
"Time in seconds to monitor the new agent connections and cleanup the expired connections.", false);
protected final ConfigKey<Integer> AlertWait = new ConfigKey<>("Advanced", Integer.class, "alert.wait", "1800", protected final ConfigKey<Integer> AlertWait = new ConfigKey<>("Advanced", Integer.class, "alert.wait", "1800",
"Seconds to wait before alerting on a disconnected agent", true); "Seconds to wait before alerting on a disconnected agent", true);
protected final ConfigKey<Integer> DirectAgentLoadSize = new ConfigKey<>("Advanced", Integer.class, "direct.agent.load.size", "16", protected final ConfigKey<Integer> DirectAgentLoadSize = new ConfigKey<>("Advanced", Integer.class, "direct.agent.load.size", "16",
@ -1945,7 +1947,7 @@ public class AgentManagerImpl extends ManagerBase implements AgentManager, Handl
@Override @Override
protected void runInContext() { protected void runInContext() {
logger.trace("Agent New Connections Monitor is started."); logger.trace("Agent New Connections Monitor is started.");
final int cleanupTime = Wait.value(); final int cleanupTime = RemoteAgentNewConnectionsMonitorInterval.value();
Set<Map.Entry<String, Long>> entrySet = newAgentConnections.entrySet(); Set<Map.Entry<String, Long>> entrySet = newAgentConnections.entrySet();
long cutOff = System.currentTimeMillis() - (cleanupTime * 1000L); long cutOff = System.currentTimeMillis() - (cleanupTime * 1000L);
List<String> expiredConnections = newAgentConnections.entrySet() List<String> expiredConnections = newAgentConnections.entrySet()
@ -2040,7 +2042,8 @@ public class AgentManagerImpl extends ManagerBase implements AgentManager, Handl
public ConfigKey<?>[] getConfigKeys() { public ConfigKey<?>[] getConfigKeys() {
return new ConfigKey<?>[] { CheckTxnBeforeSending, Workers, Port, Wait, AlertWait, DirectAgentLoadSize, return new ConfigKey<?>[] { CheckTxnBeforeSending, Workers, Port, Wait, AlertWait, DirectAgentLoadSize,
DirectAgentPoolSize, DirectAgentThreadCap, EnableKVMAutoEnableDisable, ReadyCommandWait, DirectAgentPoolSize, DirectAgentThreadCap, EnableKVMAutoEnableDisable, ReadyCommandWait,
GranularWaitTimeForCommands, RemoteAgentSslHandshakeTimeout, RemoteAgentMaxConcurrentNewConnections }; GranularWaitTimeForCommands, RemoteAgentSslHandshakeTimeout, RemoteAgentMaxConcurrentNewConnections,
RemoteAgentNewConnectionsMonitorInterval };
} }
protected class SetHostParamsListener implements Listener { protected class SetHostParamsListener implements Listener {

View File

@ -427,7 +427,7 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac
static final ConfigKey<Long> VmOpCleanupInterval = new ConfigKey<Long>("Advanced", Long.class, "vm.op.cleanup.interval", "86400", static final ConfigKey<Long> VmOpCleanupInterval = new ConfigKey<Long>("Advanced", Long.class, "vm.op.cleanup.interval", "86400",
"Interval to run the thread that cleans up the vm operations (in seconds)", false); "Interval to run the thread that cleans up the vm operations (in seconds)", false);
static final ConfigKey<Long> VmOpCleanupWait = new ConfigKey<Long>("Advanced", Long.class, "vm.op.cleanup.wait", "3600", static final ConfigKey<Long> VmOpCleanupWait = new ConfigKey<Long>("Advanced", Long.class, "vm.op.cleanup.wait", "3600",
"Time (in seconds) to wait before cleanuping up any vm work items", true); "Time (in seconds) to wait before cleaning up any vm work items", true);
static final ConfigKey<Long> VmOpCancelInterval = new ConfigKey<Long>("Advanced", Long.class, "vm.op.cancel.interval", "3600", static final ConfigKey<Long> VmOpCancelInterval = new ConfigKey<Long>("Advanced", Long.class, "vm.op.cancel.interval", "3600",
"Time (in seconds) to wait before cancelling a operation", false); "Time (in seconds) to wait before cancelling a operation", false);
static final ConfigKey<Boolean> VmDestroyForcestop = new ConfigKey<Boolean>("Advanced", Boolean.class, "vm.destroy.forcestop", "false", static final ConfigKey<Boolean> VmDestroyForcestop = new ConfigKey<Boolean>("Advanced", Boolean.class, "vm.destroy.forcestop", "false",

View File

@ -23,6 +23,7 @@ import static com.cloud.utils.ReflectUtil.flattenProperties;
import static com.google.common.collect.Lists.newArrayList; import static com.google.common.collect.Lists.newArrayList;
import java.io.File; import java.io.File;
import java.io.UnsupportedEncodingException;
import java.net.URI; import java.net.URI;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
@ -810,7 +811,7 @@ public class XenServerStorageProcessor implements StorageProcessor {
final SR poolSr = hypervisorResource.getStorageRepository(conn, final SR poolSr = hypervisorResource.getStorageRepository(conn,
CitrixHelper.getSRNameLabel(primaryStore.getUuid(), primaryStore.getPoolType(), primaryStore.getPath())); CitrixHelper.getSRNameLabel(primaryStore.getUuid(), primaryStore.getPoolType(), primaryStore.getPath()));
VDI.Record vdir = new VDI.Record(); VDI.Record vdir = new VDI.Record();
vdir.nameLabel = volume.getName(); vdir.nameLabel = getEncodedVolumeName(volume.getName());
vdir.SR = poolSr; vdir.SR = poolSr;
vdir.type = Types.VdiType.USER; vdir.type = Types.VdiType.USER;
@ -831,6 +832,26 @@ public class XenServerStorageProcessor implements StorageProcessor {
} }
} }
private String getEncodedVolumeName(String volumeName) throws UnsupportedEncodingException {
byte[] utf8Bytes = volumeName.getBytes("UTF-8");
// Decode UTF-8 into a Java String (UTF-16)
String decoded = new String(utf8Bytes, "UTF-8");
// Print each code unit as a Unicode escape
StringBuilder unicodeEscaped = new StringBuilder();
for (int i = 0; i < decoded.length(); i++) {
char ch = decoded.charAt(i);
if (ch <= 127 && Character.isLetterOrDigit(ch)) {
// Keep ASCII alphanumerics as-is
unicodeEscaped.append(ch);
} else {
// Escape non-ASCII characters
unicodeEscaped.append(String.format("\\u%04X", (int) ch));
}
}
return unicodeEscaped.toString();
}
@Override @Override
public Answer cloneVolumeFromBaseTemplate(final CopyCommand cmd) { public Answer cloneVolumeFromBaseTemplate(final CopyCommand cmd) {
final Connection conn = hypervisorResource.getConnection(); final Connection conn = hypervisorResource.getConnection();

View File

@ -95,8 +95,8 @@ public class CephObjectStoreDriverImpl extends BaseObjectStoreDriverImpl {
AmazonS3 s3client = getS3Client(storeId, accountId); AmazonS3 s3client = getS3Client(storeId, accountId);
try { try {
if (s3client.getBucketAcl(bucketName) != null) { if (s3client.doesBucketExistV2(bucketName)) {
throw new CloudRuntimeException("Bucket already exists with name " + bucketName); throw new CloudRuntimeException("Bucket already exists with the name: " + bucketName);
} }
} catch (AmazonS3Exception e) { } catch (AmazonS3Exception e) {
if (e.getStatusCode() != 404) { if (e.getStatusCode() != 404) {
@ -221,9 +221,11 @@ public class CephObjectStoreDriverImpl extends BaseObjectStoreDriverImpl {
if (user.isPresent()) { if (user.isPresent()) {
logger.info("User already exists in Ceph RGW: " + username); logger.info("User already exists in Ceph RGW: " + username);
return true; return true;
} else {
logger.debug("User does not exist. Creating user in Ceph RGW: " + username);
} }
} catch (Exception e) { } catch (Exception e) {
logger.debug("User does not exist. Creating user in Ceph RGW: " + username); logger.debug("Get user info failed for user {} with exception {}. Proceeding with user creation.", username, e.getMessage());
} }
try { try {
@ -348,7 +350,7 @@ public class CephObjectStoreDriverImpl extends BaseObjectStoreDriverImpl {
new AWSStaticCredentialsProvider( new AWSStaticCredentialsProvider(
new BasicAWSCredentials(accessKey, secretKey))) new BasicAWSCredentials(accessKey, secretKey)))
.withEndpointConfiguration( .withEndpointConfiguration(
new AwsClientBuilder.EndpointConfiguration(url, "auto")) new AwsClientBuilder.EndpointConfiguration(url, null))
.build(); .build();
if (client == null) { if (client == null) {

View File

@ -96,7 +96,7 @@ public class CephObjectStoreDriverImplTest {
when(bucketDao.findById(anyLong())).thenReturn(new BucketVO(bucket.getName())); when(bucketDao.findById(anyLong())).thenReturn(new BucketVO(bucket.getName()));
Bucket bucketRet = cephObjectStoreDriverImpl.createBucket(bucket, false); Bucket bucketRet = cephObjectStoreDriverImpl.createBucket(bucket, false);
assertEquals(bucketRet.getName(), bucket.getName()); assertEquals(bucketRet.getName(), bucket.getName());
verify(rgwClient, times(1)).getBucketAcl(anyString()); verify(rgwClient, times(1)).doesBucketExistV2(anyString());
verify(rgwClient, times(1)).createBucket(anyString()); verify(rgwClient, times(1)).createBucket(anyString());
} }

View File

@ -41,7 +41,7 @@ class networkConfig:
return devs return devs
@staticmethod @staticmethod
def getDefaultNetwork(): def getDefaultNetwork():
cmd = bash("route -n|awk \'/^0.0.0.0/ {print $2,$8}\'") cmd = bash("ip route show default | awk \'{print $3,$5}\'")
if not cmd.isSuccess(): if not cmd.isSuccess():
logging.debug("Failed to get default route") logging.debug("Failed to get default route")
raise CloudRuntimeException("Failed to get default route") raise CloudRuntimeException("Failed to get default route")

View File

@ -229,11 +229,6 @@ public class AdvancedNetworkTopology extends BasicNetworkTopology {
public boolean applyNetworkACLs(final Network network, final List<? extends NetworkACLItem> rules, final VirtualRouter router, final boolean isPrivateGateway) public boolean applyNetworkACLs(final Network network, final List<? extends NetworkACLItem> rules, final VirtualRouter router, final boolean isPrivateGateway)
throws ResourceUnavailableException { throws ResourceUnavailableException {
if (rules == null || rules.isEmpty()) {
logger.debug("No network ACLs to be applied for network {}", network);
return true;
}
logger.debug("APPLYING NETWORK ACLs RULES"); logger.debug("APPLYING NETWORK ACLs RULES");
final String typeString = "network acls"; final String typeString = "network acls";

View File

@ -17,6 +17,11 @@
package org.apache.cloudstack.resource; package org.apache.cloudstack.resource;
import java.lang.reflect.Field; import java.lang.reflect.Field;
import java.time.Duration;
import java.time.Instant;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.time.zone.ZoneRules;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Calendar; import java.util.Calendar;
import java.util.Date; import java.util.Date;
@ -629,9 +634,23 @@ public class ResourceCleanupServiceImplTest {
Date result = resourceCleanupService.calculatePastDateFromConfig( Date result = resourceCleanupService.calculatePastDateFromConfig(
ResourceCleanupService.ExpungedResourcesPurgeKeepPastDays.key(), ResourceCleanupService.ExpungedResourcesPurgeKeepPastDays.key(),
days); days);
Date today = new Date(); Instant resultInstant = result.toInstant();
long diff = today.getTime() - result.getTime(); ZoneId systemZone = ZoneId.systemDefault();
Assert.assertEquals(days, TimeUnit.DAYS.convert(diff, TimeUnit.MILLISECONDS)); ZoneRules rules = systemZone.getRules();
ZonedDateTime resultDateTime = resultInstant.atZone(systemZone);
boolean isDSTInResultDateTime = rules.isDaylightSavings(resultDateTime.toInstant());
ZonedDateTime todayDateTime = ZonedDateTime.now(systemZone);
boolean isDSTInTodayDateTime = rules.isDaylightSavings(todayDateTime.toInstant());
Duration duration = Duration.between(resultDateTime, todayDateTime);
long actualDays = TimeUnit.DAYS.convert(duration.toMillis(), TimeUnit.MILLISECONDS);
if (!isDSTInResultDateTime && isDSTInTodayDateTime) {
Assert.assertEquals(days - 1, actualDays);
} else {
Assert.assertEquals(days, actualDays);
}
} }
@Test @Test

View File

@ -32,7 +32,12 @@ export default {
}, },
params: { type: 'routing' }, params: { type: 'routing' },
columns: () => { columns: () => {
const fields = ['name', 'state', 'resourcestate', 'ipaddress', 'arch', 'hypervisor', 'instances', 'powerstate', 'version'] const fields = [
'name', 'state', 'resourcestate', 'ipaddress',
'arch', 'hypervisor', 'instances',
{ field: 'systeminstances', customTitle: 'system.vms' },
'powerstate', 'version'
]
const metricsFields = ['cpunumber', 'cputotalghz', 'cpuusedghz', 'cpuallocatedghz', 'memorytotalgb', 'memoryusedgb', 'memoryallocatedgb', 'networkread', 'networkwrite'] const metricsFields = ['cpunumber', 'cputotalghz', 'cpuusedghz', 'cpuallocatedghz', 'memorytotalgb', 'memoryusedgb', 'memoryallocatedgb', 'networkread', 'networkwrite']
if (store.getters.metrics) { if (store.getters.metrics) {
fields.push(...metricsFields) fields.push(...metricsFields)

View File

@ -185,7 +185,6 @@ export default {
isdynamicallyscalable: this.resource.isdynamicallyscalable, isdynamicallyscalable: this.resource.isdynamicallyscalable,
deleteprotection: this.resource.deleteprotection, deleteprotection: this.resource.deleteprotection,
group: this.resource.group, group: this.resource.group,
securitygroupids: this.resource.securitygroup.map(x => x.id),
userdata: '', userdata: '',
haenable: this.resource.haenable haenable: this.resource.haenable
}) })

View File

@ -88,6 +88,15 @@
<a-switch v-model:checked="form.directdownload"/> <a-switch v-model:checked="form.directdownload"/>
</a-form-item> </a-form-item>
<a-form-item ref="checksum" name="checksum">
<template #label>
<tooltip-label :title="$t('label.checksum')" :tooltip="apiParams.checksum.description"/>
</template>
<a-input
v-model:value="form.checksum"
:placeholder="apiParams.checksum.description" />
</a-form-item>
<a-form-item ref="zoneid" name="zoneid"> <a-form-item ref="zoneid" name="zoneid">
<template #label> <template #label>
<tooltip-label :title="$t('label.zoneid')" :tooltip="apiParams.zoneid.description"/> <tooltip-label :title="$t('label.zoneid')" :tooltip="apiParams.zoneid.description"/>

View File

@ -214,8 +214,8 @@
</a-form-item> </a-form-item>
</a-col> </a-col>
</a-row> </a-row>
<a-row :gutter="12" v-if="(hyperKVMShow || hyperCustomShow) && currentForm === 'Create'"> <a-row :gutter="12">
<a-col :md="24" :lg="12"> <a-col :md="24" :lg="12" v-if="(hyperKVMShow || hyperCustomShow) && currentForm === 'Create'">
<a-form-item ref="directdownload" name="directdownload"> <a-form-item ref="directdownload" name="directdownload">
<template #label> <template #label>
<tooltip-label :title="$t('label.directdownload')" :tooltip="apiParams.directdownload.description"/> <tooltip-label :title="$t('label.directdownload')" :tooltip="apiParams.directdownload.description"/>
@ -223,7 +223,7 @@
<a-switch v-model:checked="form.directdownload" @change="handleChangeDirect" /> <a-switch v-model:checked="form.directdownload" @change="handleChangeDirect" />
</a-form-item> </a-form-item>
</a-col> </a-col>
<a-col :md="24" :lg="12" v-if="allowDirectDownload"> <a-col :md="24" :lg="12">
<a-form-item ref="checksum" name="checksum"> <a-form-item ref="checksum" name="checksum">
<template #label> <template #label>
<tooltip-label :title="$t('label.checksum')" :tooltip="apiParams.checksum.description"/> <tooltip-label :title="$t('label.checksum')" :tooltip="apiParams.checksum.description"/>

View File

@ -247,7 +247,6 @@ public abstract class NioConnection implements Callable<Boolean> {
return true; return true;
} }
protected void accept(final SelectionKey key) throws IOException { protected void accept(final SelectionKey key) throws IOException {
final ServerSocketChannel serverSocketChannel = (ServerSocketChannel)key.channel(); final ServerSocketChannel serverSocketChannel = (ServerSocketChannel)key.channel();
final SocketChannel socketChannel = serverSocketChannel.accept(); final SocketChannel socketChannel = serverSocketChannel.accept();