File kdump-refactor.patch of Package cockpit

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] = cockpit.extend({}, this.settings[key]);
+        Object.keys(this.settings._internal).forEach((key) => {
+            this._originalSettings[key] = cockpit.extend({}, 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";
 
         var dfd = cockpit.defer();
-        if (target.target === "local") {
+        if (target.type === "local") {
             // local path, try to see if we can write
             cockpit.script(testWritableScript, [path], { superuser: "try" })
                     .done(dfd.resolve)
                     .fail(() => dfd.reject(cockpit.format(_("Directory $0 isn't writable or doesn't exist."), path)));
             return dfd.promise();
-        } else if (target.target === "nfs") {
-            if (!target.nfs.value.match("\\S+:/.+"))
-                dfd.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())
+                dfd.reject(_("nfs server is empty"));
+            // IPv6 must be enclosed in square brackets
+            if (target.server.trim().match(/^\[.*[^\]]$/))
+                dfd.reject(_("nfs server is not valid IPv6"));
+            if (!target.export || !target.export.trim())
+                dfd.reject(_("nfs export is empty"));
+        } else if (target.type === "ssh") {
+            if (!target.server || !target.server.trim())
                 dfd.reject(_("ssh server is empty"));
-            if (target.sshkey && !target.sshkey.value.match("/.+"))
+            if (target.sshkey && !target.sshkey.match("/.+"))
                 dfd.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
         var 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
-                var 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() {
         var detailRows;
-        // only allow compression if there is no core collector set or it's set to makedumpfile
-        var 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;
         var 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") {
-            var 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") {
-            var ssh = "";
-            if (this.props.settings && "ssh" in this.props.settings)
-                ssh = this.props.settings.ssh.value;
-            var 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) {
         var 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] = cockpit.extend({}, 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,19 +346,19 @@ 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");
-                } else if (target.target == "ssh") {
+                } else if (target.type == "ssh") {
                     kdumpLocation = _("Remote over SSH");
-                } else if (target.target == "nfs") {
+                } else if (target.type == "nfs") {
                     kdumpLocation = _("Remote over NFS");
-                } else if (target.target == "raw") {
+                } else if (target.type == "raw") {
                     kdumpLocation = _("Raw to a device");
                     targetCanChange = false;
-                } 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) {
     var dataWasChanged = cockpit.defer();
     var config;
     var configChanged = function(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");
         dataWasChanged.resolve();
     };
@@ -65,10 +65,10 @@ QUnit.test("config_update", function (assert) {
                 config = new kdump.ConfigFile(filename);
                 config.wait().always(function() {
                     assert.equal(this.state(), "resolved", "waiting for config didn't fail");
-                    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)
                             .always(function() {

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,12 +109,12 @@ class TestKdump(KdumpHelpers):
 
         b.click(settingsLink)
         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("button{}:contains('Apply')".format(self.primary_btn_class))
         b.wait_visible("h4.pf-c-alert__title:contains('Unable to apply settings')")
-        b.set_input_text(mountInput, "localhost:")
+        b.set_input_text(serverInput, "localhost")
         b.click("button{}:contains('Apply')".format(self.primary_btn_class))
         b.wait_visible("h4.pf-c-alert__title:contains('Unable to apply settings')")
         b.click("button{}:contains('Apply')".format(self.primary_btn_class))
 
@@ -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('Apply')")
         b.wait_not_present("#kdump-settings-dialog")
         conf = m.execute("cat /etc/kdump.conf")

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];
                 }
openSUSE Build Service is sponsored by