From 12d6745d759581dd4aee82f0aeabb27e7cfe92dd Mon Sep 17 00:00:00 2001 From: Michael Lipp Date: Tue, 12 Nov 2024 19:29:46 +0000 Subject: [PATCH] Add some logging messages and enhance logging configurability. --- deploy/crds/vms-crd.yaml | 5 ++++ dev-example/config.yaml | 12 +++++++++ .../manager/ConfigMapReconciler.java | 27 +++++++++++++++---- .../vmoperator/manager/Reconciler.java | 5 ++++ .../runner/qemu/CommandDefinition.java | 5 ++++ .../vmoperator/runner/qemu/Runner.java | 18 +++++++++++++ 6 files changed, 67 insertions(+), 5 deletions(-) diff --git a/deploy/crds/vms-crd.yaml b/deploy/crds/vms-crd.yaml index 492deaea8..f1bbaf25c 100644 --- a/deploy/crds/vms-crd.yaml +++ b/deploy/crds/vms-crd.yaml @@ -1019,6 +1019,11 @@ spec: - accessConsole - "*" default: [] + loggingProperties: + type: string + description: >- + Override the default logging properties for + the runner for this VM. vm: type: object description: Defines the VM. diff --git a/dev-example/config.yaml b/dev-example/config.yaml index 3f973e40e..af1f3b8b5 100644 --- a/dev-example/config.yaml +++ b/dev-example/config.yaml @@ -17,6 +17,18 @@ metallb.universe.tf/loadBalancerIPs: 192.168.168.1 metallb.universe.tf/ip-allocated-from-pool: single-common metallb.universe.tf/allow-shared-ip: single-common + loggingProperties: | + # Defaults for namespace (VM domain) + handlers=java.util.logging.ConsoleHandler + + #org.jgrapes.level=FINE + #org.jgrapes.core.handlerTracking.level=FINER + + org.jdrupes.vmoperator.runner.qemu.level=FINEST + + java.util.logging.ConsoleHandler.level=ALL + java.util.logging.ConsoleHandler.formatter=java.util.logging.SimpleFormatter + java.util.logging.SimpleFormatter.format=%1$tb %1$td %1$tT %4$s %5$s%6$s%n "/GuiSocketServer": port: 8888 "/GuiHttpServer": diff --git a/org.jdrupes.vmoperator.manager/src/org/jdrupes/vmoperator/manager/ConfigMapReconciler.java b/org.jdrupes.vmoperator.manager/src/org/jdrupes/vmoperator/manager/ConfigMapReconciler.java index 129a54de5..68ca7faf0 100644 --- a/org.jdrupes.vmoperator.manager/src/org/jdrupes/vmoperator/manager/ConfigMapReconciler.java +++ b/org.jdrupes.vmoperator.manager/src/org/jdrupes/vmoperator/manager/ConfigMapReconciler.java @@ -18,6 +18,7 @@ package org.jdrupes.vmoperator.manager; +import com.google.gson.JsonObject; import freemarker.template.Configuration; import freemarker.template.TemplateException; import io.kubernetes.client.custom.V1Patch; @@ -36,6 +37,8 @@ import static org.jdrupes.vmoperator.manager.Constants.APP_NAME; import static org.jdrupes.vmoperator.manager.Constants.VM_OP_NAME; import org.jdrupes.vmoperator.manager.events.VmChannel; +import org.jdrupes.vmoperator.util.DataPath; +import org.jdrupes.vmoperator.util.GsonPtr; import org.yaml.snakeyaml.LoaderOptions; import org.yaml.snakeyaml.Yaml; import org.yaml.snakeyaml.constructor.SafeConstructor; @@ -71,10 +74,6 @@ public ConfigMapReconciler(Configuration fmConfig) { public Map reconcile(Map model, VmChannel channel) throws IOException, TemplateException, ApiException { - // Get API - DynamicKubernetesApi cmApi = new DynamicKubernetesApi("", "v1", - "configmaps", channel.client()); - // Combine template and data and parse result var fmTemplate = fmConfig.getTemplate("runnerConfig.ftl.yaml"); StringWriter out = new StringWriter(); @@ -84,8 +83,26 @@ public Map reconcile(Map model, var mapDef = Dynamics.newFromYaml( new Yaml(new SafeConstructor(new LoaderOptions())), out.toString()); + // Maybe override logging.properties from reconciler configuration. + DataPath. get(model, "reconciler", "loggingProperties") + .ifPresent(props -> { + GsonPtr.to(mapDef.getRaw()).get(JsonObject.class, "data") + .get().addProperty("logging.properties", props); + }); + + // Maybe override logging.properties from VM definition. + DataPath. get(model, "cr", "spec", "loggingProperties") + .ifPresent(props -> { + GsonPtr.to(mapDef.getRaw()).get(JsonObject.class, "data") + .get().addProperty("logging.properties", props); + }); + + // Get API + DynamicKubernetesApi cmApi = new DynamicKubernetesApi("", "v1", + "configmaps", channel.client()); + // Apply and maybe force pod update - var newState = K8s.apply(cmApi, mapDef, out.toString()); + var newState = K8s.apply(cmApi, mapDef, mapDef.getRaw().toString()); maybeForceUpdate(channel.client(), newState); @SuppressWarnings("unchecked") var res = (Map) channel.client().getJSON().getGson() diff --git a/org.jdrupes.vmoperator.manager/src/org/jdrupes/vmoperator/manager/Reconciler.java b/org.jdrupes.vmoperator.manager/src/org/jdrupes/vmoperator/manager/Reconciler.java index 32753ac55..3fa2ffe3d 100644 --- a/org.jdrupes.vmoperator.manager/src/org/jdrupes/vmoperator/manager/Reconciler.java +++ b/org.jdrupes.vmoperator.manager/src/org/jdrupes/vmoperator/manager/Reconciler.java @@ -132,6 +132,11 @@ * ``` * This makes all VM consoles available at IP address 192.168.168.1 * with the port numbers from the VM definitions. + * + * * `loggingProperties`: If defined, specifies the default logging + * properties to be used by the runners managed by the controller. + * This property is a string that holds the content of + * a logging.properties file. */ @SuppressWarnings({ "PMD.DataflowAnomalyAnalysis", "PMD.AvoidDuplicateLiterals" }) diff --git a/org.jdrupes.vmoperator.runner.qemu/src/org/jdrupes/vmoperator/runner/qemu/CommandDefinition.java b/org.jdrupes.vmoperator.runner.qemu/src/org/jdrupes/vmoperator/runner/qemu/CommandDefinition.java index 905760610..7aec209a9 100644 --- a/org.jdrupes.vmoperator.runner.qemu/src/org/jdrupes/vmoperator/runner/qemu/CommandDefinition.java +++ b/org.jdrupes.vmoperator.runner.qemu/src/org/jdrupes/vmoperator/runner/qemu/CommandDefinition.java @@ -69,4 +69,9 @@ private void collect(List result, JsonNode node) { public String name() { return name; } + + @Override + public String toString() { + return "Command " + name + ": " + command; + } } \ No newline at end of file diff --git a/org.jdrupes.vmoperator.runner.qemu/src/org/jdrupes/vmoperator/runner/qemu/Runner.java b/org.jdrupes.vmoperator.runner.qemu/src/org/jdrupes/vmoperator/runner/qemu/Runner.java index 0b6e22ea2..fedaf1eaa 100644 --- a/org.jdrupes.vmoperator.runner.qemu/src/org/jdrupes/vmoperator/runner/qemu/Runner.java +++ b/org.jdrupes.vmoperator.runner.qemu/src/org/jdrupes/vmoperator/runner/qemu/Runner.java @@ -48,6 +48,8 @@ import java.util.logging.Level; import java.util.logging.LogManager; import java.util.logging.Logger; +import java.util.stream.Collectors; +import java.util.stream.StreamSupport; import org.apache.commons.cli.CommandLine; import org.apache.commons.cli.CommandLineParser; import org.apache.commons.cli.DefaultParser; @@ -318,6 +320,7 @@ public void onConfigurationUpdate(ConfigurationUpdate event) { }); } + @SuppressWarnings("PMD.LambdaCanBeMethodReference") private void processInitialConfiguration(Configuration newConfig) { try { config = newConfig; @@ -333,12 +336,15 @@ private void processInitialConfiguration(Configuration newConfig) { var tplData = dataFromTemplate(); swtpmDefinition = Optional.ofNullable(tplData.get(SWTPM)) .map(d -> new CommandDefinition(SWTPM, d)).orElse(null); + logger.finest(() -> swtpmDefinition.toString()); qemuDefinition = Optional.ofNullable(tplData.get(QEMU)) .map(d -> new CommandDefinition(QEMU, d)).orElse(null); + logger.finest(() -> qemuDefinition.toString()); cloudInitImgDefinition = Optional.ofNullable(tplData.get(CLOUD_INIT_IMG)) .map(d -> new CommandDefinition(CLOUD_INIT_IMG, d)) .orElse(null); + logger.finest(() -> cloudInitImgDefinition.toString()); // Forward some values to child components qemuMonitor.configure(config.monitorSocket, @@ -364,6 +370,12 @@ private void setFirmwarePaths() throws IOException { break; } } + if (codePaths.iterator().hasNext() && config.firmwareRom == null) { + throw new IllegalArgumentException("No ROM found, candidates were: " + + StreamSupport.stream(codePaths.spliterator(), false) + .map(JsonNode::asText).collect(Collectors.joining(", "))); + } + // Get file for firmware vars, if necessary config.firmwareVars = config.dataDir.resolve(FW_VARS); if (!Files.exists(config.firmwareVars)) { @@ -405,12 +417,14 @@ private JsonNode dataFromTemplate() model.put("hasDisplayPassword", config.hasDisplayPassword); model.put("cloudInit", config.cloudInit); model.put("vm", config.vm); + logger.finest(() -> "Processing template with model: " + model); // Combine template and data and parse result // (tempting, but no need to use a pipe here) var fmTemplate = fmConfig.getTemplate(templatePath.toString()); StringWriter out = new StringWriter(); fmTemplate.process(model, out); + logger.finest(() -> "Result of processing template: " + out); return yamlMapper.readValue(out.toString(), JsonNode.class); } @@ -746,6 +760,10 @@ private void shutdown() { props = Runner.class.getResourceAsStream("logging.properties"); } LogManager.getLogManager().readConfiguration(props); + Logger.getLogger(Runner.class.getName()).log(Level.CONFIG, + () -> path.isPresent() + ? "Using logging configuration from " + path.get() + : "Using default logging configuration"); } catch (IOException e) { e.printStackTrace(); }