From 3c2a32755963557f4a0066c93cc8c44b64bf6d99 Mon Sep 17 00:00:00 2001 From: "Michael N. Lipp" Date: Mon, 14 Apr 2025 14:05:23 +0200 Subject: [PATCH 01/12] Necessary changes. Still not working due to https://github.com/kubernetes-client/java/issues/3143. --- org.jdrupes.vmoperator.common/build.gradle | 2 +- .../common/DynamicTypeAdapterFactory.java | 17 +++++----- .../org/jdrupes/vmoperator/common/K8s.java | 7 +++-- .../jdrupes/vmoperator/common/K8sClient.java | 31 ++++++++++++------- .../common/K8sClusterGenericStub.java | 1 + .../common/K8sDynamicModelsBase.java | 6 ++-- .../vmoperator/common/K8sDynamicStub.java | 5 +-- .../vmoperator/common/K8sDynamicStubBase.java | 2 +- .../vmoperator/common/K8sGenericStub.java | 4 ++- .../vmoperator/common/VmDefinition.java | 2 +- .../vmoperator/common/VmDefinitionStub.java | 5 +-- .../manager/LoadBalancerReconciler.java | 3 +- .../vmoperator/manager/PoolMonitor.java | 3 +- .../vmoperator/manager/PvcReconciler.java | 10 +++--- .../vmoperator/runner/qemu/StatusUpdater.java | 2 +- .../vmoperator/runner/qemu/VmDefUpdater.java | 4 +-- 16 files changed, 56 insertions(+), 48 deletions(-) diff --git a/org.jdrupes.vmoperator.common/build.gradle b/org.jdrupes.vmoperator.common/build.gradle index e72cb14..c9f1413 100644 --- a/org.jdrupes.vmoperator.common/build.gradle +++ b/org.jdrupes.vmoperator.common/build.gradle @@ -11,7 +11,7 @@ plugins { dependencies { api project(':org.jdrupes.vmoperator.util') api 'org.jgrapes:org.jgrapes.core:[1.22.1,2)' - api 'io.kubernetes:client-java:[19.0.0,20.0.0)' + api 'io.kubernetes:client-java:[23.0.0,24.0.0)' api 'org.yaml:snakeyaml' api 'com.fasterxml.jackson.datatype:jackson-datatype-jsr310:[2.16.1,3]' } diff --git a/org.jdrupes.vmoperator.common/src/org/jdrupes/vmoperator/common/DynamicTypeAdapterFactory.java b/org.jdrupes.vmoperator.common/src/org/jdrupes/vmoperator/common/DynamicTypeAdapterFactory.java index d21eed4..9625b98 100644 --- a/org.jdrupes.vmoperator.common/src/org/jdrupes/vmoperator/common/DynamicTypeAdapterFactory.java +++ b/org.jdrupes.vmoperator.common/src/org/jdrupes/vmoperator/common/DynamicTypeAdapterFactory.java @@ -26,7 +26,7 @@ import com.google.gson.TypeAdapterFactory; import com.google.gson.reflect.TypeToken; import com.google.gson.stream.JsonReader; import com.google.gson.stream.JsonWriter; -import io.kubernetes.client.openapi.ApiClient; +import io.kubernetes.client.openapi.JSON; import java.io.IOException; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Type; @@ -45,18 +45,15 @@ public class DynamicTypeAdapterFactory queryParams, - List collectionQueryParams, Object body, - Map headerParams, Map cookieParams, - Map formParams, String[] authNames, + public Call buildCall(String baseUrl, String path, String method, + List queryParams, List collectionQueryParams, + Object body, Map headerParams, + Map cookieParams, Map formParams, + String[] authNames, ApiCallback callback) throws ApiException { - return apiClient().buildCall(path, method, queryParams, + return apiClient().buildCall(baseUrl, path, method, queryParams, collectionQueryParams, body, headerParams, cookieParams, formParams, authNames, callback); } @@ -833,6 +836,7 @@ public class K8sClient extends ApiClient { /** * Builds the request. * + * @param baseUrl the base url * @param path the path * @param method the method * @param queryParams the query params @@ -849,12 +853,12 @@ public class K8sClient extends ApiClient { */ @SuppressWarnings({ "rawtypes", "PMD.ExcessiveParameterList" }) @Override - public Request buildRequest(String path, String method, + public Request buildRequest(String baseUrl, String path, String method, List queryParams, List collectionQueryParams, Object body, Map headerParams, Map cookieParams, Map formParams, String[] authNames, ApiCallback callback) throws ApiException { - return apiClient().buildRequest(path, method, queryParams, + return apiClient().buildRequest(baseUrl, path, method, queryParams, collectionQueryParams, body, headerParams, cookieParams, formParams, authNames, callback); } @@ -862,6 +866,7 @@ public class K8sClient extends ApiClient { /** * Builds the url. * + * @param baseUrl the base url * @param path the path * @param queryParams the query params * @param collectionQueryParams the collection query params @@ -869,9 +874,10 @@ public class K8sClient extends ApiClient { * @see ApiClient#buildUrl(java.lang.String, java.util.List, java.util.List) */ @Override - public String buildUrl(String path, List queryParams, + public String buildUrl(String baseUrl, String path, List queryParams, List collectionQueryParams) { - return apiClient().buildUrl(path, queryParams, collectionQueryParams); + return apiClient().buildUrl(baseUrl, path, queryParams, + collectionQueryParams); } /** @@ -907,14 +913,15 @@ public class K8sClient extends ApiClient { * @param queryParams the query params * @param headerParams the header params * @param cookieParams the cookie params + * @throws ApiException * @see ApiClient#updateParamsForAuth(java.lang.String[], java.util.List, java.util.Map, java.util.Map) */ @Override public void updateParamsForAuth(String[] authNames, List queryParams, - Map headerParams, - Map cookieParams) { + Map headerParams, Map cookieParams, + String payload, String method, URI uri) throws ApiException { apiClient().updateParamsForAuth(authNames, queryParams, headerParams, - cookieParams); + cookieParams, payload, method, uri); } /** diff --git a/org.jdrupes.vmoperator.common/src/org/jdrupes/vmoperator/common/K8sClusterGenericStub.java b/org.jdrupes.vmoperator.common/src/org/jdrupes/vmoperator/common/K8sClusterGenericStub.java index af87af2..6b5fd7e 100644 --- a/org.jdrupes.vmoperator.common/src/org/jdrupes/vmoperator/common/K8sClusterGenericStub.java +++ b/org.jdrupes.vmoperator.common/src/org/jdrupes/vmoperator/common/K8sClusterGenericStub.java @@ -240,6 +240,7 @@ public class K8sClusterGenericStub the object list type * @param the result type */ + @FunctionalInterface public interface GenericSupplier> { diff --git a/org.jdrupes.vmoperator.common/src/org/jdrupes/vmoperator/common/K8sDynamicModelsBase.java b/org.jdrupes.vmoperator.common/src/org/jdrupes/vmoperator/common/K8sDynamicModelsBase.java index 1813621..61c17be 100644 --- a/org.jdrupes.vmoperator.common/src/org/jdrupes/vmoperator/common/K8sDynamicModelsBase.java +++ b/org.jdrupes.vmoperator.common/src/org/jdrupes/vmoperator/common/K8sDynamicModelsBase.java @@ -22,7 +22,7 @@ import com.google.gson.Gson; import com.google.gson.JsonElement; import com.google.gson.JsonObject; import io.kubernetes.client.common.KubernetesListObject; -import io.kubernetes.client.openapi.Configuration; +import io.kubernetes.client.openapi.JSON; import io.kubernetes.client.openapi.models.V1ListMeta; import java.lang.reflect.InvocationTargetException; import java.util.ArrayList; @@ -147,9 +147,7 @@ public class K8sDynamicModelsBase * @param objectMeta the new metadata */ public void setMetadata(V1ListMeta objectMeta) { - data.add("metadata", - Configuration.getDefaultApiClient().getJSON().getGson() - .toJsonTree(objectMeta)); + data.add("metadata", JSON.getGson().toJsonTree(objectMeta)); } @Override diff --git a/org.jdrupes.vmoperator.common/src/org/jdrupes/vmoperator/common/K8sDynamicStub.java b/org.jdrupes.vmoperator.common/src/org/jdrupes/vmoperator/common/K8sDynamicStub.java index afed802..42658d0 100644 --- a/org.jdrupes.vmoperator.common/src/org/jdrupes/vmoperator/common/K8sDynamicStub.java +++ b/org.jdrupes.vmoperator.common/src/org/jdrupes/vmoperator/common/K8sDynamicStub.java @@ -21,6 +21,7 @@ package org.jdrupes.vmoperator.common; import io.kubernetes.client.Discovery.APIResource; import io.kubernetes.client.apimachinery.GroupVersionKind; import io.kubernetes.client.openapi.ApiException; +import io.kubernetes.client.openapi.JSON; import io.kubernetes.client.util.generic.options.ListOptions; import java.io.Reader; import java.util.Collection; @@ -101,8 +102,8 @@ public class K8sDynamicStub */ public static K8sDynamicStub createFromYaml(K8sClient client, APIResource context, Reader yaml) throws ApiException { - var model = new K8sDynamicModel(client.getJSON().getGson(), - K8s.yamlToJson(client, yaml)); + var model + = new K8sDynamicModel(JSON.getGson(), K8s.yamlToJson(client, yaml)); return K8sGenericStub.create(K8sDynamicModel.class, K8sDynamicModels.class, client, context, model, (c, ns, n) -> new K8sDynamicStub(c, context, ns, n)); diff --git a/org.jdrupes.vmoperator.common/src/org/jdrupes/vmoperator/common/K8sDynamicStubBase.java b/org.jdrupes.vmoperator.common/src/org/jdrupes/vmoperator/common/K8sDynamicStubBase.java index 44f419c..1d39847 100644 --- a/org.jdrupes.vmoperator.common/src/org/jdrupes/vmoperator/common/K8sDynamicStubBase.java +++ b/org.jdrupes.vmoperator.common/src/org/jdrupes/vmoperator/common/K8sDynamicStubBase.java @@ -46,6 +46,6 @@ public abstract class K8sDynamicStubBase the object list type * @param the result type */ + @FunctionalInterface public interface GenericSupplier> { diff --git a/org.jdrupes.vmoperator.common/src/org/jdrupes/vmoperator/common/VmDefinition.java b/org.jdrupes.vmoperator.common/src/org/jdrupes/vmoperator/common/VmDefinition.java index 0a25dd6..2d5e2fe 100644 --- a/org.jdrupes.vmoperator.common/src/org/jdrupes/vmoperator/common/VmDefinition.java +++ b/org.jdrupes.vmoperator.common/src/org/jdrupes/vmoperator/common/VmDefinition.java @@ -54,7 +54,7 @@ public class VmDefinition extends K8sDynamicModel { private static final Logger logger = Logger.getLogger(VmDefinition.class.getName()); @SuppressWarnings("PMD.FieldNamingConventions") - private static final Gson gson = new JSON().getGson(); + private static final Gson gson = JSON.getGson(); @SuppressWarnings("PMD.FieldNamingConventions") private static final ObjectMapper objectMapper = new ObjectMapper().registerModule(new JavaTimeModule()); diff --git a/org.jdrupes.vmoperator.common/src/org/jdrupes/vmoperator/common/VmDefinitionStub.java b/org.jdrupes.vmoperator.common/src/org/jdrupes/vmoperator/common/VmDefinitionStub.java index 72194da..57cd210 100644 --- a/org.jdrupes.vmoperator.common/src/org/jdrupes/vmoperator/common/VmDefinitionStub.java +++ b/org.jdrupes.vmoperator.common/src/org/jdrupes/vmoperator/common/VmDefinitionStub.java @@ -21,6 +21,7 @@ package org.jdrupes.vmoperator.common; import io.kubernetes.client.Discovery.APIResource; import io.kubernetes.client.apimachinery.GroupVersionKind; import io.kubernetes.client.openapi.ApiException; +import io.kubernetes.client.openapi.JSON; import io.kubernetes.client.util.generic.options.ListOptions; import java.io.Reader; import java.util.Collection; @@ -101,8 +102,8 @@ public class VmDefinitionStub */ public static VmDefinitionStub createFromYaml(K8sClient client, APIResource context, Reader yaml) throws ApiException { - var model = new VmDefinition(client.getJSON().getGson(), - K8s.yamlToJson(client, yaml)); + var model + = new VmDefinition(JSON.getGson(), K8s.yamlToJson(client, yaml)); return K8sGenericStub.create(VmDefinition.class, VmDefinitions.class, client, context, model, (c, ns, n) -> new VmDefinitionStub(c, context, ns, n)); diff --git a/org.jdrupes.vmoperator.manager/src/org/jdrupes/vmoperator/manager/LoadBalancerReconciler.java b/org.jdrupes.vmoperator.manager/src/org/jdrupes/vmoperator/manager/LoadBalancerReconciler.java index d190cef..b04a35f 100644 --- a/org.jdrupes.vmoperator.manager/src/org/jdrupes/vmoperator/manager/LoadBalancerReconciler.java +++ b/org.jdrupes.vmoperator.manager/src/org/jdrupes/vmoperator/manager/LoadBalancerReconciler.java @@ -22,6 +22,7 @@ import com.google.gson.Gson; import freemarker.template.Configuration; import freemarker.template.TemplateException; import io.kubernetes.client.openapi.ApiException; +import io.kubernetes.client.openapi.JSON; import io.kubernetes.client.openapi.models.V1APIService; import io.kubernetes.client.openapi.models.V1ObjectMeta; import io.kubernetes.client.util.generic.dynamic.DynamicKubernetesObject; @@ -122,7 +123,7 @@ import org.yaml.snakeyaml.constructor.SafeConstructor; ? (Map>) lbsDef : null; var client = channel.client(); - mergeMetadata(client.getJSON().getGson(), svcDef, defaults, vmDef); + mergeMetadata(JSON.getGson(), svcDef, defaults, vmDef); // Apply var svcStub = K8sV1ServiceStub diff --git a/org.jdrupes.vmoperator.manager/src/org/jdrupes/vmoperator/manager/PoolMonitor.java b/org.jdrupes.vmoperator.manager/src/org/jdrupes/vmoperator/manager/PoolMonitor.java index 1bc323c..6c9a823 100644 --- a/org.jdrupes.vmoperator.manager/src/org/jdrupes/vmoperator/manager/PoolMonitor.java +++ b/org.jdrupes.vmoperator.manager/src/org/jdrupes/vmoperator/manager/PoolMonitor.java @@ -21,6 +21,7 @@ package org.jdrupes.vmoperator.manager; import com.google.gson.JsonObject; import io.kubernetes.client.apimachinery.GroupVersionKind; import io.kubernetes.client.openapi.ApiException; +import io.kubernetes.client.openapi.JSON; import io.kubernetes.client.util.Watch; import java.io.IOException; import java.util.Collections; @@ -130,7 +131,7 @@ public class PoolMonitor extends // Get pool and merge changes var vmPool = pools.computeIfAbsent(poolName, k -> new VmPool(poolName)); - vmPool.defineFrom(client().getJSON().getGson().fromJson( + vmPool.defineFrom(JSON.getGson().fromJson( GsonPtr.to(poolModel.data()).to("spec").get(), VmPool.class)); poolPipeline.fire(new VmPoolChanged(vmPool)); } diff --git a/org.jdrupes.vmoperator.manager/src/org/jdrupes/vmoperator/manager/PvcReconciler.java b/org.jdrupes.vmoperator.manager/src/org/jdrupes/vmoperator/manager/PvcReconciler.java index e297183..b0f97fd 100644 --- a/org.jdrupes.vmoperator.manager/src/org/jdrupes/vmoperator/manager/PvcReconciler.java +++ b/org.jdrupes.vmoperator.manager/src/org/jdrupes/vmoperator/manager/PvcReconciler.java @@ -25,6 +25,7 @@ import freemarker.template.TemplateException; import freemarker.template.TemplateNotFoundException; import io.kubernetes.client.custom.V1Patch; import io.kubernetes.client.openapi.ApiException; +import io.kubernetes.client.openapi.JSON; import io.kubernetes.client.util.generic.dynamic.Dynamics; import io.kubernetes.client.util.generic.options.ListOptions; import io.kubernetes.client.util.generic.options.PatchOptions; @@ -160,8 +161,7 @@ import org.yaml.snakeyaml.constructor.SafeConstructor; opts.setForce(true); opts.setFieldManager("kubernetes-java-kubectl-apply"); if (pvcStub.patch(V1Patch.PATCH_FORMAT_APPLY_YAML, - new V1Patch(channel.client().getJSON().serialize(pvcDef)), opts) - .isEmpty()) { + new V1Patch(JSON.serialize(pvcDef)), opts).isEmpty()) { logger.warning( () -> "Could not patch pvc for " + pvcStub.name()); } @@ -203,8 +203,7 @@ import org.yaml.snakeyaml.constructor.SafeConstructor; opts.setForce(true); opts.setFieldManager("kubernetes-java-kubectl-apply"); if (pvcStub.patch(V1Patch.PATCH_FORMAT_APPLY_YAML, - new V1Patch(channel.client().getJSON().serialize(pvcDef)), opts) - .isEmpty()) { + new V1Patch(JSON.serialize(pvcDef)), opts).isEmpty()) { logger.warning( () -> "Could not patch pvc for " + pvcStub.name()); } @@ -218,8 +217,7 @@ import org.yaml.snakeyaml.constructor.SafeConstructor; PatchOptions opts = new PatchOptions(); opts.setFieldManager("kubernetes-java-kubectl-apply"); if (pvcStub.patch(V1Patch.PATCH_FORMAT_JSON_MERGE_PATCH, - new V1Patch(channel.client().getJSON().serialize(pvcDef)), opts) - .isEmpty()) { + new V1Patch(JSON.serialize(pvcDef)), opts).isEmpty()) { logger.warning( () -> "Could not patch pvc for " + pvcStub.name()); } diff --git a/org.jdrupes.vmoperator.runner.qemu/src/org/jdrupes/vmoperator/runner/qemu/StatusUpdater.java b/org.jdrupes.vmoperator.runner.qemu/src/org/jdrupes/vmoperator/runner/qemu/StatusUpdater.java index b5d02c2..f6c7c48 100644 --- a/org.jdrupes.vmoperator.runner.qemu/src/org/jdrupes/vmoperator/runner/qemu/StatusUpdater.java +++ b/org.jdrupes.vmoperator.runner.qemu/src/org/jdrupes/vmoperator/runner/qemu/StatusUpdater.java @@ -71,7 +71,7 @@ import org.jgrapes.core.events.Start; public class StatusUpdater extends VmDefUpdater { @SuppressWarnings("PMD.FieldNamingConventions") - private static final Gson gson = new JSON().getGson(); + private static final Gson gson = JSON.getGson(); @SuppressWarnings("PMD.FieldNamingConventions") private static final ObjectMapper objectMapper = new ObjectMapper().registerModule(new JavaTimeModule()); diff --git a/org.jdrupes.vmoperator.runner.qemu/src/org/jdrupes/vmoperator/runner/qemu/VmDefUpdater.java b/org.jdrupes.vmoperator.runner.qemu/src/org/jdrupes/vmoperator/runner/qemu/VmDefUpdater.java index 49c9e67..a24b766 100644 --- a/org.jdrupes.vmoperator.runner.qemu/src/org/jdrupes/vmoperator/runner/qemu/VmDefUpdater.java +++ b/org.jdrupes.vmoperator.runner.qemu/src/org/jdrupes/vmoperator/runner/qemu/VmDefUpdater.java @@ -19,6 +19,7 @@ package org.jdrupes.vmoperator.runner.qemu; import com.google.gson.JsonObject; +import io.kubernetes.client.openapi.JSON; import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; @@ -161,8 +162,7 @@ public class VmDefUpdater extends Component { : cond) .collect(Collectors.toCollection(() -> new ArrayList<>())); newConds.addAll(toReplace); - status.add("conditions", - apiClient.getJSON().getGson().toJsonTree(newConds)); + status.add("conditions", JSON.getGson().toJsonTree(newConds)); return status; } } From 7d298ce24b91269dcb4cb68af2ae01397339ba10 Mon Sep 17 00:00:00 2001 From: "Michael N. Lipp" Date: Mon, 14 Apr 2025 21:39:35 +0200 Subject: [PATCH 02/12] Clarify intend. --- .../src/org/jdrupes/vmoperator/common/K8sClusterGenericStub.java | 1 + .../src/org/jdrupes/vmoperator/common/K8sGenericStub.java | 1 + 2 files changed, 2 insertions(+) diff --git a/org.jdrupes.vmoperator.common/src/org/jdrupes/vmoperator/common/K8sClusterGenericStub.java b/org.jdrupes.vmoperator.common/src/org/jdrupes/vmoperator/common/K8sClusterGenericStub.java index af87af2..6b5fd7e 100644 --- a/org.jdrupes.vmoperator.common/src/org/jdrupes/vmoperator/common/K8sClusterGenericStub.java +++ b/org.jdrupes.vmoperator.common/src/org/jdrupes/vmoperator/common/K8sClusterGenericStub.java @@ -240,6 +240,7 @@ public class K8sClusterGenericStub the object list type * @param the result type */ + @FunctionalInterface public interface GenericSupplier> { diff --git a/org.jdrupes.vmoperator.common/src/org/jdrupes/vmoperator/common/K8sGenericStub.java b/org.jdrupes.vmoperator.common/src/org/jdrupes/vmoperator/common/K8sGenericStub.java index b8f1992..aef791f 100644 --- a/org.jdrupes.vmoperator.common/src/org/jdrupes/vmoperator/common/K8sGenericStub.java +++ b/org.jdrupes.vmoperator.common/src/org/jdrupes/vmoperator/common/K8sGenericStub.java @@ -359,6 +359,7 @@ public class K8sGenericStub the object list type * @param the result type */ + @FunctionalInterface public interface GenericSupplier> { From b7fad4614db37bc9f72b3d622fc708d15bdde5e9 Mon Sep 17 00:00:00 2001 From: "Michael N. Lipp" Date: Tue, 29 Apr 2025 14:02:12 +0200 Subject: [PATCH 03/12] Improve debug messages. --- .../runner/qemu/GuestAgentClient.java | 16 ++++++----- .../vmoperator/runner/qemu/QemuMonitor.java | 25 ++++++++++------- .../runner/qemu/VmopAgentClient.java | 27 +++++++++++++------ .../runner/qemu/events/MonitorEvent.java | 18 +++++++++++++ .../runner/qemu/events/OsinfoEvent.java | 19 +++++++++++++ 5 files changed, 81 insertions(+), 24 deletions(-) diff --git a/org.jdrupes.vmoperator.runner.qemu/src/org/jdrupes/vmoperator/runner/qemu/GuestAgentClient.java b/org.jdrupes.vmoperator.runner.qemu/src/org/jdrupes/vmoperator/runner/qemu/GuestAgentClient.java index b0001e4..45d2487 100644 --- a/org.jdrupes.vmoperator.runner.qemu/src/org/jdrupes/vmoperator/runner/qemu/GuestAgentClient.java +++ b/org.jdrupes.vmoperator.runner.qemu/src/org/jdrupes/vmoperator/runner/qemu/GuestAgentClient.java @@ -69,14 +69,14 @@ public class GuestAgentClient extends AgentConnector { */ @Override protected void agentConnected() { - logger.fine(() -> "guest agent connected"); + logger.fine(() -> "Guest agent connected"); connected = true; rep().fire(new GuestAgentCommand(new QmpGuestGetOsinfo())); } @Override protected void agentDisconnected() { - logger.fine(() -> "guest agent disconnected"); + logger.fine(() -> "Guest agent disconnected"); connected = false; } @@ -88,15 +88,16 @@ public class GuestAgentClient extends AgentConnector { */ @Override protected void processInput(String line) throws IOException { - logger.fine(() -> "guest agent(in): " + line); + logger.finer(() -> "guest agent(in): " + line); try { var response = mapper.readValue(line, ObjectNode.class); if (response.has("return") || response.has("error")) { QmpCommand executed = executing.poll(); - logger.fine(() -> String.format("(Previous \"guest agent(in)\"" + logger.finer(() -> String.format("(Previous \"guest agent(in)\"" + " is result from executing %s)", executed)); if (executed instanceof QmpGuestGetOsinfo) { var osInfo = new OsinfoEvent(response.get("return")); + logger.fine(() -> "Guest agent triggers: " + osInfo); rep().fire(osInfo); } } @@ -120,10 +121,11 @@ public class GuestAgentClient extends AgentConnector { return; } var command = event.command(); - logger.fine(() -> "guest agent(out): " + command.toString()); + logger.fine(() -> "Guest handles: " + event); String asText; try { asText = command.asText(); + logger.finer(() -> "guest agent(out): " + asText); } catch (JsonProcessingException e) { logger.log(Level.SEVERE, e, () -> "Cannot serialize Json: " + e.getMessage()); @@ -163,8 +165,8 @@ public class GuestAgentClient extends AgentConnector { } event.suspendHandling(); suspendedStop = event; - logger.fine(() -> "Sending powerdown command, waiting for" - + " termination until " + waitUntil); + logger.fine(() -> "Attempting shutdown through guest agent," + + " waiting for termination until " + waitUntil); powerdownTimer = Components.schedule(t -> { logger.fine(() -> "Powerdown timeout reached."); synchronized (this) { diff --git a/org.jdrupes.vmoperator.runner.qemu/src/org/jdrupes/vmoperator/runner/qemu/QemuMonitor.java b/org.jdrupes.vmoperator.runner.qemu/src/org/jdrupes/vmoperator/runner/qemu/QemuMonitor.java index 6be7603..e844bc4 100644 --- a/org.jdrupes.vmoperator.runner.qemu/src/org/jdrupes/vmoperator/runner/qemu/QemuMonitor.java +++ b/org.jdrupes.vmoperator.runner.qemu/src/org/jdrupes/vmoperator/runner/qemu/QemuMonitor.java @@ -108,24 +108,30 @@ public class QemuMonitor extends QemuConnector { @Override protected void processInput(String line) throws IOException { - logger.fine(() -> "monitor(in): " + line); + logger.finer(() -> "monitor(in): " + line); try { var response = mapper.readValue(line, ObjectNode.class); if (response.has("QMP")) { monitorReady = true; + logger.fine(() -> "QMP connection ready"); rep().fire(new MonitorReady()); return; } if (response.has("return") || response.has("error")) { QmpCommand executed = executing.poll(); - logger.fine( + logger.finer( () -> String.format("(Previous \"monitor(in)\" is result " + "from executing %s)", executed)); - rep().fire(MonitorResult.from(executed, response)); + var monRes = MonitorResult.from(executed, response); + logger.fine(() -> "QMP triggers: " + monRes); + rep().fire(monRes); return; } if (response.has("event")) { - MonitorEvent.from(response).ifPresent(rep()::fire); + MonitorEvent.from(response).ifPresent(me -> { + logger.fine(() -> "QMP triggers: " + me); + rep().fire(me); + }); } } catch (JsonProcessingException e) { throw new IOException(e); @@ -141,7 +147,7 @@ public class QemuMonitor extends QemuConnector { public void onClosed(Closed event, SocketIOChannel channel) { channel.associated(this, getClass()).ifPresent(qm -> { super.onClosed(event, channel); - logger.finer(() -> "QMP socket closed."); + logger.fine(() -> "QMP connection closed."); monitorReady = false; }); } @@ -158,7 +164,7 @@ public class QemuMonitor extends QemuConnector { public void onMonitorCommand(MonitorCommand event) throws IOException { // Check prerequisites if (!monitorReady && !(event.command() instanceof QmpCapabilities)) { - logger.severe(() -> "Premature monitor command (not ready): " + logger.severe(() -> "Premature QMP command (not ready): " + event.command()); rep().fire(new Stop()); return; @@ -166,10 +172,11 @@ public class QemuMonitor extends QemuConnector { // Send the command var command = event.command(); - logger.fine(() -> "monitor(out): " + command.toString()); + logger.fine(() -> "QMP handles: " + event.toString()); String asText; try { asText = command.asText(); + logger.finer(() -> "monitor(out): " + asText); } catch (JsonProcessingException e) { logger.log(Level.SEVERE, e, () -> "Cannot serialize Json: " + e.getMessage()); @@ -192,8 +199,8 @@ public class QemuMonitor extends QemuConnector { @SuppressWarnings("PMD.AvoidSynchronizedStatement") public void onStop(Stop event) { if (!monitorReady) { - logger.fine(() -> "No QMP connection," - + " cannot send powerdown command"); + logger.fine(() -> "Not sending QMP powerdown command" + + " because QMP connection is closed"); return; } diff --git a/org.jdrupes.vmoperator.runner.qemu/src/org/jdrupes/vmoperator/runner/qemu/VmopAgentClient.java b/org.jdrupes.vmoperator.runner.qemu/src/org/jdrupes/vmoperator/runner/qemu/VmopAgentClient.java index f50d397..cac41d4 100644 --- a/org.jdrupes.vmoperator.runner.qemu/src/org/jdrupes/vmoperator/runner/qemu/VmopAgentClient.java +++ b/org.jdrupes.vmoperator.runner.qemu/src/org/jdrupes/vmoperator/runner/qemu/VmopAgentClient.java @@ -59,10 +59,14 @@ public class VmopAgentClient extends AgentConnector { */ @Handler public void onVmopAgentLogIn(VmopAgentLogIn event) throws IOException { - logger.fine(() -> "vmop agent(out): login " + event.user()); if (writer().isPresent()) { + logger.fine(() -> "Vmop agent handles:" + event); executing.add(event); + logger.finer(() -> "vmop agent(out): login " + event.user()); sendCommand("login " + event.user()); + } else { + logger + .warning(() -> "No vmop agent connection for sending " + event); } } @@ -74,9 +78,10 @@ public class VmopAgentClient extends AgentConnector { */ @Handler public void onVmopAgentLogout(VmopAgentLogOut event) throws IOException { - logger.fine(() -> "vmop agent(out): logout"); if (writer().isPresent()) { + logger.fine(() -> "Vmop agent handles:" + event); executing.add(event); + logger.finer(() -> "vmop agent(out): logout"); sendCommand("logout"); } } @@ -85,23 +90,27 @@ public class VmopAgentClient extends AgentConnector { @SuppressWarnings({ "PMD.UnnecessaryReturn", "PMD.AvoidLiteralsInIfCondition" }) protected void processInput(String line) throws IOException { - logger.fine(() -> "vmop agent(in): " + line); + logger.finer(() -> "vmop agent(in): " + line); // Check validity if (line.isEmpty() || !Character.isDigit(line.charAt(0))) { - logger.warning(() -> "Illegal response: " + line); + logger.warning(() -> "Illegal vmop agent response: " + line); return; } // Check positive responses if (line.startsWith("220 ")) { - rep().fire(new VmopAgentConnected()); + var evt = new VmopAgentConnected(); + logger.fine(() -> "Vmop agent triggers " + evt); + rep().fire(evt); return; } if (line.startsWith("201 ")) { Event cmd = executing.pop(); if (cmd instanceof VmopAgentLogIn login) { - rep().fire(new VmopAgentLoggedIn(login)); + var evt = new VmopAgentLoggedIn(login); + logger.fine(() -> "Vmop agent triggers " + evt); + rep().fire(evt); } else { logger.severe(() -> "Response " + line + " does not match executing command " + cmd); @@ -111,7 +120,9 @@ public class VmopAgentClient extends AgentConnector { if (line.startsWith("202 ")) { Event cmd = executing.pop(); if (cmd instanceof VmopAgentLogOut logout) { - rep().fire(new VmopAgentLoggedOut(logout)); + var evt = new VmopAgentLoggedOut(logout); + logger.fine(() -> "Vmop agent triggers " + evt); + rep().fire(evt); } else { logger.severe(() -> "Response " + line + "does not match executing command " + cmd); @@ -125,7 +136,7 @@ public class VmopAgentClient extends AgentConnector { } // Error - logger.warning(() -> "Error response: " + line); + logger.warning(() -> "Error response from vmop agent: " + line); executing.pop(); } diff --git a/org.jdrupes.vmoperator.runner.qemu/src/org/jdrupes/vmoperator/runner/qemu/events/MonitorEvent.java b/org.jdrupes.vmoperator.runner.qemu/src/org/jdrupes/vmoperator/runner/qemu/events/MonitorEvent.java index e35a172..6663fa4 100644 --- a/org.jdrupes.vmoperator.runner.qemu/src/org/jdrupes/vmoperator/runner/qemu/events/MonitorEvent.java +++ b/org.jdrupes.vmoperator.runner.qemu/src/org/jdrupes/vmoperator/runner/qemu/events/MonitorEvent.java @@ -20,6 +20,8 @@ package org.jdrupes.vmoperator.runner.qemu.events; import com.fasterxml.jackson.databind.JsonNode; import java.util.Optional; +import org.jgrapes.core.Channel; +import org.jgrapes.core.Components; import org.jgrapes.core.Event; /** @@ -112,4 +114,20 @@ public class MonitorEvent extends Event { public JsonNode data() { return data; } + + /* + * (non-Javadoc) + * + * @see java.lang.Object#toString() + */ + @Override + public String toString() { + StringBuilder builder = new StringBuilder(); + builder.append(Components.objectName(this)).append(" [").append(data); + if (channels() != null) { + builder.append(", channels=").append(Channel.toString(channels())); + } + builder.append(']'); + return builder.toString(); + } } diff --git a/org.jdrupes.vmoperator.runner.qemu/src/org/jdrupes/vmoperator/runner/qemu/events/OsinfoEvent.java b/org.jdrupes.vmoperator.runner.qemu/src/org/jdrupes/vmoperator/runner/qemu/events/OsinfoEvent.java index 294ac7b..0e90019 100644 --- a/org.jdrupes.vmoperator.runner.qemu/src/org/jdrupes/vmoperator/runner/qemu/events/OsinfoEvent.java +++ b/org.jdrupes.vmoperator.runner.qemu/src/org/jdrupes/vmoperator/runner/qemu/events/OsinfoEvent.java @@ -19,6 +19,8 @@ package org.jdrupes.vmoperator.runner.qemu.events; import com.fasterxml.jackson.databind.JsonNode; +import org.jgrapes.core.Channel; +import org.jgrapes.core.Components; import org.jgrapes.core.Event; /** @@ -40,4 +42,21 @@ public class OsinfoEvent extends Event { public JsonNode osinfo() { return osinfo; } + + /* + * (non-Javadoc) + * + * @see java.lang.Object#toString() + */ + @Override + public String toString() { + StringBuilder builder = new StringBuilder(); + builder.append(Components.objectName(this)).append(" [") + .append(osinfo); + if (channels() != null) { + builder.append(", channels=").append(Channel.toString(channels())); + } + builder.append(']'); + return builder.toString(); + } } From 10f3028f06d58d81f6972d5c29fcf8f1dfc698a7 Mon Sep 17 00:00:00 2001 From: "Michael N. Lipp" Date: Wed, 30 Apr 2025 16:27:15 +0200 Subject: [PATCH 04/12] Increase concurrency and avoid race condition. --- .../vmoperator/manager/Controller.java | 3 +- .../jdrupes/vmoperator/manager/VmMonitor.java | 58 +++++++++++++------ 2 files changed, 42 insertions(+), 19 deletions(-) diff --git a/org.jdrupes.vmoperator.manager/src/org/jdrupes/vmoperator/manager/Controller.java b/org.jdrupes.vmoperator.manager/src/org/jdrupes/vmoperator/manager/Controller.java index c15acc5..ce14488 100644 --- a/org.jdrupes.vmoperator.manager/src/org/jdrupes/vmoperator/manager/Controller.java +++ b/org.jdrupes.vmoperator.manager/src/org/jdrupes/vmoperator/manager/Controller.java @@ -50,6 +50,7 @@ import org.jdrupes.vmoperator.manager.events.VmPoolChanged; import org.jdrupes.vmoperator.manager.events.VmResourceChanged; import org.jgrapes.core.Channel; import org.jgrapes.core.Component; +import org.jgrapes.core.EventPipeline; import org.jgrapes.core.annotation.Handler; import org.jgrapes.core.events.HandlingError; import org.jgrapes.core.events.Start; @@ -94,7 +95,7 @@ import org.jgrapes.util.events.ConfigurationUpdate; public class Controller extends Component { private String namespace; - private final ChannelManager chanMgr; + private final ChannelManager chanMgr; /** * Creates a new instance. diff --git a/org.jdrupes.vmoperator.manager/src/org/jdrupes/vmoperator/manager/VmMonitor.java b/org.jdrupes.vmoperator.manager/src/org/jdrupes/vmoperator/manager/VmMonitor.java index 09bade8..b667aa6 100644 --- a/org.jdrupes.vmoperator.manager/src/org/jdrupes/vmoperator/manager/VmMonitor.java +++ b/org.jdrupes.vmoperator.manager/src/org/jdrupes/vmoperator/manager/VmMonitor.java @@ -22,7 +22,6 @@ import com.google.gson.JsonObject; import io.kubernetes.client.apimachinery.GroupVersionKind; import io.kubernetes.client.custom.V1Patch; import io.kubernetes.client.openapi.ApiException; -import io.kubernetes.client.openapi.models.V1ObjectMeta; import io.kubernetes.client.util.Watch; import io.kubernetes.client.util.generic.options.ListOptions; import java.io.IOException; @@ -32,7 +31,6 @@ import java.util.ArrayList; import java.util.Collections; import java.util.Optional; import java.util.Set; -import java.util.concurrent.atomic.AtomicBoolean; import java.util.stream.Collectors; import org.jdrupes.vmoperator.common.Constants.Crd; import org.jdrupes.vmoperator.common.Constants.Status; @@ -57,16 +55,28 @@ import org.jdrupes.vmoperator.manager.events.VmResourceChanged; import org.jdrupes.vmoperator.util.GsonPtr; import org.jgrapes.core.Channel; import org.jgrapes.core.Event; +import org.jgrapes.core.EventPipeline; import org.jgrapes.core.annotation.Handler; /** - * Watches for changes of VM definitions. + * Watches for changes of VM definitions. When a VM definition (CR) + * becomes known, is is registered with a {@link ChannelManager} and thus + * gets an associated {@link VmChannel} and an associated + * {@link EventPipeline}. + * + * The {@link EventPipeline} is used for submitting an action that processes + * the change data from kubernetes, eventually transforming it to a + * {@link VmResourceChanged} event that is handled by another + * {@link EventPipeline} associated with the {@link VmChannel}. This + * event pipeline should be used for all events related to changes of + * a particular VM. */ @SuppressWarnings({ "PMD.DataflowAnomalyAnalysis", "PMD.ExcessiveImports" }) public class VmMonitor extends AbstractMonitor { - private final ChannelManager channelManager; + private final ChannelManager channelManager; /** * Instantiates a new VM definition watcher. @@ -75,7 +85,7 @@ public class VmMonitor extends * @param channelManager the channel manager */ public VmMonitor(Channel componentChannel, - ChannelManager channelManager) { + ChannelManager channelManager) { super(componentChannel, VmDefinition.class, VmDefinitions.class); this.channelManager = channelManager; @@ -122,14 +132,18 @@ public class VmMonitor extends @Override protected void handleChange(K8sClient client, Watch.Response response) { - V1ObjectMeta metadata = response.object.getMetadata(); - AtomicBoolean toBeAdded = new AtomicBoolean(false); - VmChannel channel = channelManager.channel(metadata.getName()) - .orElseGet(() -> { - toBeAdded.set(true); - return channelManager.createChannel(metadata.getName()); - }); + var name = response.object.getMetadata().getName(); + // Process the response data on a VM specific pipeline to + // increase concurrency when e.g. starting many VMs. + var preparing = channelManager.associated(name) + .orElseGet(() -> newEventPipeline()); + preparing.submit("VmChange[" + name + "]", + () -> processChange(client, response, preparing)); + } + + private void processChange(K8sClient client, + Watch.Response response, EventPipeline preparing) { // Get full definition and associate with channel as backup var vmDef = response.object; if (vmDef.data() == null) { @@ -137,6 +151,9 @@ public class VmMonitor extends // https://github.com/kubernetes-client/java/issues/3215 vmDef = getModel(client, vmDef); } + var name = response.object.getMetadata().getName(); + var channel = channelManager.channel(name) + .orElseGet(() -> channelManager.createChannel(name)); if (vmDef.data() != null) { // New data, augment and save addExtraData(vmDef, channel.vmDefinition()); @@ -150,9 +167,7 @@ public class VmMonitor extends + response.object.getMetadata()); return; } - if (toBeAdded.get()) { - channelManager.put(vmDef.name(), channel); - } + channelManager.put(name, channel, preparing); // Create and fire changed event. Remove channel from channel // manager on completion. @@ -199,9 +214,16 @@ public class VmMonitor extends @Handler public void onPodChanged(PodChanged event, VmChannel channel) { var vmDef = channel.vmDefinition(); - updateNodeInfo(event, vmDef); - channel - .fire(new VmResourceChanged(ResponseType.MODIFIED, vmDef, false, true)); + + // Make sure that this is properly sync'd with VM CR changes. + channelManager.associated(vmDef.name()) + .orElseGet(() -> activeEventPipeline()) + .submit("NodeInfo[" + vmDef.name() + "]", + () -> { + updateNodeInfo(event, vmDef); + channel.fire(new VmResourceChanged(ResponseType.MODIFIED, + vmDef, false, true)); + }); } private void updateNodeInfo(PodChanged event, VmDefinition vmDef) { From a5433c869bb5d25601060200cba2335bf250788a Mon Sep 17 00:00:00 2001 From: "Michael N. Lipp" Date: Sat, 3 May 2025 22:29:42 +0200 Subject: [PATCH 05/12] Upgrade webconsole base library. --- org.jdrupes.vmoperator.manager/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/org.jdrupes.vmoperator.manager/build.gradle b/org.jdrupes.vmoperator.manager/build.gradle index eda5ce0..4ce4ed0 100644 --- a/org.jdrupes.vmoperator.manager/build.gradle +++ b/org.jdrupes.vmoperator.manager/build.gradle @@ -17,7 +17,7 @@ dependencies { implementation 'org.jgrapes:org.jgrapes.io:[2.12.1,3)' implementation 'org.jgrapes:org.jgrapes.http:[3.5.0,4)' - implementation 'org.jgrapes:org.jgrapes.webconsole.base:[2.2.0,3)' + implementation 'org.jgrapes:org.jgrapes.webconsole.base:[2.3.0,3)' implementation 'org.jgrapes:org.jgrapes.webconsole.vuejs:[1.8.0,2)' implementation 'org.jgrapes:org.jgrapes.webconsole.rbac:[1.4.0,2)' implementation 'org.jgrapes:org.jgrapes.webconlet.oidclogin:[1.7.0,2)' From 76b579c404f4ea691bf5a00cde47ddb8d35b1342 Mon Sep 17 00:00:00 2001 From: "Michael N. Lipp" Date: Sun, 4 May 2025 11:36:55 +0200 Subject: [PATCH 06/12] Add key, allowing vue to optimize. --- .../org/jdrupes/vmoperator/vmmgmt/VmMgmt-view.ftl.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/org.jdrupes.vmoperator.vmmgmt/resources/org/jdrupes/vmoperator/vmmgmt/VmMgmt-view.ftl.html b/org.jdrupes.vmoperator.vmmgmt/resources/org/jdrupes/vmoperator/vmmgmt/VmMgmt-view.ftl.html index 5a28cb8..3197440 100644 --- a/org.jdrupes.vmoperator.vmmgmt/resources/org/jdrupes/vmoperator/vmmgmt/VmMgmt-view.ftl.html +++ b/org.jdrupes.vmoperator.vmmgmt/resources/org/jdrupes/vmoperator/vmmgmt/VmMgmt-view.ftl.html @@ -30,7 +30,7 @@ -