Improved directory handling and logging configuration.
This commit is contained in:
parent
601c17c6d1
commit
f4d3905f52
5 changed files with 76 additions and 41 deletions
|
|
@ -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'
|
||||||
// }
|
// }
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
@ -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
|
||||||
|
|
@ -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");
|
|
||||||
}
|
}
|
||||||
|
if (!Files.exists(runtimeDir)) {
|
||||||
|
runtimeDir.toFile().mkdirs();
|
||||||
}
|
}
|
||||||
runtimeDir += "/vmrunner/" + vm.name;
|
if (!Files.isDirectory(runtimeDir) || !Files.isWritable(runtimeDir)) {
|
||||||
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)) {
|
|
||||||
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";
|
|
||||||
}
|
}
|
||||||
|
if (!Files.exists(dataDir)) {
|
||||||
|
dataDir.toFile().mkdirs();
|
||||||
}
|
}
|
||||||
dataDir += "/vmrunner/" + vm.name;
|
if (!Files.isDirectory(dataDir) || !Files.isWritable(dataDir)) {
|
||||||
}
|
|
||||||
Path dataPath = Path.of(dataDir);
|
|
||||||
if (!Files.exists(dataPath)) {
|
|
||||||
dataPath.toFile().mkdirs();
|
|
||||||
}
|
|
||||||
if (!Files.isDirectory(dataPath) || !Files.isWritable(dataPath)) {
|
|
||||||
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
|
||||||
|
|
|
||||||
|
|
@ -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"));
|
||||||
|
try {
|
||||||
attach(new YamlConfigurationStore(channel(), config, false));
|
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);
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue