diff --git a/pkg/systemd/host.js b/pkg/systemd/host.js index accb4c1a76d..bb1cd27c4e9 100644 --- a/pkg/systemd/host.js +++ b/pkg/systemd/host.js @@ -17,9 +17,11 @@ * along with Cockpit; If not, see . */ +require("polyfills.js"); var $ = require("jquery"); var cockpit = require("cockpit"); var machine_info = require("machine-info.es6"); +var packagekit = require("packagekit.es6"); var Mustache = require("mustache"); var plot = require("plot"); @@ -196,6 +198,9 @@ PageServer.prototype = { this.server_time = null; this.client = null; this.hostname_proxy = null; + this.os_updates = null; // packagekit.Enum.INFO_* → #count + this.os_updates_icon = document.getElementById("system_information_updates_icon"); + this.unregistered = false; }, getTitle: function() { @@ -369,6 +374,87 @@ PageServer.prototype = { $(pmlogger_service).on('changed', refresh_pmlogger_state); refresh_pmlogger_state(); + + // if cockpit component "page" is available, set element content to a link to it, otherwise just text + function set_page_link(element_sel, page, text) { + if (cockpit.manifests[page]) { + var link = document.createElement("a"); + link.innerHTML = text; + link.addEventListener("click", function() { cockpit.jump("/" + page); }); + $(element_sel).html(link); + } else { + $(element_sel).text(text); + } + } + + function refresh_os_updates_state() { + self.os_updates_icon.className = ""; // hide spinner + + // if system is unregistered, always show that + if (self.unregistered) { + self.os_updates_icon.className = "pficon pficon-warning-triangle-o"; + set_page_link("#system_information_updates_text", "subscriptions", _("System Not Registered")); + return; + } + + var infos = Object.keys(self.os_updates || {}).sort(); + if (infos.length === 0) { + $("#system_information_updates_text").text(_("System Up To Date")); + return; + } + + // show highest severity level + var severity = infos[infos.length - 1]; + var text; + self.os_updates_icon.className = packagekit.getSeverityIcon(severity); + if (severity == packagekit.Enum.INFO_SECURITY) + text = _("Security Updates Available"); + else if (severity >= packagekit.Enum.INFO_NORMAL) + text = _("Bug Fix Updates Available"); + else + text = _("Enhancement Updates Available"); + + set_page_link("#system_information_updates_text", "updates", text); + } + + function check_for_updates() { + self.os_updates = null; + + packagekit.cancellableTransaction("GetUpdates", [0], + function (data) { + // we are getting progress, so PackageKit works; show spinner + if (self.os_updates === null) { + self.os_updates = {}; + $("#system_information_updates_text").text(_("Checking for updates…")); + self.os_updates_icon.className = "spinner spinner-xs spinner-inline"; + } + }, + { + Package: function (info) { + // HACK: dnf backend yields wrong severity (https://bugs.freedesktop.org/show_bug.cgi?id=101070) + if (info < packagekit.Enum.INFO_LOW || info > packagekit.Enum.INFO_SECURITY) + info = packagekit.Enum.INFO_NORMAL; + self.os_updates[info] = (self.os_updates[info] || 0) + 1; + } + }) + .then(refresh_os_updates_state) + .catch(function (ex) { + // if PackageKit is not available, hide the table line + console.warn("Checking for available updates failed:", ex.toString()); + self.os_updates_icon.className = ""; + $("#system_information_updates_text").toggle(false); + }); + } + + // check for updates now and on page switches, in case they get applied on /updates or terminal + $(cockpit).on("visibilitychange", check_for_updates); + check_for_updates(); + + // check for unregistered system + packagekit.watchRedHatSubscription(function (subscribed) { + self.unregistered = !subscribed; + refresh_os_updates_state(); + }); }, enter: function() { diff --git a/pkg/systemd/index.html b/pkg/systemd/index.html index 70af3b5136e..2cd7f2cd13a 100644 --- a/pkg/systemd/index.html +++ b/pkg/systemd/index.html @@ -9,6 +9,7 @@ + @@ -99,6 +100,12 @@ Operating System + + + + + + Secure Shell Keys . import os +import time import parent from testlib import * @@ -85,15 +86,39 @@ class TestUpdates(PackageCase): return row + def wait_checking_updates(self): + '''Wait until spinner is gone from #system_information_updates_icon for 3 s''' + + self.browser.wait_present("#system_information_updates_icon") + good_count = 0 + for retry in range(60): + classes = self.browser.attr("#system_information_updates_icon", "class") + if classes is None or "spinner" in classes: + good_count = 0 + else: + good_count += 1 + if good_count >= 3: + return + time.sleep(1) + else: + self.fail("Timed out waiting for updates spinner to go away") + def testBasic(self): # no security updates, no changelogs b = self.browser m = self.machine m.start_cockpit() - b.login_and_go("/updates") - - # no repositories at all, thus no updates + b.login_and_go("/system") + if m.image != "rhel-7-4": + # status on /system front page: no repos at all, thus no updates + self.wait_checking_updates() + b.wait_text("#system_information_updates_text", "System Up To Date") + self.assertEqual(b.attr("#system_information_updates_icon", "class"), "") + + # no updates on the Software Updates page + b.go("/updates") + b.enter_page("/updates") b.wait_present(".content-header-extra td button") b.wait_in_text("#state", "No updates pending") b.wait_present(".content-header-extra td.text-right span") @@ -127,6 +152,17 @@ class TestUpdates(PackageCase): self.check_nth_update(1, "chocolate", "2.0-2") self.check_nth_update(2, "vanilla", "1.0-2") + if m.image != "rhel-7-4": + # updates are shown on system page + b.go("/system") + b.enter_page("/system") + self.wait_checking_updates() + b.wait_text("#system_information_updates_text", "Bug Fix Updates Available") + self.assertIn("fa-bug", b.attr("#system_information_updates_icon", "class")) + # should be a link, click on it to go to /updates + b.click("#system_information_updates_text a") + b.enter_page("/updates") + # old versions are still installed m.execute("test -f /stamp-vanilla-1.0-1 && test -f /stamp-chocolate-2.0-1") @@ -182,6 +218,14 @@ class TestUpdates(PackageCase): # new versions are now installed m.execute("test -f /stamp-vanilla-1.0-2 && test -f /stamp-chocolate-2.0-2") + if m.image != "rhel-7-4": + # system page has current state as well + b.go("/system") + b.enter_page("/system") + self.wait_checking_updates() + b.wait_text("#system_information_updates_text", "System Up To Date") + self.assertEqual(b.attr("#system_information_updates_icon", "class"), "") + @skipImage("Ubuntu 16.04 PackageKit does not support changelogs", "ubuntu-1604") def testInfoSecurity(self): b = self.browser @@ -263,6 +307,20 @@ class TestUpdates(PackageCase): self.assertEqual(b.text(sel + " .listing-ct-body em"), "more") # *more* self.assertEqual(b.attr(sel + " .listing-ct-body a:first-of-type", "href"), "http://unicorn.example.com") + if m.image != "rhel-7-4": + # updates are shown on system page + b.go("/system") + b.enter_page("/system") + self.wait_checking_updates() + # PackageKit's dnf backend mis-parses severity (https://bugs.freedesktop.org/show_bug.cgi?id=101070) + # and PackageKit's deb backend does not know about severity at all, so only test this on RPM + if self.backend == 'yum': + b.wait_text("#system_information_updates_text", "Security Updates Available") + self.assertIn("security", b.attr("#system_information_updates_icon", "class")) + # should be a link, click on it to go to back to /updates + b.click("#system_information_updates_text a") + b.enter_page("/updates") + # install only security updates self.assertEqual(b.text("#app .container-fluid button.btn-default"), "Install Security Updates") b.click("#app .container-fluid button.btn-default") @@ -588,6 +646,14 @@ class TestUpdates(PackageCase): b.wait_present("#app pre") b.wait_in_text("#app pre", "PackageKit is not installed") + if m.image != "rhel-7-4": + # update status on front page should be invisible + b.go("/system") + b.enter_page("/system") + b.wait_present("#system_information_updates_text") + b.wait_not_visible("#system_information_updates_text") + self.assertEqual(b.attr("#system_information_updates_icon", "class"), "") + @skipImage("Image uses OSTree", "continuous-atomic", "fedora-atomic", "rhel-atomic") @skipImage("No subscriptions", "centos-7", "debian-stable", "debian-testing", "fedora-26", "fedora-27", "fedora-i386", "fedora-testing", "ubuntu-1604", "ubuntu-stable") @@ -632,7 +698,16 @@ class TestUpdatesSubscriptions(PackageCase): # fresh machine, no updates available; by default our rhel-* images are not registered m.start_cockpit() - b.login_and_go("/updates") + b.login_and_go("/system") + if m.image != "rhel-7-4": + # show unregistered status on system front page + b.wait_present("#system_information_updates_text") + b.wait_in_text("#system_information_updates_text", "Not Registered") + self.assertIn("triangle", b.attr("#system_information_updates_icon", "class")) + + # and also on software updates page + b.go("/updates") + b.enter_page("/updates") # empty state visible in main area b.wait_present(".container-fluid div.blank-slate-pf button") @@ -655,6 +730,14 @@ class TestUpdatesSubscriptions(PackageCase): b.wait_present(".container-fluid div.blank-slate-pf") b.wait_in_text(".container-fluid div.blank-slate-pf", "up to date") + if m.image != "rhel-7-4": + # same on system page + b.go("/system") + b.enter_page("/system") + b.wait_present("#system_information_updates_text") + b.wait_text("#system_information_updates_text", "System Up To Date") + self.assertEqual(b.attr("#system_information_updates_icon", "class"), "") + def testAvailableUpdates(self): m = self.machine b = self.browser @@ -664,11 +747,21 @@ class TestUpdatesSubscriptions(PackageCase): self.createPackage("vanilla", "1.0", "2") self.enableRepo() - # by default our rhel-* images are not registered; should show available update and unregistered warning, but no - # other action buttons m.start_cockpit() - b.login_and_go("/updates") + b.login_and_go("/system") + if m.image != "rhel-7-4": + # by default our rhel-* images are not registered; show warning on system page + b.wait_present("#system_information_updates_text") + b.wait_in_text("#system_information_updates_text", "Not Registered") + self.assertIn("triangle", b.attr("#system_information_updates_icon", "class")) + # should be a link leading to subscriptions page + b.click("#system_information_updates_text a") + b.enter_page("/subscriptions") + + # /updates should show available update and unregistered warning, but no other action buttons + b.go("/updates") + b.enter_page("/updates") b.wait_present(".alert-warning") b.wait_in_text(".alert-warning", "subscribe") b.wait_present("table.listing-ct") @@ -702,6 +795,14 @@ class TestUpdatesSubscriptions(PackageCase): b.wait_present("#app .container-fluid button") self.assertEqual(b.text("#app .container-fluid button"), "Install All Updates") + if m.image != "rhel-7-4": + # show available updates on system page too + b.go("/system") + b.enter_page("/system") + b.wait_present("#system_information_updates_text") + b.wait_text("#system_information_updates_text", "Bug Fix Updates Available") + self.assertIn("fa-bug", b.attr("#system_information_updates_icon", "class")) + @skipImage("Image uses OSTree", "continuous-atomic", "fedora-atomic", "rhel-atomic") class TestAutoUpdates(PackageCase):