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):