300 lines
11 KiB
JavaScript
300 lines
11 KiB
JavaScript
/*
|
|
* This file is part of Cockpit.
|
|
*
|
|
* Copyright (C) 2015 Red Hat, Inc.
|
|
*
|
|
* Cockpit is free software; you can redistribute it and/or modify it
|
|
* under the terms of the GNU Lesser General Public License as published by
|
|
* the Free Software Foundation; either version 2.1 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* Cockpit is distributed in the hope that it will be useful, but
|
|
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* Lesser General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public License
|
|
* along with Cockpit; If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
import $ from "jquery";
|
|
import cockpit from "cockpit";
|
|
|
|
import { show_modal_dialog } from "cockpit-components-dialog.jsx";
|
|
import * as service from "service";
|
|
import React from "react";
|
|
|
|
import { TunedDialogBody } from "./change-profile.jsx";
|
|
import link_html from "raw-loader!./link.html";
|
|
|
|
const _ = cockpit.gettext;
|
|
var permission = cockpit.permission({ admin: true });
|
|
|
|
function setup() {
|
|
var tuned_service = service.proxy('tuned.service');
|
|
|
|
var element = $(link_html);
|
|
|
|
var button = element.find(".action-trigger");
|
|
var tooltip = element.find("#tuned-status-tooltip");
|
|
tooltip.tooltip({ placement: "top" });
|
|
|
|
/* Tuned doesn't implement the DBus.Properties interface, so
|
|
* we occasionally poll for what we need.
|
|
*
|
|
* Tuned doesn't auto-activate on the bus, so we have to start
|
|
* it explicitly when opening the dialog.
|
|
*/
|
|
|
|
function poll(tuned) {
|
|
var dfd = cockpit.defer();
|
|
|
|
Promise.all([
|
|
tuned.call('/Tuned', 'com.redhat.tuned.control', 'is_running', []),
|
|
tuned.call('/Tuned', 'com.redhat.tuned.control', 'active_profile', []),
|
|
tuned.call('/Tuned', 'com.redhat.tuned.control', 'recommend_profile', [])
|
|
])
|
|
.then(function([is_running_result, active_result, recommended_result]) {
|
|
var is_running = is_running_result[0];
|
|
var active = is_running ? active_result[0] : "none";
|
|
var recommended = recommended_result[0];
|
|
|
|
dfd.resolve("running", active, recommended);
|
|
})
|
|
.catch(function(ex) {
|
|
tuned_service.wait(function () {
|
|
if (!tuned_service.exists)
|
|
dfd.resolve("not-installed");
|
|
else if (tuned_service.state != "running")
|
|
dfd.resolve("not-running");
|
|
else
|
|
dfd.reject(ex);
|
|
});
|
|
});
|
|
|
|
return dfd.promise();
|
|
}
|
|
|
|
function update_button() {
|
|
/* Reading the current profile works as user */
|
|
var tuned = cockpit.dbus('com.redhat.tuned');
|
|
|
|
function set_status(text) {
|
|
tooltip.attr("data-original-title", text);
|
|
}
|
|
|
|
poll(tuned)
|
|
.done(function (state, active, recommended) {
|
|
var status;
|
|
|
|
if (state == "not-installed")
|
|
status = _("Tuned is not available");
|
|
else if (state == "not-running")
|
|
status = _("Tuned is not running");
|
|
else if (active == "none")
|
|
status = _("Tuned is off");
|
|
else if (!permission.allowed)
|
|
status = cockpit.format(
|
|
_("The user <b>$0</b> is not permitted to change profiles"),
|
|
permission.user ? permission.user.name : '');
|
|
else if (active == recommended)
|
|
status = _("This system is using the recommended profile");
|
|
else
|
|
status = _("This system is using a custom profile");
|
|
|
|
button.text(state == "running" ? active : _("none"));
|
|
button.prop('disabled', state == "not-installed" || !permission.allowed);
|
|
button.toggleClass('disabled', state == "not-installed" || !permission.allowed);
|
|
set_status(status);
|
|
})
|
|
.fail(function (ex) {
|
|
console.warn("failed to poll tuned", ex);
|
|
button.text("error");
|
|
button.prop('disabled', true);
|
|
button.toggleClass('disabled', true);
|
|
set_status(_("Communication with tuned has failed"));
|
|
});
|
|
}
|
|
|
|
function open_dialog() {
|
|
var tuned;
|
|
var dialog_selected;
|
|
|
|
function set_profile() {
|
|
// no need to check input here, all states are valid
|
|
var profile = dialog_selected;
|
|
var promise;
|
|
|
|
if (profile == "none") {
|
|
promise = tuned.call("/Tuned", 'com.redhat.tuned.control', 'disable', [])
|
|
.then(function(results) {
|
|
/* Yup this is how tuned returns failures */
|
|
if (!results[0]) {
|
|
console.warn("Failed to disable tuned profile:", results);
|
|
return cockpit.reject(_("Failed to disable tuned profile"));
|
|
}
|
|
|
|
update_button();
|
|
});
|
|
} else {
|
|
promise = tuned.call('/Tuned', 'com.redhat.tuned.control', 'switch_profile', [profile])
|
|
.then(function(results) {
|
|
/* Yup this is how tuned returns failures */
|
|
if (!results[0][0]) {
|
|
console.warn("Failed to switch profile:", results);
|
|
return cockpit.reject(results[0][1] || _("Failed to switch profile"));
|
|
}
|
|
|
|
update_button();
|
|
});
|
|
}
|
|
|
|
return promise.then(set_service);
|
|
}
|
|
|
|
function set_service() {
|
|
/* When the profile is none we disable tuned */
|
|
var enable = (dialog_selected != "none");
|
|
var action = enable ? "start" : "stop";
|
|
return tuned.call('/Tuned', 'com.redhat.tuned.control', action, [])
|
|
.then(function(results) {
|
|
/* Yup this is how tuned returns failures */
|
|
if (!results[0]) {
|
|
console.warn("Failed to " + action + " tuned:", results);
|
|
if (results[1])
|
|
return cockpit.reject(results[1]);
|
|
else if (enable)
|
|
return cockpit.reject(cockpit.format(_("Failed to enable tuned")));
|
|
else
|
|
return cockpit.reject(cockpit.format(_("Failed to disable tuned")));
|
|
}
|
|
|
|
/* Now tell systemd about this change */
|
|
if (enable && !tuned_service.enabled)
|
|
return tuned_service.enable();
|
|
else if (!enable && tuned_service.enabled)
|
|
return tuned_service.disable();
|
|
else
|
|
return null;
|
|
});
|
|
}
|
|
|
|
function update_selected_item(selected) {
|
|
dialog_selected = selected;
|
|
}
|
|
|
|
function create_dialog(profiles, active_profile, primary_disabled, static_error) {
|
|
dialog_selected = active_profile;
|
|
var dialog_props = {
|
|
title: _("Change Performance Profile"),
|
|
body: React.createElement(TunedDialogBody, {
|
|
active_profile: active_profile,
|
|
change_selected: update_selected_item,
|
|
profiles: profiles,
|
|
}),
|
|
};
|
|
var footer_props = {
|
|
actions: [{
|
|
clicked: set_profile,
|
|
caption: _("Change Profile"),
|
|
style: 'primary',
|
|
}
|
|
],
|
|
static_error: static_error,
|
|
};
|
|
show_modal_dialog(dialog_props, footer_props);
|
|
}
|
|
|
|
function with_info(active, recommended, profiles) {
|
|
var model = [];
|
|
profiles.forEach(function(p) {
|
|
var name, desc;
|
|
if (typeof p === "string") {
|
|
name = p;
|
|
desc = "";
|
|
} else {
|
|
name = p[0];
|
|
desc = p[1];
|
|
}
|
|
if (name != "none") {
|
|
model.push({
|
|
name: name,
|
|
title: name,
|
|
description: desc,
|
|
active: name == active,
|
|
recommended: name == recommended,
|
|
});
|
|
}
|
|
});
|
|
|
|
model.unshift({
|
|
name: "none",
|
|
title: _("None"),
|
|
description: _("Disable tuned"),
|
|
active: active == "none",
|
|
recommended: recommended == "none",
|
|
});
|
|
|
|
create_dialog(model, active);
|
|
}
|
|
|
|
function show_error(error) {
|
|
create_dialog([], "none", true, error);
|
|
}
|
|
|
|
function tuned_profiles() {
|
|
return tuned.call('/Tuned', 'com.redhat.tuned.control', 'profiles2', [])
|
|
.then(function(result) {
|
|
return result[0];
|
|
}, function() {
|
|
return tuned.call('/Tuned', 'com.redhat.tuned.control', 'profiles', [])
|
|
.then(function(result) {
|
|
return result[0];
|
|
});
|
|
});
|
|
}
|
|
|
|
function with_tuned() {
|
|
poll(tuned)
|
|
.done(function (state, active, recommended) {
|
|
if (state != "running") {
|
|
show_error(_("Tuned has failed to start"));
|
|
return;
|
|
}
|
|
tuned_profiles().then(function(profiles) {
|
|
with_info(active, recommended, profiles);
|
|
}, show_error);
|
|
})
|
|
.fail(show_error);
|
|
}
|
|
|
|
tuned_service.start()
|
|
.done(function () {
|
|
update_button();
|
|
/* There are a few cases where tuned can be started by any user,
|
|
* but as the dialog needs superuser anyway, there's little
|
|
* reason to put 'try'.
|
|
*/
|
|
tuned = cockpit.dbus('com.redhat.tuned', { superuser: true });
|
|
with_tuned();
|
|
})
|
|
.fail(show_error);
|
|
}
|
|
|
|
button.on('click', e => {
|
|
e.preventDefault();
|
|
open_dialog();
|
|
});
|
|
|
|
tuned_service.addEventListener("changed", update_button);
|
|
update_button();
|
|
|
|
return element[0];
|
|
}
|
|
|
|
$(permission).on('changed', function () {
|
|
var element = $('#system-info-performance');
|
|
element.append(setup());
|
|
element.removeAttr('hidden');
|
|
});
|