From 620d389d1717b26b09dcdf8893a0749e2d1cd600 Mon Sep 17 00:00:00 2001 From: mprokopchuk Date: Wed, 11 Mar 2026 19:20:47 +0100 Subject: [PATCH 1/2] There is a set of TO classes with renamed fields, which makes impossible to correctly communicate between Management Servers and Agents --- .../transport/compat/AbstractTOAdaptor.java | 127 ++++++++++++++++++ .../agent/transport/compat/DiskTOAdaptor.java | 28 ++++ .../compat/MigrateCommandAdaptor.java | 28 ++++ .../transport/compat/NetworkTOAdaptor.java | 28 ++++ .../compat/VirtualMachineTOAdaptor.java | 29 ++++ .../java/com/cloud/serializer/GsonHelper.java | 13 ++ 6 files changed, 253 insertions(+) create mode 100644 core/src/main/java/com/cloud/agent/transport/compat/AbstractTOAdaptor.java create mode 100644 core/src/main/java/com/cloud/agent/transport/compat/DiskTOAdaptor.java create mode 100644 core/src/main/java/com/cloud/agent/transport/compat/MigrateCommandAdaptor.java create mode 100644 core/src/main/java/com/cloud/agent/transport/compat/NetworkTOAdaptor.java create mode 100644 core/src/main/java/com/cloud/agent/transport/compat/VirtualMachineTOAdaptor.java diff --git a/core/src/main/java/com/cloud/agent/transport/compat/AbstractTOAdaptor.java b/core/src/main/java/com/cloud/agent/transport/compat/AbstractTOAdaptor.java new file mode 100644 index 000000000000..558b249e4756 --- /dev/null +++ b/core/src/main/java/com/cloud/agent/transport/compat/AbstractTOAdaptor.java @@ -0,0 +1,127 @@ +// 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 com.cloud.agent.transport.compat; + +import com.cloud.agent.api.Answer; +import com.cloud.agent.api.Command; +import com.cloud.agent.api.SecStorageFirewallCfgCommand; +import com.cloud.agent.api.to.DataStoreTO; +import com.cloud.agent.api.to.DataTO; +import com.cloud.agent.transport.ArrayTypeAdaptor; +import com.cloud.agent.transport.InterfaceTypeAdaptor; +import com.cloud.agent.transport.LoggingExclusionStrategy; +import com.cloud.agent.transport.Request; +import com.cloud.agent.transport.StoragePoolTypeAdaptor; +import com.cloud.hypervisor.Hypervisor; +import com.cloud.storage.Storage; +import com.cloud.utils.Pair; +import com.cloud.utils.StringUtils; +import com.cloud.utils.exception.CloudRuntimeException; +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.google.gson.JsonSerializationContext; +import com.google.gson.JsonSerializer; +import com.google.gson.reflect.TypeToken; +import org.apache.cloudstack.transport.HypervisorTypeAdaptor; +import org.apache.log4j.Logger; + +import java.lang.reflect.Type; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + +/** + * JSON serializer adapter for transport classes (com.cloud.agent.api.to.*) that ensures backward compatibility + * with older Agent versions due to rename of the fields + * (see https://github.com/shapeblue/cloudstack-apple/pull/532/changes) + */ + +public class AbstractTOAdaptor implements JsonSerializer { + private static final Logger s_logger = Logger.getLogger(AbstractTOAdaptor.class); + private static final Gson gson; + + static { + GsonBuilder gsonBuilder = new GsonBuilder(); + gson = setDefaultGsonConfig(gsonBuilder); + GsonBuilder loggerBuilder = new GsonBuilder(); + loggerBuilder.disableHtmlEscaping(); + loggerBuilder.setExclusionStrategies(new LoggingExclusionStrategy(s_logger)); + } + + private Map fieldMappings; + + protected AbstractTOAdaptor(String... fields) { + this.fieldMappings = new LinkedHashMap<>(); + for (int i = 0; i + 1 < fields.length; i += 2) { + String sourceField = fields[i]; + String destinationField = fields[i + 1]; + // skip empty fields + if (StringUtils.isBlank(sourceField) || StringUtils.isBlank(destinationField)) { + continue; + } + this.fieldMappings.put(sourceField, destinationField); + } + if (this.fieldMappings.isEmpty()) { + throw new CloudRuntimeException("Field mappings must not be empty"); + } + } + + private static Gson setDefaultGsonConfig(GsonBuilder builder) { + builder.setVersion(1.5); + InterfaceTypeAdaptor dsAdaptor = new InterfaceTypeAdaptor(); + builder.registerTypeAdapter(DataStoreTO.class, dsAdaptor); + InterfaceTypeAdaptor dtAdaptor = new InterfaceTypeAdaptor(); + builder.registerTypeAdapter(DataTO.class, dtAdaptor); + ArrayTypeAdaptor cmdAdaptor = new ArrayTypeAdaptor(); + builder.registerTypeAdapter(Command[].class, cmdAdaptor); + ArrayTypeAdaptor ansAdaptor = new ArrayTypeAdaptor(); + builder.registerTypeAdapter(Answer[].class, ansAdaptor); + builder.registerTypeAdapter(new TypeToken>() { + }.getType(), new Request.PortConfigListTypeAdaptor()); + builder.registerTypeAdapter(new TypeToken>() { + }.getType(), new Request.NwGroupsCommandTypeAdaptor()); + builder.registerTypeAdapter(Storage.StoragePoolType.class, new StoragePoolTypeAdaptor()); + builder.registerTypeAdapter(Hypervisor.HypervisorType.class, new HypervisorTypeAdaptor()); + + Gson gson = builder.create(); + dsAdaptor.initGson(gson); + dtAdaptor.initGson(gson); + cmdAdaptor.initGson(gson); + ansAdaptor.initGson(gson); + return gson; + } + + @Override + public JsonElement serialize(T src, Type typeOfSrc, JsonSerializationContext context) { + if (src == null) { + return null; + } + JsonObject obj = gson.toJsonTree(src).getAsJsonObject(); + if (obj != null) { + for (Map.Entry field : fieldMappings.entrySet()) { + String sourceField = field.getKey(); + String destinationField = field.getValue(); + if (obj.has(sourceField)) { + obj.add(destinationField, obj.get(sourceField)); + } + } + } + return obj; + } +} diff --git a/core/src/main/java/com/cloud/agent/transport/compat/DiskTOAdaptor.java b/core/src/main/java/com/cloud/agent/transport/compat/DiskTOAdaptor.java new file mode 100644 index 000000000000..6b45529de05f --- /dev/null +++ b/core/src/main/java/com/cloud/agent/transport/compat/DiskTOAdaptor.java @@ -0,0 +1,28 @@ +// 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 com.cloud.agent.transport.compat; + +import com.cloud.agent.api.to.DiskTO; + +/** + * See {@link AbstractTOAdaptor}. + */ +public class DiskTOAdaptor extends AbstractTOAdaptor { + public DiskTOAdaptor() { + super("details", "_details"); + } +} diff --git a/core/src/main/java/com/cloud/agent/transport/compat/MigrateCommandAdaptor.java b/core/src/main/java/com/cloud/agent/transport/compat/MigrateCommandAdaptor.java new file mode 100644 index 000000000000..ddac8afe5975 --- /dev/null +++ b/core/src/main/java/com/cloud/agent/transport/compat/MigrateCommandAdaptor.java @@ -0,0 +1,28 @@ +// 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 com.cloud.agent.transport.compat; + +import com.cloud.agent.api.MigrateCommand; + +/** + * See {@link AbstractTOAdaptor}. + */ +public class MigrateCommandAdaptor extends AbstractTOAdaptor { + public MigrateCommandAdaptor() { + super("destinationIp", "destIp", "windows", "isWindows", "virtualMachine", "vmTO"); + } +} diff --git a/core/src/main/java/com/cloud/agent/transport/compat/NetworkTOAdaptor.java b/core/src/main/java/com/cloud/agent/transport/compat/NetworkTOAdaptor.java new file mode 100644 index 000000000000..01b0cdb4cdf9 --- /dev/null +++ b/core/src/main/java/com/cloud/agent/transport/compat/NetworkTOAdaptor.java @@ -0,0 +1,28 @@ +// 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 com.cloud.agent.transport.compat; + +import com.cloud.agent.api.to.NetworkTO; + +/** + * See {@link AbstractTOAdaptor}. + */ +public class NetworkTOAdaptor extends AbstractTOAdaptor { + public NetworkTOAdaptor() { + super("securityGroupEnabled", "isSecurityGroupEnabled"); + } +} diff --git a/core/src/main/java/com/cloud/agent/transport/compat/VirtualMachineTOAdaptor.java b/core/src/main/java/com/cloud/agent/transport/compat/VirtualMachineTOAdaptor.java new file mode 100644 index 000000000000..24a8c02a59c5 --- /dev/null +++ b/core/src/main/java/com/cloud/agent/transport/compat/VirtualMachineTOAdaptor.java @@ -0,0 +1,29 @@ +// 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 com.cloud.agent.transport.compat; + +import com.cloud.agent.api.to.VirtualMachineTO; + +/** + * See {@link AbstractTOAdaptor}. + */ +public class VirtualMachineTOAdaptor extends AbstractTOAdaptor { + + public VirtualMachineTOAdaptor() { + super("details", "params"); + } +} diff --git a/core/src/main/java/com/cloud/serializer/GsonHelper.java b/core/src/main/java/com/cloud/serializer/GsonHelper.java index 7de98c08b7e0..2cc247cc10dc 100644 --- a/core/src/main/java/com/cloud/serializer/GsonHelper.java +++ b/core/src/main/java/com/cloud/serializer/GsonHelper.java @@ -21,6 +21,14 @@ import java.util.List; +import com.cloud.agent.api.MigrateCommand; +import com.cloud.agent.api.to.DiskTO; +import com.cloud.agent.api.to.NetworkTO; +import com.cloud.agent.api.to.VirtualMachineTO; +import com.cloud.agent.transport.compat.DiskTOAdaptor; +import com.cloud.agent.transport.compat.MigrateCommandAdaptor; +import com.cloud.agent.transport.compat.NetworkTOAdaptor; +import com.cloud.agent.transport.compat.VirtualMachineTOAdaptor; import com.cloud.hypervisor.Hypervisor; import org.apache.cloudstack.transport.HypervisorTypeAdaptor; import org.apache.logging.log4j.Logger; @@ -78,6 +86,11 @@ public static Gson setDefaultGsonConfig(GsonBuilder builder) { }.getType(), new NwGroupsCommandTypeAdaptor()); builder.registerTypeAdapter(Storage.StoragePoolType.class, new StoragePoolTypeAdaptor()); builder.registerTypeAdapter(Hypervisor.HypervisorType.class, new HypervisorTypeAdaptor()); + // added for compatibility purposes, remove after all Agents migrate to the new version + builder.registerTypeAdapter(VirtualMachineTO.class, new VirtualMachineTOAdaptor()); + builder.registerTypeAdapter(DiskTO.class, new DiskTOAdaptor()); + builder.registerTypeAdapter(NetworkTO.class, new NetworkTOAdaptor()); + builder.registerTypeAdapter(MigrateCommand.class, new MigrateCommandAdaptor()); Gson gson = builder.create(); dsAdaptor.initGson(gson); dtAdaptor.initGson(gson); From e8c2037d41bbbe0e304be5ff1b67d6b45347525d Mon Sep 17 00:00:00 2001 From: Wei Zhou Date: Wed, 15 Jul 2026 16:47:38 +0200 Subject: [PATCH 2/2] fix logger --- .../cloud/agent/transport/compat/AbstractTOAdaptor.java | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/core/src/main/java/com/cloud/agent/transport/compat/AbstractTOAdaptor.java b/core/src/main/java/com/cloud/agent/transport/compat/AbstractTOAdaptor.java index 558b249e4756..9dc448bf4368 100644 --- a/core/src/main/java/com/cloud/agent/transport/compat/AbstractTOAdaptor.java +++ b/core/src/main/java/com/cloud/agent/transport/compat/AbstractTOAdaptor.java @@ -39,7 +39,8 @@ import com.google.gson.JsonSerializer; import com.google.gson.reflect.TypeToken; import org.apache.cloudstack.transport.HypervisorTypeAdaptor; -import org.apache.log4j.Logger; +import org.apache.logging.log4j.Logger; +import org.apache.logging.log4j.LogManager; import java.lang.reflect.Type; import java.util.LinkedHashMap; @@ -53,7 +54,7 @@ */ public class AbstractTOAdaptor implements JsonSerializer { - private static final Logger s_logger = Logger.getLogger(AbstractTOAdaptor.class); + private static final Logger LOGGER = LogManager.getLogger(AbstractTOAdaptor.class); private static final Gson gson; static { @@ -61,7 +62,7 @@ public class AbstractTOAdaptor implements JsonSerializer { gson = setDefaultGsonConfig(gsonBuilder); GsonBuilder loggerBuilder = new GsonBuilder(); loggerBuilder.disableHtmlEscaping(); - loggerBuilder.setExclusionStrategies(new LoggingExclusionStrategy(s_logger)); + loggerBuilder.setExclusionStrategies(new LoggingExclusionStrategy(LOGGER)); } private Map fieldMappings;