forked from cockpit/cockpit
713 lines
34 KiB
Diff
713 lines
34 KiB
Diff
|
From b61b273451987a283825a585a4f40901be12b11c Mon Sep 17 00:00:00 2001
|
||
|
From: Jacek Tomasiak <jacek.tomasiak@gmail.com>
|
||
|
Date: Tue, 12 Jul 2022 01:38:04 +0200
|
||
|
Subject: [PATCH 1/4] kdump: Refactor config handling
|
||
|
|
||
|
Config handling was modified to decouple UI from config file format.
|
||
|
All of the platform-specific code was moved to config-client.js and
|
||
|
"abstract" settings model was added as an interface.
|
||
|
In addition, NFS UI page was modified to not require NFS mounts in
|
||
|
specific format but as separate "server" and "export" fields.
|
||
|
---
|
||
|
pkg/kdump/config-client.js | 186 +++++++++++++++++++++++++++++++++++--
|
||
|
pkg/kdump/kdump-client.js | 87 ++++-------------
|
||
|
pkg/kdump/kdump-view.jsx | 125 ++++++++-----------------
|
||
|
3 files changed, 237 insertions(+), 161 deletions(-)
|
||
|
|
||
|
diff --git a/pkg/kdump/config-client.js b/pkg/kdump/config-client.js
|
||
|
index 39913bb6c86..d292bd9bebd 100644
|
||
|
--- a/pkg/kdump/config-client.js
|
||
|
+++ b/pkg/kdump/config-client.js
|
||
|
@@ -19,6 +19,12 @@
|
||
|
|
||
|
import cockpit from 'cockpit';
|
||
|
|
||
|
+const deprecatedKeys = ["net", "options", "link_delay", "disk_timeout", "debug_mem_level", "blacklist"];
|
||
|
+const knownKeys = [
|
||
|
+ "raw", "nfs", "ssh", "sshkey", "path", "core_collector", "kdump_post", "kdump_pre", "extra_bins", "extra_modules",
|
||
|
+ "default", "force_rebuild", "override_resettable", "dracut_args", "fence_kdump_args", "fence_kdump_nodes"
|
||
|
+];
|
||
|
+
|
||
|
/* Parse an ini-style config file
|
||
|
* and monitor it for changes
|
||
|
*/
|
||
|
@@ -82,7 +88,12 @@ export class ConfigFile {
|
||
|
// parse the config file
|
||
|
this._lines = rawContent.split(/\r?\n/);
|
||
|
|
||
|
- this.settings = { };
|
||
|
+ // this is the format expected by the UI
|
||
|
+ this.settings = {
|
||
|
+ _internal: {},
|
||
|
+ targets: {},
|
||
|
+ compression: { enabled: false, allowed: false, },
|
||
|
+ };
|
||
|
this._lines.forEach((line, index) => {
|
||
|
const trimmed = line.trim();
|
||
|
// if the line is empty or only a comment, skip
|
||
|
@@ -103,7 +114,7 @@ export class ConfigFile {
|
||
|
comment = value.substring(commentIndex).trim();
|
||
|
value = value.substring(0, commentIndex).trim();
|
||
|
}
|
||
|
- this.settings[key] = {
|
||
|
+ this.settings._internal[key] = {
|
||
|
index: index,
|
||
|
value: value,
|
||
|
origLine: line,
|
||
|
@@ -113,21 +124,182 @@ export class ConfigFile {
|
||
|
|
||
|
// make sure we copy the original keys so we overwrite the correct lines when saving
|
||
|
this._originalSettings = { };
|
||
|
- Object.keys(this.settings).forEach((key) => {
|
||
|
- this._originalSettings[key] = { ...this.settings[key] };
|
||
|
+ Object.keys(this.settings._internal).forEach((key) => {
|
||
|
+ this._originalSettings[key] = { ...this.settings._internal[key] };
|
||
|
});
|
||
|
+
|
||
|
+ this._extractSettings();
|
||
|
+
|
||
|
if (!skipNotify)
|
||
|
this.dispatchEvent("kdumpConfigChanged", this.settings);
|
||
|
}
|
||
|
|
||
|
+ /* extract settings managed by cockpit from _internal into platform independent model
|
||
|
+ */
|
||
|
+ _extractSettings() {
|
||
|
+ // "path" applies to all targets
|
||
|
+ const path = this.settings._internal.path || { value: "" };
|
||
|
+
|
||
|
+ Object.keys(this.settings._internal).forEach((key) => {
|
||
|
+ if (key === "nfs") {
|
||
|
+ // split nfs line into server and export parts
|
||
|
+ const parts = this.settings._internal.nfs.value.match(/^([^[][^:]+|\[[^\]]+\]):(.*)$/);
|
||
|
+ if (!parts)
|
||
|
+ return;
|
||
|
+ this.settings.targets.nfs = {
|
||
|
+ type: key,
|
||
|
+ path: path.value,
|
||
|
+ server: parts[1],
|
||
|
+ export: parts[2],
|
||
|
+ };
|
||
|
+ } else if (key === "ssh") {
|
||
|
+ this.settings.targets.ssh = {
|
||
|
+ type: key,
|
||
|
+ path: path.value,
|
||
|
+ server: this.settings._internal.ssh.value,
|
||
|
+ };
|
||
|
+ if ("sshkey" in this.settings._internal)
|
||
|
+ this.settings.targets.ssh.sshkey = this.settings._internal.sshkey.value;
|
||
|
+ } else if (key === "raw") {
|
||
|
+ this.settings.targets.raw = {
|
||
|
+ type: key,
|
||
|
+ partition: this.settings._internal.raw.value
|
||
|
+ };
|
||
|
+ } else {
|
||
|
+ // probably local, but we might also have a mount
|
||
|
+ // check against known keys, the ones left over may be a mount target
|
||
|
+ // if the key is empty or known, we don't care about it here
|
||
|
+ if (!key || key in knownKeys || key in deprecatedKeys)
|
||
|
+ return;
|
||
|
+ // if we have a UUID, LABEL or /dev in the value, we can be pretty sure it's a mount option
|
||
|
+ const value = JSON.stringify(this.settings._internal[key]).toLowerCase();
|
||
|
+ if (value.indexOf("uuid") > -1 || value.indexOf("label") > -1 || value.indexOf("/dev") > -1) {
|
||
|
+ this.settings.targets.mount = {
|
||
|
+ type: "mount",
|
||
|
+ path: path.value,
|
||
|
+ fsType: key,
|
||
|
+ partition: this.settings._internal[key].value,
|
||
|
+ };
|
||
|
+ } else {
|
||
|
+ // TODO: check for know filesystem types here
|
||
|
+ }
|
||
|
+ }
|
||
|
+ });
|
||
|
+
|
||
|
+ // default to local if no target configured
|
||
|
+ if (Object.keys(this.settings.targets).length === 0)
|
||
|
+ this.settings.targets.local = { type: "local", path: path.value };
|
||
|
+
|
||
|
+ // only allow compression if there is no core collector set or it's set to makedumpfile
|
||
|
+ this.settings.compression.allowed = (
|
||
|
+ !("core_collector" in this.settings._internal) ||
|
||
|
+ (this.settings._internal.core_collector.value.trim().indexOf("makedumpfile") === 0)
|
||
|
+ );
|
||
|
+ // compression is enabled if we have a core_collector command with the "-c" parameter
|
||
|
+ this.settings.compression.enabled = (
|
||
|
+ ("core_collector" in this.settings._internal) &&
|
||
|
+ this.settings._internal.core_collector.value &&
|
||
|
+ (this.settings._internal.core_collector.value.split(" ").indexOf("-c") != -1)
|
||
|
+ );
|
||
|
+ }
|
||
|
+
|
||
|
+ /* update single _internal setting to given value
|
||
|
+ * make sure setting exists if value is not empty
|
||
|
+ */
|
||
|
+ _updateSetting(settings, key, value) {
|
||
|
+ if (key in settings._internal) {
|
||
|
+ if (value)
|
||
|
+ settings._internal[key].value = value;
|
||
|
+ else
|
||
|
+ delete settings._internal[key];
|
||
|
+ } else {
|
||
|
+ if (value)
|
||
|
+ settings._internal[key] = { value: value };
|
||
|
+ }
|
||
|
+ }
|
||
|
+
|
||
|
+ /* transform settings from model back to _internal format
|
||
|
+ * this.settings = current state from file
|
||
|
+ * settings = in-memory state from UI
|
||
|
+ */
|
||
|
+ _persistSettings(settings) {
|
||
|
+ // target
|
||
|
+ if (Object.keys(settings.targets).length > 0) {
|
||
|
+ const target = Object.values(settings.targets)[0];
|
||
|
+ this._updateSetting(settings, "path", target.path);
|
||
|
+
|
||
|
+ // wipe old target settings
|
||
|
+ for (const key in this.settings.targets) {
|
||
|
+ const oldTarget = this.settings.targets[key];
|
||
|
+ if (oldTarget.type == "mount") {
|
||
|
+ delete settings._internal[oldTarget.fsType];
|
||
|
+ } else {
|
||
|
+ delete settings._internal[key];
|
||
|
+ }
|
||
|
+ }
|
||
|
+
|
||
|
+ if (target.type === "nfs") {
|
||
|
+ this._updateSetting(settings, "nfs", [target.server, target.export].join(":"));
|
||
|
+ } else if (target.type === "ssh") {
|
||
|
+ this._updateSetting(settings, "ssh", target.server);
|
||
|
+ if ("sshkey" in target)
|
||
|
+ this._updateSetting(settings, "sshkey", target.sshkey);
|
||
|
+ } else if (target.type === "raw") {
|
||
|
+ this._updateSetting(settings, "raw", target.partition);
|
||
|
+ } else if (target.type === "mount") {
|
||
|
+ this._updateSetting(settings, target.fsType, target.partition);
|
||
|
+ }
|
||
|
+
|
||
|
+ /* ssh target needs a flattened vmcore for transport */
|
||
|
+ if ("core_collector" in settings._internal &&
|
||
|
+ settings._internal.core_collector.value.includes("makedumpfile")) {
|
||
|
+ if (target.type === "ssh" && !settings._internal.core_collector.value.includes("-F"))
|
||
|
+ settings._internal.core_collector.value += " -F";
|
||
|
+ else if (settings._internal.core_collector.value.includes("-F"))
|
||
|
+ settings._internal.core_collector.value =
|
||
|
+ settings._internal.core_collector.value
|
||
|
+ .split(" ")
|
||
|
+ .filter(e => e != "-F")
|
||
|
+ .join(" ");
|
||
|
+ }
|
||
|
+ }
|
||
|
+ // compression
|
||
|
+ if (this.settings.compression.enabled != settings.compression.enabled) {
|
||
|
+ if (settings.compression.enabled) {
|
||
|
+ // enable compression
|
||
|
+ if ("core_collector" in settings._internal)
|
||
|
+ settings._internal.core_collector.value = settings._internal.core_collector.value + " -c";
|
||
|
+ else
|
||
|
+ settings._internal.core_collector = { value: "makedumpfile -c" };
|
||
|
+ } else {
|
||
|
+ // disable compression
|
||
|
+ if ("core_collector" in this.settings._internal) {
|
||
|
+ // just remove all "-c" parameters
|
||
|
+ settings._internal.core_collector.value =
|
||
|
+ settings._internal.core_collector.value
|
||
|
+ .split(" ")
|
||
|
+ .filter((e) => { return (e != "-c") })
|
||
|
+ .join(" ");
|
||
|
+ } else {
|
||
|
+ // if we don't have anything on this in the original settings,
|
||
|
+ // we can get rid of the entry altogether
|
||
|
+ delete settings._internal.core_collector;
|
||
|
+ }
|
||
|
+ }
|
||
|
+ }
|
||
|
+ return settings;
|
||
|
+ }
|
||
|
+
|
||
|
/* generate the config file from raw text and settings
|
||
|
*/
|
||
|
_generateConfig(settings) {
|
||
|
+ settings = this._persistSettings(settings);
|
||
|
+
|
||
|
const lines = this._lines.slice(0);
|
||
|
const linesToDelete = [];
|
||
|
// first find the settings lines that have been disabled/deleted
|
||
|
Object.keys(this._originalSettings).forEach((key) => {
|
||
|
- if (!(key in settings) || !(key in settings && settings[key].value)) {
|
||
|
+ if (!(key in settings._internal) || !(key in settings._internal && settings._internal[key].value)) {
|
||
|
const origEntry = this._originalSettings[key];
|
||
|
// if the line had a comment, keep it, otherwise delete
|
||
|
if (origEntry.comment !== undefined)
|
||
|
@@ -138,8 +310,8 @@ export class ConfigFile {
|
||
|
});
|
||
|
|
||
|
// we take the lines from our last read operation and modify them with the new settings
|
||
|
- Object.keys(settings).forEach((key) => {
|
||
|
- const entry = settings[key];
|
||
|
+ Object.keys(settings._internal).forEach((key) => {
|
||
|
+ const entry = settings._internal[key];
|
||
|
let line = key + " " + entry.value;
|
||
|
if (entry.comment)
|
||
|
line = line + " " + entry.comment;
|
||
|
diff --git a/pkg/kdump/kdump-client.js b/pkg/kdump/kdump-client.js
|
||
|
index a161fc25214..d001ebb0b5a 100644
|
||
|
--- a/pkg/kdump/kdump-client.js
|
||
|
+++ b/pkg/kdump/kdump-client.js
|
||
|
@@ -25,12 +25,6 @@ import crashKernelScript from 'raw-loader!./crashkernel.sh';
|
||
|
import testWritableScript from 'raw-loader!./testwritable.sh';
|
||
|
const _ = cockpit.gettext;
|
||
|
|
||
|
-const deprecatedKeys = ["net", "options", "link_delay", "disk_timeout", "debug_mem_level", "blacklist"];
|
||
|
-const knownKeys = [
|
||
|
- "raw", "nfs", "ssh", "sshkey", "path", "core_collector", "kdump_post", "kdump_pre", "extra_bins", "extra_modules",
|
||
|
- "default", "force_rebuild", "override_resettable", "dracut_args", "fence_kdump_args", "fence_kdump_nodes"
|
||
|
-];
|
||
|
-
|
||
|
/* initializes the kdump status
|
||
|
* emits "kdumpStatusChanged" when the status changes, along with a status object:
|
||
|
* {
|
||
|
@@ -40,7 +34,7 @@ const knownKeys = [
|
||
|
* config: settings from kdump.conf
|
||
|
* target: dump target info, content depends on dump type
|
||
|
* always contains the keys:
|
||
|
- * target value in ["local", "nfs", "ssh", "raw", "mount", "unknown"]
|
||
|
+ * type value in ["local", "nfs", "ssh", "raw", "mount", "unknown"]
|
||
|
* multipleTargets true if the config file has more than one target defined, false otherwise
|
||
|
* }
|
||
|
*
|
||
|
@@ -106,19 +100,24 @@ export class KdumpClient {
|
||
|
path = "/var/crash";
|
||
|
|
||
|
return new Promise((resolve, reject) => {
|
||
|
- if (target.target === "local") {
|
||
|
+ if (target.type === "local") {
|
||
|
// local path, try to see if we can write
|
||
|
cockpit.script(testWritableScript, [path], { superuser: "try" })
|
||
|
.then(resolve)
|
||
|
.catch(() => reject(cockpit.format(_("Directory $0 isn't writable or doesn't exist."), path)));
|
||
|
return;
|
||
|
- } else if (target.target === "nfs") {
|
||
|
- if (!target.nfs.value.match("\\S+:/.+"))
|
||
|
- reject(_("nfs dump target isn't formatted as server:path"));
|
||
|
- } else if (target.target === "ssh") {
|
||
|
- if (!target.ssh.value.trim())
|
||
|
+ } else if (target.type === "nfs") {
|
||
|
+ if (!target.server || !target.server.trim())
|
||
|
+ reject(_("nfs server is empty"));
|
||
|
+ // IPv6 must be enclosed in square brackets
|
||
|
+ if (target.server.trim().match(/^\[.*[^\]]$/))
|
||
|
+ reject(_("nfs server is not valid IPv6"));
|
||
|
+ if (!target.export || !target.export.trim())
|
||
|
+ reject(_("nfs export is empty"));
|
||
|
+ } else if (target.type === "ssh") {
|
||
|
+ if (!target.server || !target.server.trim())
|
||
|
reject(_("ssh server is empty"));
|
||
|
- if (target.sshkey && !target.sshkey.value.match("/.+"))
|
||
|
+ if (target.sshkey && !target.sshkey.match("/.+"))
|
||
|
reject(_("ssh key isn't a path"));
|
||
|
}
|
||
|
|
||
|
@@ -149,67 +148,17 @@ export class KdumpClient {
|
||
|
}
|
||
|
|
||
|
targetFromSettings(settings) {
|
||
|
- // since local target is the default and can be used even without "path", we need to
|
||
|
- // check for the presence of all known targets
|
||
|
- // we have the additional difficulty that partitions don't have a good config key, since their
|
||
|
- // lines begin with the fs_type
|
||
|
const target = {
|
||
|
- target: "unknown",
|
||
|
+ type: "unknown",
|
||
|
multipleTargets: false,
|
||
|
};
|
||
|
|
||
|
- if (!settings)
|
||
|
+ if (!settings || Object.keys(settings.targets).length === 0)
|
||
|
return target;
|
||
|
|
||
|
- if ("nfs" in settings) {
|
||
|
- if (target.target != "unknown")
|
||
|
- target.multipleTargets = true;
|
||
|
- target.target = "nfs";
|
||
|
- target.nfs = settings.nfs;
|
||
|
- if ("path" in settings)
|
||
|
- target.path = settings.path;
|
||
|
- } else if ("ssh" in settings) {
|
||
|
- if (target.target != "unknown")
|
||
|
- target.multipleTargets = true;
|
||
|
- target.target = "ssh";
|
||
|
- target.ssh = settings.ssh;
|
||
|
- target.sshkey = settings.sshkey;
|
||
|
- } else if ("raw" in settings) {
|
||
|
- if (target.target != "unknown")
|
||
|
- target.multipleTargets = true;
|
||
|
- target.target = "raw";
|
||
|
- target.raw = settings.raw;
|
||
|
- } else {
|
||
|
- // probably local, but we might also have a mount
|
||
|
- // check all keys against known keys, the ones left over may be a mount target
|
||
|
- Object.keys(settings).forEach((key) => {
|
||
|
- // if the key is empty or known, we don't care about it here
|
||
|
- if (!key || key in knownKeys || key in deprecatedKeys)
|
||
|
- return;
|
||
|
- // if we have a UUID, LABEL or /dev in the value, we can be pretty sure it's a mount option
|
||
|
- const value = JSON.stringify(settings[key]).toLowerCase();
|
||
|
- if (value.indexOf("uuid") > -1 || value.indexOf("label") > -1 || value.indexOf("/dev") > -1) {
|
||
|
- if (target.target != "unknown")
|
||
|
- target.multipleTargets = true;
|
||
|
- target.target = "mount";
|
||
|
- target.fsType = key;
|
||
|
- target.partition = settings[key].value;
|
||
|
- } else {
|
||
|
- // TODO: check for know filesystem types here
|
||
|
- }
|
||
|
- });
|
||
|
- }
|
||
|
-
|
||
|
- // if no target matches, then we use the local filesystem
|
||
|
- if (target.target == "unknown")
|
||
|
- target.target = "local";
|
||
|
-
|
||
|
- // "path" applies to all targets
|
||
|
- // default to "/var/crash for "
|
||
|
- if ("path" in settings)
|
||
|
- target.path = settings.path.value;
|
||
|
- else if (["local", "ssh", "nfs", "mount"].indexOf(target.target) !== -1)
|
||
|
- target.path = "/var/crash";
|
||
|
+ // copy first target
|
||
|
+ cockpit.extend(target, Object.values(settings.targets)[0]);
|
||
|
+ target.multipleTargets = Object.keys(settings.targets).length > 1;
|
||
|
return target;
|
||
|
}
|
||
|
}
|
||
|
diff --git a/pkg/kdump/kdump-view.jsx b/pkg/kdump/kdump-view.jsx
|
||
|
index 718d8c43bc9..956811d7826 100644
|
||
|
--- a/pkg/kdump/kdump-view.jsx
|
||
|
+++ b/pkg/kdump/kdump-view.jsx
|
||
|
@@ -57,7 +57,7 @@ class KdumpTargetBody extends React.Component {
|
||
|
constructor(props) {
|
||
|
super(props);
|
||
|
this.state = {
|
||
|
- storeDest: this.props.initialTarget.target, // dialog mode, depends on location
|
||
|
+ storeDest: this.props.initialTarget.type, // dialog mode, depends on location
|
||
|
};
|
||
|
this.changeLocation = this.changeLocation.bind(this);
|
||
|
}
|
||
|
@@ -71,15 +71,10 @@ class KdumpTargetBody extends React.Component {
|
||
|
|
||
|
render() {
|
||
|
let detailRows;
|
||
|
- // only allow compression if there is no core collector set or it's set to makedumpfile
|
||
|
- const compressionPossible = (
|
||
|
- !this.props.settings ||
|
||
|
- !("core_collector" in this.props.settings) ||
|
||
|
- (this.props.settings.core_collector.value.trim().indexOf("makedumpfile") === 0)
|
||
|
- );
|
||
|
+ const compressionPossible = !this.props.settings || this.props.settings.compression.allowed;
|
||
|
let directory = "";
|
||
|
- if (this.props.settings && "path" in this.props.settings)
|
||
|
- directory = this.props.settings.path.value;
|
||
|
+ if (this.props.settings && "path" in this.props.settings.targets[this.state.storeDest])
|
||
|
+ directory = this.props.settings.targets[this.state.storeDest].path;
|
||
|
|
||
|
if (this.state.storeDest == "local") {
|
||
|
detailRows = (
|
||
|
@@ -91,15 +86,22 @@ class KdumpTargetBody extends React.Component {
|
||
|
</FormGroup>
|
||
|
);
|
||
|
} else if (this.state.storeDest == "nfs") {
|
||
|
- let nfs = "";
|
||
|
- if (this.props.settings && "nfs" in this.props.settings)
|
||
|
- nfs = this.props.settings.nfs.value;
|
||
|
+ let nfs = {};
|
||
|
+ if (this.props.settings && "nfs" in this.props.settings.targets)
|
||
|
+ nfs = this.props.settings.targets.nfs;
|
||
|
+ const server = nfs.server || "";
|
||
|
+ const exportpath = nfs.export || "";
|
||
|
detailRows = (
|
||
|
<>
|
||
|
- <FormGroup fieldId="kdump-settings-nfs-mount" label={_("Mount")}>
|
||
|
- <TextInput id="kdump-settings-nfs-mount" key="mount"
|
||
|
- placeholder="penguin.example.com:/export/cores" value={nfs}
|
||
|
- onChange={value => this.props.onChange("nfs", value)} />
|
||
|
+ <FormGroup fieldId="kdump-settings-nfs-server" label={_("Server")}>
|
||
|
+ <TextInput id="kdump-settings-nfs-server" key="server"
|
||
|
+ placeholder="penguin.example.com" value={server}
|
||
|
+ onChange={value => this.props.onChange("server", value)} />
|
||
|
+ </FormGroup>
|
||
|
+ <FormGroup fieldId="kdump-settings-nfs-export" label={_("Export")}>
|
||
|
+ <TextInput id="kdump-settings-nfs-export" key="export"
|
||
|
+ placeholder="/export/cores" value={exportpath}
|
||
|
+ onChange={value => this.props.onChange("export", value)} />
|
||
|
</FormGroup>
|
||
|
<FormGroup fieldId="kdump-settings-nfs-directory" label={_("Directory")}>
|
||
|
<TextInput id="kdump-settings-nfs-directory" key="directory"
|
||
|
@@ -110,18 +112,17 @@ class KdumpTargetBody extends React.Component {
|
||
|
</>
|
||
|
);
|
||
|
} else if (this.state.storeDest == "ssh") {
|
||
|
- let ssh = "";
|
||
|
- if (this.props.settings && "ssh" in this.props.settings)
|
||
|
- ssh = this.props.settings.ssh.value;
|
||
|
- let sshkey = "";
|
||
|
- if (this.props.settings && "sshkey" in this.props.settings)
|
||
|
- sshkey = this.props.settings.sshkey.value;
|
||
|
+ let ssh = {};
|
||
|
+ if (this.props.settings && "ssh" in this.props.settings.targets)
|
||
|
+ ssh = this.props.settings.targets.ssh;
|
||
|
+ const server = ssh.server || "";
|
||
|
+ const sshkey = ssh.sshkey || "";
|
||
|
detailRows = (
|
||
|
<>
|
||
|
<FormGroup fieldId="kdump-settings-ssh-server" label={_("Server")}>
|
||
|
<TextInput id="kdump-settings-ssh-server" key="server"
|
||
|
- placeholder="user@server.com" value={ssh}
|
||
|
- onChange={value => this.props.onChange("ssh", value)} />
|
||
|
+ placeholder="user@server.com" value={server}
|
||
|
+ onChange={value => this.props.onChange("server", value)} />
|
||
|
</FormGroup>
|
||
|
|
||
|
<FormGroup fieldId="kdump-settings-ssh-key" label={_("ssh key")}>
|
||
|
@@ -201,78 +202,32 @@ export class KdumpPage extends React.Component {
|
||
|
}
|
||
|
|
||
|
compressionStatus(settings) {
|
||
|
- // compression is enabled if we have a core_collector command with the "-c" parameter
|
||
|
- return (
|
||
|
- settings &&
|
||
|
- ("core_collector" in settings) &&
|
||
|
- settings.core_collector.value &&
|
||
|
- (settings.core_collector.value.split(" ").indexOf("-c") != -1)
|
||
|
- );
|
||
|
+ return settings && settings.compression.enabled;
|
||
|
}
|
||
|
|
||
|
changeSetting(key, value) {
|
||
|
let settings = this.state.dialogSettings;
|
||
|
|
||
|
- // a few special cases, otherwise write to config directly
|
||
|
+ // a few special cases, otherwise write to config target directly
|
||
|
if (key == "compression") {
|
||
|
- if (value) {
|
||
|
- // enable compression
|
||
|
- if ("core_collector" in settings)
|
||
|
- settings.core_collector.value = settings.core_collector.value + " -c";
|
||
|
- else
|
||
|
- settings.core_collector = { value: "makedumpfile -c" };
|
||
|
- } else {
|
||
|
- // disable compression
|
||
|
- if ("core_collector" in this.props.kdumpStatus.config) {
|
||
|
- // just remove all "-c" parameters
|
||
|
- settings.core_collector.value =
|
||
|
- settings.core_collector.value
|
||
|
- .split(" ")
|
||
|
- .filter((e) => { return (e != "-c") })
|
||
|
- .join(" ");
|
||
|
- } else {
|
||
|
- // if we don't have anything on this in the original settings,
|
||
|
- // we can get rid of the entry altogether
|
||
|
- delete settings.core_collector;
|
||
|
- }
|
||
|
- }
|
||
|
+ settings.compression.enabled = value;
|
||
|
} else if (key === "target") {
|
||
|
/* target changed, restore settings and wipe all settings associated
|
||
|
* with a target so no conflicting settings remain */
|
||
|
settings = {};
|
||
|
+ // TODO: do we need a deep copy here?
|
||
|
Object.keys(this.props.kdumpStatus.config).forEach((key) => {
|
||
|
settings[key] = { ...this.props.kdumpStatus.config[key] };
|
||
|
});
|
||
|
- Object.keys(this.props.kdumpStatus.target).forEach((key) => {
|
||
|
- if (settings[key])
|
||
|
- delete settings[key];
|
||
|
- });
|
||
|
- if (value === "ssh")
|
||
|
- settings.ssh = { value: "" };
|
||
|
- else if (value === "nfs")
|
||
|
- settings.nfs = { value: "" };
|
||
|
-
|
||
|
- if ("core_collector" in settings &&
|
||
|
- settings.core_collector.value.includes("makedumpfile")) {
|
||
|
- /* ssh target needs a flattened vmcore for transport */
|
||
|
- if (value === "ssh" && !settings.core_collector.value.includes("-F"))
|
||
|
- settings.core_collector.value += " -F";
|
||
|
- else if (settings.core_collector.value.includes("-F"))
|
||
|
- settings.core_collector.value =
|
||
|
- settings.core_collector.value
|
||
|
- .split(" ")
|
||
|
- .filter(e => e != "-F")
|
||
|
- .join(" ");
|
||
|
- }
|
||
|
+ settings.targets = {};
|
||
|
+ settings.targets[value] = { type: value };
|
||
|
} else if (key !== undefined) {
|
||
|
+ const type = Object.keys(settings.targets)[0];
|
||
|
if (!value) {
|
||
|
- if (settings[key])
|
||
|
- delete settings[key];
|
||
|
+ if (settings.targets[type][key])
|
||
|
+ delete settings.targets[type][key];
|
||
|
} else {
|
||
|
- if (key in settings)
|
||
|
- settings[key].value = value;
|
||
|
- else
|
||
|
- settings[key] = { value: value };
|
||
|
+ settings.targets[type][key] = value;
|
||
|
}
|
||
|
}
|
||
|
this.setState({ dialogSettings: settings });
|
||
|
@@ -391,21 +346,21 @@ export class KdumpPage extends React.Component {
|
||
|
if (target.multipleTargets) {
|
||
|
kdumpLocation = _("invalid: multiple targets defined");
|
||
|
} else {
|
||
|
- if (target.target == "local") {
|
||
|
+ if (target.type == "local") {
|
||
|
if (target.path)
|
||
|
kdumpLocation = cockpit.format(_("locally in $0"), target.path);
|
||
|
else
|
||
|
kdumpLocation = cockpit.format(_("locally in $0"), "/var/crash");
|
||
|
targetCanChange = true;
|
||
|
- } else if (target.target == "ssh") {
|
||
|
+ } else if (target.type == "ssh") {
|
||
|
kdumpLocation = _("Remote over SSH");
|
||
|
targetCanChange = true;
|
||
|
- } else if (target.target == "nfs") {
|
||
|
+ } else if (target.type == "nfs") {
|
||
|
kdumpLocation = _("Remote over NFS");
|
||
|
targetCanChange = true;
|
||
|
- } else if (target.target == "raw") {
|
||
|
+ } else if (target.type == "raw") {
|
||
|
kdumpLocation = _("Raw to a device");
|
||
|
- } else if (target.target == "mount") {
|
||
|
+ } else if (target.type == "mount") {
|
||
|
/* mount targets outside of nfs are too complex for the
|
||
|
* current target dialog */
|
||
|
kdumpLocation = _("On a mounted device");
|
||
|
|
||
|
From 2d045f916a9cff40e38411f8c07487d0b0f65b48 Mon Sep 17 00:00:00 2001
|
||
|
From: Jacek Tomasiak <jacek.tomasiak@gmail.com>
|
||
|
Date: Tue, 12 Jul 2022 12:03:28 +0200
|
||
|
Subject: [PATCH 2/4] kdump: Update config-client test
|
||
|
|
||
|
---
|
||
|
pkg/kdump/test-config-client.js | 16 ++++++++--------
|
||
|
1 file changed, 8 insertions(+), 8 deletions(-)
|
||
|
|
||
|
diff --git a/pkg/kdump/test-config-client.js b/pkg/kdump/test-config-client.js
|
||
|
index 61b10a57cb4..6eb84592d26 100644
|
||
|
--- a/pkg/kdump/test-config-client.js
|
||
|
+++ b/pkg/kdump/test-config-client.js
|
||
|
@@ -49,10 +49,10 @@ QUnit.test("config_update", function (assert) {
|
||
|
const dataWasChanged = new Promise(resolve => { this.dataWasChangedResolve = resolve });
|
||
|
let config;
|
||
|
const configChanged = (event, settings) => {
|
||
|
- assert.equal(settings.foo.value, "moo", "value changed correctly");
|
||
|
- assert.equal("key" in settings, false, "setting with comment deleted correctly");
|
||
|
- assert.equal("will" in settings, false, "setting without comment deleted correctly");
|
||
|
- assert.equal(settings.hooray.value, "value", "value added correctly");
|
||
|
+ assert.equal(settings._internal.foo.value, "moo", "value changed correctly");
|
||
|
+ assert.equal("key" in settings._internal, false, "setting with comment deleted correctly");
|
||
|
+ assert.equal("will" in settings._internal, false, "setting without comment deleted correctly");
|
||
|
+ assert.equal(settings._internal.hooray.value, "value", "value added correctly");
|
||
|
assert.equal(config._rawContent, changedConfig, "raw text for changed config is correct");
|
||
|
this.dataWasChangedResolve();
|
||
|
};
|
||
|
@@ -65,10 +65,10 @@ QUnit.test("config_update", function (assert) {
|
||
|
assert.equal(configFile.path, filename, "file has correct path");
|
||
|
config = new kdump.ConfigFile(filename);
|
||
|
config.wait().then(() => {
|
||
|
- config.settings.foo.value = "moo";
|
||
|
- delete config.settings.key;
|
||
|
- delete config.settings.will;
|
||
|
- config.settings.hooray = { value: "value" };
|
||
|
+ config.settings._internal.foo.value = "moo";
|
||
|
+ delete config.settings._internal.key;
|
||
|
+ delete config.settings._internal.will;
|
||
|
+ config.settings._internal.hooray = { value: "value" };
|
||
|
config.addEventListener('kdumpConfigChanged', configChanged);
|
||
|
config.write(config.settings)
|
||
|
.then(() => dataWasChanged.then(done));
|
||
|
|
||
|
From f94cf930e138681feeda272edef8172a5504cfb9 Mon Sep 17 00:00:00 2001
|
||
|
From: Jacek Tomasiak <jacek.tomasiak@gmail.com>
|
||
|
Date: Tue, 12 Jul 2022 12:21:59 +0200
|
||
|
Subject: [PATCH 3/4] kdump: Update kdump tests
|
||
|
|
||
|
---
|
||
|
test/verify/check-kdump | 16 +++++++++-------
|
||
|
1 file changed, 9 insertions(+), 7 deletions(-)
|
||
|
|
||
|
diff --git a/test/verify/check-kdump b/test/verify/check-kdump
|
||
|
index 5ef7108062c..107c6730dfa 100755
|
||
|
--- a/test/verify/check-kdump
|
||
|
+++ b/test/verify/check-kdump
|
||
|
@@ -109,15 +109,15 @@ class TestKdump(KdumpHelpers):
|
||
|
b.click("#kdump-change-target")
|
||
|
b.wait_visible("#kdump-settings-dialog")
|
||
|
b.set_val("#kdump-settings-location", "nfs")
|
||
|
- mountInput = "#kdump-settings-nfs-mount"
|
||
|
- b.set_input_text(mountInput, ":/var/crash")
|
||
|
+ serverInput = "#kdump-settings-nfs-server"
|
||
|
+ b.set_input_text(serverInput, "")
|
||
|
b.click(f"#kdump-settings-dialog button{self.primary_btn_class}")
|
||
|
- b.wait_in_text("#kdump-settings-dialog h4.pf-c-alert__title", "Unable to save settings: nfs dump target isn't formatted as server:path")
|
||
|
+ b.wait_in_text("#kdump-settings-dialog h4.pf-c-alert__title", "Unable to save settings: nfs server is empty")
|
||
|
# no further details/journal
|
||
|
self.assertFalse(b.is_present("#kdump-settings-dialog .pf-c-code-block__code"))
|
||
|
- b.set_input_text(mountInput, "localhost:")
|
||
|
+ b.set_input_text(serverInput, "localhost")
|
||
|
b.click(f"#kdump-settings-dialog button{self.primary_btn_class}")
|
||
|
- b.wait_in_text("#kdump-settings-dialog h4.pf-c-alert__title", "Unable to save settings: nfs dump target isn't formatted as server:path")
|
||
|
+ b.wait_in_text("#kdump-settings-dialog h4.pf-c-alert__title", "Unable to save settings: nfs export is empty")
|
||
|
b.click("#kdump-settings-dialog button.cancel")
|
||
|
b.wait_not_present("#kdump-settings-dialog")
|
||
|
|
||
|
@@ -207,7 +207,8 @@ class TestKdump(KdumpHelpers):
|
||
|
b.click("#kdump-change-target")
|
||
|
b.wait_visible("#kdump-settings-dialog")
|
||
|
b.set_val("#kdump-settings-location", "nfs")
|
||
|
- b.set_input_text("#kdump-settings-nfs-mount", "someserver:/srv")
|
||
|
+ b.set_input_text("#kdump-settings-nfs-server", "someserver")
|
||
|
+ b.set_input_text("#kdump-settings-nfs-export", "/srv")
|
||
|
b.click("button:contains('Save')")
|
||
|
b.wait_not_present("#kdump-settings-dialog")
|
||
|
conf = m.execute("cat /etc/kdump.conf")
|
||
|
@@ -277,7 +278,8 @@ class TestKdumpNFS(KdumpHelpers):
|
||
|
b.click("#kdump-change-target")
|
||
|
b.wait_visible("#kdump-settings-dialog")
|
||
|
b.set_val("#kdump-settings-location", "nfs")
|
||
|
- b.set_input_text("#kdump-settings-nfs-mount", "10.111.113.2:/srv/kdump")
|
||
|
+ b.set_input_text("#kdump-settings-nfs-server", "10.111.113.2")
|
||
|
+ b.set_input_text("#kdump-settings-nfs-export", "/srv/kdump")
|
||
|
b.click(f"#kdump-settings-dialog button{self.primary_btn_class}")
|
||
|
# rebuilding initrd might take a while on busy CI machines
|
||
|
with b.wait_timeout(300):
|
||
|
|
||
|
From 7a0d578063a1f4e25697eb13a9331f37d277857d Mon Sep 17 00:00:00 2001
|
||
|
From: Jacek Tomasiak <jacek.tomasiak@gmail.com>
|
||
|
Date: Tue, 19 Jul 2022 15:58:03 +0200
|
||
|
Subject: [PATCH 4/4] kdump: Fix ssh settings wiping
|
||
|
|
||
|
Leftover sshkey setting confused kdump on Fedora-35.
|
||
|
---
|
||
|
pkg/kdump/config-client.js | 3 +++
|
||
|
1 file changed, 3 insertions(+)
|
||
|
|
||
|
diff --git a/pkg/kdump/config-client.js b/pkg/kdump/config-client.js
|
||
|
index d292bd9bebd..9b95b9a0c65 100644
|
||
|
--- a/pkg/kdump/config-client.js
|
||
|
+++ b/pkg/kdump/config-client.js
|
||
|
@@ -233,6 +233,9 @@ export class ConfigFile {
|
||
|
const oldTarget = this.settings.targets[key];
|
||
|
if (oldTarget.type == "mount") {
|
||
|
delete settings._internal[oldTarget.fsType];
|
||
|
+ } else if (oldTarget.type == "ssh") {
|
||
|
+ delete settings._internal.ssh;
|
||
|
+ delete settings._internal.sshkey;
|
||
|
} else {
|
||
|
delete settings._internal[key];
|
||
|
}
|