1
0
Fork 0
mirror of https://github.com/retspen/webvirtcloud synced 2026-11-08 11:16:47 +00:00
webvirtcloud/static/js/xml_editor.js
catborise 3730a95c02 fix(instances): the XML dialog keeps the edit and defines only this VM
Found by review (Codex and agy) of the XML editor's dialog:

- A busy or moved VM made the page reload and the edit was lost. The
  dialog's requests now get these answers as JSON (X-Requested-With, as the
  create page's flavor forms) and show them; any other failure says the
  edit is still in the editor.
- libvirt finds the VM to define by the XML's UUID: another VM's UUID
  overwrote that VM past its lock, a new or missing one added a VM. The
  XML must keep this VM's UUID; a malformed one is refused with the
  parser's error, as on the create page.
- Save sends the XML whose diff the dialog shows, also when two previews
  answer out of order; a failed save keeps the diff and Save.
- The edited XML is defined with the line breaks libvirt gave, not the
  form's CRLF, and the VM's state is read before the definition, so a
  failed read cannot turn a saved change into an error.
2026-10-24 20:03:20 +03:00

83 lines
3.4 KiB
JavaScript

// The VM page's XML editor. Change shows the lines the edit changes in the
// VM's definition (instances.views.change_xml), and Save in that dialog
// defines it. A refusal shows in the dialog, and the edit stays in the editor.
(function () {
var form = document.getElementById("xml-form");
if (!form) {
return; // the editor exists only for superusers
}
var editor = ace.edit("editor");
editor.getSession().setMode("ace/mode/xml");
var base = editor.getValue(); // the definition the editor opened with
var dialog = document.getElementById("xml-review");
var modal = bootstrap.Modal.getOrCreateInstance(dialog);
var error = document.getElementById("xml-review-error");
var same = document.getElementById("xml-review-same");
var diff = document.getElementById("xml-review-diff");
var save = document.getElementById("xml-review-save");
var reviewed = ""; // Save defines the XML the dialog shows, not later edits
// The answer: diff, saved, or error (detail when the VM is busy or moved).
// Any other failure, such as a redirect, keeps the edit and says so.
function post(xml, confirm) {
var data = new FormData(form);
data.set("inst_xml", xml);
data.set("base", base);
if (confirm) {
data.set("confirm", "1");
}
return fetch(form.action, {
method: "POST", body: data, redirect: "manual", headers: {"X-Requested-With": "XMLHttpRequest"},
}).then(function (response) {
return response.json();
}).then(function (answer) {
return {diff: answer.diff, saved: answer.saved, error: answer.error || answer.detail};
}).catch(function () {
return {error: dialog.dataset.failed};
});
}
function show(answer) {
error.textContent = answer.error || "";
error.classList.toggle("d-none", !answer.error);
var lines = answer.diff || [];
same.classList.toggle("d-none", Boolean(answer.error) || lines.length > 0);
save.classList.toggle("d-none", lines.length === 0);
save.disabled = false;
diff.replaceChildren();
// skip the two file header lines; + added, - removed, @@ where
lines.slice(2).forEach(function (line) {
var row = document.createElement("div");
row.textContent = line;
row.className = {"+": "text-success", "-": "text-danger", "@": "text-body-secondary"}[line[0]] || "";
diff.appendChild(row);
});
diff.classList.toggle("d-none", lines.length === 0);
}
form.addEventListener("submit", function (event) {
event.preventDefault();
var xml = editor.getValue();
// the last answer shown is the one Save sends, whatever order answers come in
post(xml, false).then(function (answer) {
reviewed = xml;
show(answer);
modal.show();
});
});
save.addEventListener("click", function () {
save.disabled = true;
post(reviewed, true).then(function (answer) {
if (answer.saved) {
location.hash = "xmledit";
location.reload();
} else {
// the diff stays, and Save can be tried again
error.textContent = answer.error;
error.classList.remove("d-none");
save.disabled = false;
}
});
});
})();