Improved directory handling and logging configuration.

This commit is contained in:
Michael Lipp 2023-06-09 15:19:48 +02:00
parent 601c17c6d1
commit f4d3905f52
5 changed files with 76 additions and 41 deletions

View file

@ -24,6 +24,7 @@ dependencies {
} }
application { application {
applicationName = 'vm-runner.qemu'
applicationDefaultJvmArgs = ['-Xms50m', applicationDefaultJvmArgs = ['-Xms50m',
'-Djava.util.logging.manager=org.jdrupes.vmoperator.util.LongLoggingManager' '-Djava.util.logging.manager=org.jdrupes.vmoperator.util.LongLoggingManager'
] ]
@ -33,7 +34,7 @@ application {
distributions { distributions {
main { main {
distributionBaseName = 'runner-qemu' //distributionBaseName = 'runner-qemu'
// contents { // contents {
// from 'src/readme' // from 'src/readme'
// } // }

View file

@ -26,4 +26,4 @@ org.jdrupes.vmoperator.runner.qemu.level=FINE
java.util.logging.ConsoleHandler.level=ALL java.util.logging.ConsoleHandler.level=ALL
java.util.logging.ConsoleHandler.formatter=java.util.logging.SimpleFormatter 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

View file

@ -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 <http://www.gnu.org/licenses/>.
#
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

View file

@ -26,6 +26,7 @@ import java.util.UUID;
import java.util.logging.Level; import java.util.logging.Level;
import java.util.logging.Logger; import java.util.logging.Logger;
import org.jdrupes.vmoperator.util.Dto; import org.jdrupes.vmoperator.util.Dto;
import org.jdrupes.vmoperator.util.FsdUtils;
/** /**
* The configuration information from the configuration file. * The configuration information from the configuration file.
@ -34,8 +35,8 @@ class Configuration implements Dto {
@SuppressWarnings("PMD.FieldNamingConventions") @SuppressWarnings("PMD.FieldNamingConventions")
protected final Logger logger = Logger.getLogger(getClass().getName()); protected final Logger logger = Logger.getLogger(getClass().getName());
public String dataDir; public Path dataDir;
public String runtimeDir; public Path runtimeDir;
public String template; public String template;
public boolean updateTemplate; public boolean updateTemplate;
public Path swtpmSocket; public Path swtpmSocket;
@ -127,22 +128,14 @@ class Configuration implements Dto {
private boolean checkRuntimeDir() { private boolean checkRuntimeDir() {
// Runtime directory (sockets) // Runtime directory (sockets)
if (runtimeDir == null) { if (runtimeDir == null) {
runtimeDir = System.getenv("XDG_RUNTIME_DIR"); runtimeDir = FsdUtils.runtimeDir(Runner.APP_NAME).resolve(vm.name);
if (runtimeDir == null) { swtpmSocket = runtimeDir.resolve("swtpm-sock");
runtimeDir = "/tmp"; monitorSocket = runtimeDir.resolve("monitor.sock");
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");
} }
Path runtimePath = Path.of(runtimeDir); if (!Files.exists(runtimeDir)) {
if (!Files.exists(runtimePath)) { runtimeDir.toFile().mkdirs();
runtimePath.toFile().mkdirs();
} }
if (!Files.isDirectory(runtimePath) || !Files.isWritable(runtimePath)) { if (!Files.isDirectory(runtimeDir) || !Files.isWritable(runtimeDir)) {
logger.severe(() -> String.format( logger.severe(() -> String.format(
"Configured runtime directory \"%s\"" "Configured runtime directory \"%s\""
+ " does not exist or isn't writable.", + " does not exist or isn't writable.",
@ -156,20 +149,12 @@ class Configuration implements Dto {
private boolean checkDataDir() { private boolean checkDataDir() {
// Data directory // Data directory
if (dataDir == null) { if (dataDir == null) {
dataDir = System.getenv("XDG_DATA_HOME"); dataDir = FsdUtils.dataHome(Runner.APP_NAME).resolve(vm.name);
if (dataDir == null) {
dataDir = ".";
if (System.getenv("HOME") != null) {
dataDir = System.getenv("HOME") + "/.local/share";
}
}
dataDir += "/vmrunner/" + vm.name;
} }
Path dataPath = Path.of(dataDir); if (!Files.exists(dataDir)) {
if (!Files.exists(dataPath)) { dataDir.toFile().mkdirs();
dataPath.toFile().mkdirs();
} }
if (!Files.isDirectory(dataPath) || !Files.isWritable(dataPath)) { if (!Files.isDirectory(dataDir) || !Files.isWritable(dataDir)) {
logger.severe(() -> String.format( logger.severe(() -> String.format(
"Configured data directory \"%s\"" "Configured data directory \"%s\""
+ " does not exist or isn't writable.", + " does not exist or isn't writable.",
@ -186,7 +171,7 @@ class Configuration implements Dto {
} }
// Try to read stored uuid. // Try to read stored uuid.
Path uuidPath = Path.of(dataDir, "uuid.txt"); Path uuidPath = dataDir.resolve("uuid.txt");
if (Files.isReadable(uuidPath)) { if (Files.isReadable(uuidPath)) {
try { try {
var stored var stored

View file

@ -32,6 +32,7 @@ import freemarker.template.TemplateNotFoundException;
import java.io.File; import java.io.File;
import java.io.FileDescriptor; import java.io.FileDescriptor;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream;
import java.io.StringWriter; import java.io.StringWriter;
import java.lang.reflect.UndeclaredThrowableException; import java.lang.reflect.UndeclaredThrowableException;
import java.nio.file.Files; import java.nio.file.Files;
@ -41,6 +42,7 @@ import java.util.HashMap;
import java.util.Map; import java.util.Map;
import java.util.Optional; import java.util.Optional;
import java.util.logging.Level; import java.util.logging.Level;
import java.util.logging.LogManager;
import java.util.logging.Logger; import java.util.logging.Logger;
import org.apache.commons.cli.CommandLine; import org.apache.commons.cli.CommandLine;
import org.apache.commons.cli.CommandLineParser; import org.apache.commons.cli.CommandLineParser;
@ -49,6 +51,7 @@ import org.apache.commons.cli.Option;
import org.apache.commons.cli.Options; import org.apache.commons.cli.Options;
import org.jdrupes.vmoperator.runner.qemu.StateController.State; import org.jdrupes.vmoperator.runner.qemu.StateController.State;
import org.jdrupes.vmoperator.util.ExtendedObjectWrapper; import org.jdrupes.vmoperator.util.ExtendedObjectWrapper;
import org.jdrupes.vmoperator.util.FsdUtils;
import org.jgrapes.core.Channel; import org.jgrapes.core.Channel;
import org.jgrapes.core.Component; import org.jgrapes.core.Component;
import org.jgrapes.core.Components; import org.jgrapes.core.Components;
@ -142,10 +145,12 @@ import org.jgrapes.util.events.WatchFile;
* @enduml * @enduml
* *
*/ */
@SuppressWarnings("PMD.ExcessiveImports") @SuppressWarnings({ "PMD.ExcessiveImports", "PMD.AvoidPrintStackTrace" })
public class Runner extends Component { 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 private static final String DEFAULT_TEMPLATE
= "Standard-VM-latest.ftl.yaml"; = "Standard-VM-latest.ftl.yaml";
private static final String SAVED_TEMPLATE = "VM.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. * @throws IOException Signals that an I/O exception has occurred.
*/ */
@SuppressWarnings("PMD.SystemPrintln")
public Runner(CommandLine cmdLine) throws IOException { public Runner(CommandLine cmdLine) throws IOException {
state = new StateController(this); state = new StateController(this);
mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES,
@ -195,9 +201,13 @@ public class Runner extends Component {
attach(qemuMonitor = new QemuMonitor(channel())); attach(qemuMonitor = new QemuMonitor(channel()));
// Configuration store with file in /etc (default) // Configuration store with file in /etc (default)
File config = new File(cmdLine.getOptionValue('C', File config = new File(cmdLine.getOptionValue('c',
"/etc/vmrunner/config.yaml")); "/etc/" + APP_NAME + "/config.yaml"));
attach(new YamlConfigurationStore(channel(), config, false)); try {
attach(new YamlConfigurationStore(channel(), config, false));
} catch (IOException e) {
System.err.println("Cannot open configuration file " + config);
}
fire(new WatchFile(config.toPath())); fire(new WatchFile(config.toPath()));
} }
@ -264,7 +274,7 @@ public class Runner extends Component {
} }
} }
// Get file for firmware vars, if necessary // 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)) { if (!Files.exists(config.firmwareVars)) {
for (var p : firmware.path("vars")) { for (var p : firmware.path("vars")) {
var path = Path.of(p.asText()); var path = Path.of(p.asText());
@ -281,7 +291,7 @@ public class Runner extends Component {
MalformedTemplateNameException, ParseException, TemplateException, MalformedTemplateNameException, ParseException, TemplateException,
JsonProcessingException, JsonMappingException { JsonProcessingException, JsonMappingException {
// Try saved template, copy if not there (or to be updated) // 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) { if (!Files.isReadable(templatePath) || config.updateTemplate) {
// Get template // Get template
Path sourcePath = Paths.get(TEMPLATE_DIR).resolve(Optional Path sourcePath = Paths.get(TEMPLATE_DIR).resolve(Optional
@ -323,7 +333,7 @@ public class Runner extends Component {
// Store process id // Store process id
try (var pidFile = Files.newBufferedWriter( try (var pidFile = Files.newBufferedWriter(
Path.of(config.runtimeDir, "runner.pid"))) { config.runtimeDir.resolve("runner.pid"))) {
pidFile.write(ProcessHandle.current().pid() + "\n"); pidFile.write(ProcessHandle.current().pid() + "\n");
} }
@ -394,7 +404,7 @@ public class Runner extends Component {
.ifPresent(procDef -> { .ifPresent(procDef -> {
channel.setAssociated(CommandDefinition.class, procDef); channel.setAssociated(CommandDefinition.class, procDef);
try (var pidFile = Files.newBufferedWriter( 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"); pidFile.write(channel.process().toHandle().pid() + "\n");
} catch (IOException e) { } catch (IOException e) {
throw new UndeclaredThrowableException(e); throw new UndeclaredThrowableException(e);
@ -472,6 +482,22 @@ public class Runner extends Component {
state.set(State.TERMINATING); 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. * The main method.
* *
@ -483,7 +509,7 @@ public class Runner extends Component {
CommandLineParser parser = new DefaultParser(); CommandLineParser parser = new DefaultParser();
// parse the command line arguments // parse the command line arguments
final Options options = new Options(); 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).")); + "guration file (defaults to /etc/vmrunner/config.yaml)."));
CommandLine cmd = parser.parse(options, args); CommandLine cmd = parser.parse(options, args);
var app = new Runner(cmd); var app = new Runner(cmd);