diff --git a/instances/templates/add_instance_volume.html b/instances/templates/add_instance_volume.html index 4f8be1ab..aab3c130 100644 --- a/instances/templates/add_instance_volume.html +++ b/instances/templates/add_instance_volume.html @@ -69,7 +69,7 @@
+ data-guess-mac-url="{% url 'instances:guess_mac_address' '__name__' %}">
diff --git a/instances/tests/test_create_dialog_cpu.py b/instances/tests/test_create_dialog_cpu.py index 54097ee7..4884e7f5 100644 --- a/instances/tests/test_create_dialog_cpu.py +++ b/instances/tests/test_create_dialog_cpu.py @@ -10,6 +10,7 @@ import unittest from unittest.mock import PropertyMock, patch from computes.models import Compute +from django.conf import settings from django.contrib.auth import get_user_model from django.test import TestCase from django.urls import reverse @@ -95,7 +96,8 @@ class CreateDialogCpuTestCase(TestCase): @unittest.skipUnless(shutil.which("node"), "node is not installed") def test_the_dialog_reads_host_by_host_two_at_a_time(self): page = self.page() - script = next(s for s in html.fromstring(page).xpath("//script[not(@src)]/text()") if "show.bs.modal" in s) + self.assertIn("/static/js/new_vm_dialog.js", html.fromstring(page).xpath("//script/@src")) + script = (settings.BASE_DIR / "static/js/new_vm_dialog.js").read_text() cpu = [reverse("compute_graph", args=[c.id]) for c in self.computes] line = [reverse("compute_capacity", args=[c.id]) for c in self.computes] js = RUN.replace("SCRIPT", json.dumps(script)).replace("CPU_URLS", json.dumps(cpu)).replace("LINE_URLS", json.dumps(line)) diff --git a/static/js/console_window.js b/static/js/console_window.js new file mode 100644 index 00000000..430c22f8 --- /dev/null +++ b/static/js/console_window.js @@ -0,0 +1,6 @@ +// A console button (data-url, data-window) opens the console in one window +// per VM: opening it again reloads that window. +document.addEventListener("click", function (event) { + var button = event.target.closest("button[data-window]"); + if (button) window.open(button.dataset.url, button.dataset.window, "width=850,height=650"); +}); diff --git a/static/js/host_groups.js b/static/js/host_groups.js new file mode 100644 index 00000000..8d45daab --- /dev/null +++ b/static/js/host_groups.js @@ -0,0 +1,24 @@ +// The VM list grouped by host (allinstances_index_grouped.html): a host's +// VMs collapse under its row. A collapsed host's VM rows are hidden by its +// table body's class, so the search (table_filter.js, the hidden attribute) +// can still hide them too. +function set_host_collapsed(tbody, collapsed) { + $(tbody).toggleClass('host-collapsed', collapsed); + $(tbody).find('[aria-controls]').attr('aria-expanded', !collapsed); + $(tbody).find('.host-chevron').toggleClass('bi-chevron-down', collapsed).toggleClass('bi-chevron-up', !collapsed); + $(tbody).find('.host-collapsed-badge').toggleClass('d-none', !collapsed); + // expanded while any host is, as the next click collapses them all + $('#hide-all-instances').attr('aria-expanded', $('tbody.searchable').not('.host-collapsed').length > 0); +} + +// every host the same way: collapsed, unless all already are +$('#hide-all-instances').on('click', function () { + var bodies = $('tbody.searchable'); + var collapse = bodies.not('.host-collapsed').length > 0; + bodies.each(function () { set_host_collapsed(this, collapse); }); +}); + +$('.host-toggle').on('click', function () { + var tbody = $(this).closest('tbody'); + set_host_collapsed(tbody, !tbody.hasClass('host-collapsed')); +}); diff --git a/static/js/instance_page.js b/static/js/instance_page.js index f32d235e..1304d3af 100644 --- a/static/js/instance_page.js +++ b/static/js/instance_page.js @@ -50,10 +50,6 @@ show_tab('#clone'); }); - // one window per VM: opening it again reloads it - on("console_button", "click", function () { - window.open(this.dataset.url, this.dataset.window, 'width=850,height=650'); - }); if (summary.dataset.vdiUrl) { $.get(summary.dataset.vdiUrl, function (data) { $("#vdi_url_input").attr("value", data); @@ -88,7 +84,7 @@ var pool = document.getElementById("selected_storage"); if (pool) { pool.addEventListener("change", function () { - $.getJSON(pool.dataset.volumesUrl.replace("POOL", encodeURIComponent(pool.value)), function (data) { + $.getJSON(pool.dataset.volumesUrl.replace("__pool__", encodeURIComponent(pool.value)), function (data) { var vols = $("#vols").empty(); $.each(data['vols'], function (i, item) { vols.append($("