From b61b273451987a283825a585a4f40901be12b11c Mon Sep 17 00:00:00 2001 From: Jacek Tomasiak 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 { ); } 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 = ( <> - - this.props.onChange("nfs", value)} /> + + this.props.onChange("server", value)} /> + + + this.props.onChange("export", value)} /> ); } 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 = ( <> this.props.onChange("ssh", value)} /> + placeholder="user@server.com" value={server} + onChange={value => this.props.onChange("server", value)} /> @@ -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 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 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 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]; }