From f4d3905f52ec75589dac34372597c2567d92d991 Mon Sep 17 00:00:00 2001 From: "Michael N. Lipp" Date: Fri, 9 Jun 2023 15:19:48 +0200 Subject: [PATCH] Improved directory handling and logging configuration. --- .../build.gradle | 3 +- ...ul-debug.properties => logging.properties} | 2 +- .../vmoperator/runner/qemu/logging.properties | 23 ++++++++++ .../vmoperator/runner/qemu/Configuration.java | 43 ++++++----------- .../vmoperator/runner/qemu/Runner.java | 46 +++++++++++++++---- 5 files changed, 76 insertions(+), 41 deletions(-) rename org.jdrupes.vmoperator.runner.qemu/{jul-debug.properties => logging.properties} (92%) create mode 100644 org.jdrupes.vmoperator.runner.qemu/resources/org/jdrupes/vmoperator/runner/qemu/logging.properties diff --git a/org.jdrupes.vmoperator.runner.qemu/build.gradle b/org.jdrupes.vmoperator.runner.qemu/build.gradle index c9a6f84..0d83235 100644 --- a/org.jdrupes.vmoperator.runner.qemu/build.gradle +++ b/org.jdrupes.vmoperator.runner.qemu/build.gradle @@ -24,6 +24,7 @@ dependencies { } application { + applicationName = 'vm-runner.qemu' applicationDefaultJvmArgs = ['-Xms50m', '-Djava.util.logging.manager=org.jdrupes.vmoperator.util.LongLoggingManager' ] @@ -33,7 +34,7 @@ application { distributions { main { - distributionBaseName = 'runner-qemu' + //distributionBaseName = 'runner-qemu' // contents { // from 'src/readme' // } diff --git a/org.jdrupes.vmoperator.runner.qemu/jul-debug.properties b/org.jdrupes.vmoperator.runner.qemu/logging.properties similarity index 92% rename from org.jdrupes.vmoperator.runner.qemu/jul-debug.properties rename to org.jdrupes.vmoperator.runner.qemu/logging.properties index 8ebba90..6b0542d 100644 --- a/org.jdrupes.vmoperator.runner.qemu/jul-debug.properties +++ b/org.jdrupes.vmoperator.runner.qemu/logging.properties @@ -26,4 +26,4 @@ org.jdrupes.vmoperator.runner.qemu.level=FINE java.util.logging.ConsoleHandler.level=ALL java.util.logging.ConsoleHandler.formatter=java.util.logging.SimpleFormatter -java.util.logging.SimpleFormatter.format=%1$tY-%1$tm-%1$td %5$s%6$s%n +java.util.logging.SimpleFormatter.format=%1$tb %1$td %1$tT %4$s %5$s%6$s%n diff --git a/org.jdrupes.vmoperator.runner.qemu/resources/org/jdrupes/vmoperator/runner/qemu/logging.properties b/org.jdrupes.vmoperator.runner.qemu/resources/org/jdrupes/vmoperator/runner/qemu/logging.properties new file mode 100644 index 0000000..9a0a080 --- /dev/null +++ b/org.jdrupes.vmoperator.runner.qemu/resources/org/jdrupes/vmoperator/runner/qemu/logging.properties @@ -0,0 +1,23 @@ +# +# VM-Operator +# Copyright (C) 2023 Michael N. Lipp +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# for more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, see . +# + +handlers=java.util.logging.ConsoleHandler + +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 diff --git a/org.jdrupes.vmoperator.runner.qemu/src/org/jdrupes/vmoperator/runner/qemu/Configuration.java b/org.jdrupes.vmoperator.runner.qemu/src/org/jdrupes/vmoperator/runner/qemu/Configuration.java index 26dad6c..10d49c6 100644 --- a/org.jdrupes.vmoperator.runner.qemu/src/org/jdrupes/vmoperator/runner/qemu/Configuration.java +++ b/org.jdrupes.vmoperator.runner.qemu/src/org/jdrupes/vmoperator/runner/qemu/Configuration.java @@ -26,6 +26,7 @@ import java.util.UUID; import java.util.logging.Level; import java.util.logging.Logger; import org.jdrupes.vmoperator.util.Dto; +import org.jdrupes.vmoperator.util.FsdUtils; /** * The configuration information from the configuration file. @@ -34,8 +35,8 @@ class Configuration implements Dto { @SuppressWarnings("PMD.FieldNamingConventions") protected final Logger logger = Logger.getLogger(getClass().getName()); - public String dataDir; - public String runtimeDir; + public Path dataDir; + public Path runtimeDir; public String template; public boolean updateTemplate; public Path swtpmSocket; @@ -127,22 +128,14 @@ class Configuration implements Dto { private boolean checkRuntimeDir() { // Runtime directory (sockets) if (runtimeDir == null) { - runtimeDir = System.getenv("XDG_RUNTIME_DIR"); - if (runtimeDir == null) { - runtimeDir = "/tmp"; - if (System.getenv("USER") != null) { - runtimeDir += "/" + System.getenv("USER"); - } - } - runtimeDir += "/vmrunner/" + vm.name; - swtpmSocket = Path.of(runtimeDir, "swtpm-sock"); - monitorSocket = Path.of(runtimeDir, "monitor.sock"); + runtimeDir = FsdUtils.runtimeDir(Runner.APP_NAME).resolve(vm.name); + swtpmSocket = runtimeDir.resolve("swtpm-sock"); + monitorSocket = runtimeDir.resolve("monitor.sock"); } - Path runtimePath = Path.of(runtimeDir); - if (!Files.exists(runtimePath)) { - runtimePath.toFile().mkdirs(); + if (!Files.exists(runtimeDir)) { + runtimeDir.toFile().mkdirs(); } - if (!Files.isDirectory(runtimePath) || !Files.isWritable(runtimePath)) { + if (!Files.isDirectory(runtimeDir) || !Files.isWritable(runtimeDir)) { logger.severe(() -> String.format( "Configured runtime directory \"%s\"" + " does not exist or isn't writable.", @@ -156,20 +149,12 @@ class Configuration implements Dto { private boolean checkDataDir() { // Data directory if (dataDir == null) { - dataDir = System.getenv("XDG_DATA_HOME"); - if (dataDir == null) { - dataDir = "."; - if (System.getenv("HOME") != null) { - dataDir = System.getenv("HOME") + "/.local/share"; - } - } - dataDir += "/vmrunner/" + vm.name; + dataDir = FsdUtils.dataHome(Runner.APP_NAME).resolve(vm.name); } - Path dataPath = Path.of(dataDir); - if (!Files.exists(dataPath)) { - dataPath.toFile().mkdirs(); + if (!Files.exists(dataDir)) { + dataDir.toFile().mkdirs(); } - if (!Files.isDirectory(dataPath) || !Files.isWritable(dataPath)) { + if (!Files.isDirectory(dataDir) || !Files.isWritable(dataDir)) { logger.severe(() -> String.format( "Configured data directory \"%s\"" + " does not exist or isn't writable.", @@ -186,7 +171,7 @@ class Configuration implements Dto { } // Try to read stored uuid. - Path uuidPath = Path.of(dataDir, "uuid.txt"); + Path uuidPath = dataDir.resolve("uuid.txt"); if (Files.isReadable(uuidPath)) { try { var stored 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 23b7ecc..539c24b 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 @@ -32,6 +32,7 @@ import freemarker.template.TemplateNotFoundException; import java.io.File; import java.io.FileDescriptor; import java.io.IOException; +import java.io.InputStream; import java.io.StringWriter; import java.lang.reflect.UndeclaredThrowableException; import java.nio.file.Files; @@ -41,6 +42,7 @@ import java.util.HashMap; import java.util.Map; import java.util.Optional; import java.util.logging.Level; +import java.util.logging.LogManager; import java.util.logging.Logger; import org.apache.commons.cli.CommandLine; import org.apache.commons.cli.CommandLineParser; @@ -49,6 +51,7 @@ import org.apache.commons.cli.Option; import org.apache.commons.cli.Options; import org.jdrupes.vmoperator.runner.qemu.StateController.State; import org.jdrupes.vmoperator.util.ExtendedObjectWrapper; +import org.jdrupes.vmoperator.util.FsdUtils; import org.jgrapes.core.Channel; import org.jgrapes.core.Component; import org.jgrapes.core.Components; @@ -142,10 +145,12 @@ import org.jgrapes.util.events.WatchFile; * @enduml * */ -@SuppressWarnings("PMD.ExcessiveImports") +@SuppressWarnings({ "PMD.ExcessiveImports", "PMD.AvoidPrintStackTrace" }) public class Runner extends Component { - private static final String TEMPLATE_DIR = "/usr/share/vmrunner/templates"; + public static final String APP_NAME = "vmrunner"; + private static final String TEMPLATE_DIR + = "/usr/share/" + APP_NAME + "/templates"; private static final String DEFAULT_TEMPLATE = "Standard-VM-latest.ftl.yaml"; private static final String SAVED_TEMPLATE = "VM.ftl.yaml"; @@ -167,6 +172,7 @@ public class Runner extends Component { * * @throws IOException Signals that an I/O exception has occurred. */ + @SuppressWarnings("PMD.SystemPrintln") public Runner(CommandLine cmdLine) throws IOException { state = new StateController(this); mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, @@ -195,9 +201,13 @@ public class Runner extends Component { attach(qemuMonitor = new QemuMonitor(channel())); // Configuration store with file in /etc (default) - File config = new File(cmdLine.getOptionValue('C', - "/etc/vmrunner/config.yaml")); - attach(new YamlConfigurationStore(channel(), config, false)); + File config = new File(cmdLine.getOptionValue('c', + "/etc/" + APP_NAME + "/config.yaml")); + try { + attach(new YamlConfigurationStore(channel(), config, false)); + } catch (IOException e) { + System.err.println("Cannot open configuration file " + config); + } fire(new WatchFile(config.toPath())); } @@ -264,7 +274,7 @@ public class Runner extends Component { } } // Get file for firmware vars, if necessary - config.firmwareVars = Path.of(config.dataDir, FW_VARS); + config.firmwareVars = config.dataDir.resolve(FW_VARS); if (!Files.exists(config.firmwareVars)) { for (var p : firmware.path("vars")) { var path = Path.of(p.asText()); @@ -281,7 +291,7 @@ public class Runner extends Component { MalformedTemplateNameException, ParseException, TemplateException, JsonProcessingException, JsonMappingException { // Try saved template, copy if not there (or to be updated) - Path templatePath = Path.of(config.dataDir, SAVED_TEMPLATE); + Path templatePath = config.dataDir.resolve(SAVED_TEMPLATE); if (!Files.isReadable(templatePath) || config.updateTemplate) { // Get template Path sourcePath = Paths.get(TEMPLATE_DIR).resolve(Optional @@ -323,7 +333,7 @@ public class Runner extends Component { // Store process id try (var pidFile = Files.newBufferedWriter( - Path.of(config.runtimeDir, "runner.pid"))) { + config.runtimeDir.resolve("runner.pid"))) { pidFile.write(ProcessHandle.current().pid() + "\n"); } @@ -394,7 +404,7 @@ public class Runner extends Component { .ifPresent(procDef -> { channel.setAssociated(CommandDefinition.class, procDef); try (var pidFile = Files.newBufferedWriter( - Path.of(config.runtimeDir, procDef.name + ".pid"))) { + config.runtimeDir.resolve(procDef.name + ".pid"))) { pidFile.write(channel.process().toHandle().pid() + "\n"); } catch (IOException e) { throw new UndeclaredThrowableException(e); @@ -472,6 +482,22 @@ public class Runner extends Component { state.set(State.TERMINATING); } + static { + try { + InputStream props; + var path = FsdUtils.findConfigFile(Runner.APP_NAME, + "logging.properties"); + if (path.isPresent()) { + props = Files.newInputStream(path.get()); + } else { + props = Runner.class.getResourceAsStream("logging.properties"); + } + LogManager.getLogManager().readConfiguration(props); + } catch (IOException e) { + e.printStackTrace(); + } + } + /** * The main method. * @@ -483,7 +509,7 @@ public class Runner extends Component { CommandLineParser parser = new DefaultParser(); // parse the command line arguments final Options options = new Options(); - options.addOption(new Option("C", "config", true, "The confi" + options.addOption(new Option("c", "config", true, "The confi" + "guration file (defaults to /etc/vmrunner/config.yaml).")); CommandLine cmd = parser.parse(options, args); var app = new Runner(cmd);