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 {
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'
// }

View file

@ -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

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.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 = FsdUtils.runtimeDir(Runner.APP_NAME).resolve(vm.name);
swtpmSocket = runtimeDir.resolve("swtpm-sock");
monitorSocket = runtimeDir.resolve("monitor.sock");
}
if (!Files.exists(runtimeDir)) {
runtimeDir.toFile().mkdirs();
}
runtimeDir += "/vmrunner/" + vm.name;
swtpmSocket = Path.of(runtimeDir, "swtpm-sock");
monitorSocket = Path.of(runtimeDir, "monitor.sock");
}
Path runtimePath = Path.of(runtimeDir);
if (!Files.exists(runtimePath)) {
runtimePath.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 = FsdUtils.dataHome(Runner.APP_NAME).resolve(vm.name);
}
if (!Files.exists(dataDir)) {
dataDir.toFile().mkdirs();
}
dataDir += "/vmrunner/" + vm.name;
}
Path dataPath = Path.of(dataDir);
if (!Files.exists(dataPath)) {
dataPath.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

View file

@ -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"));
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);