subscriptions: Drop

This was only built on RHEL/CentOS 7, which is not supported by master
any more.
This commit is contained in:
Martin Pitt 2019-06-26 07:59:37 +02:00 committed by Martin Pitt
parent d7c33be0cd
commit 70158d0f20
14 changed files with 1 additions and 1185 deletions

View File

@ -165,7 +165,6 @@ WEBPACK_PACKAGES = \
sosreport \
ssh \
storaged \
subscriptions \
systemd \
tuned \
users \

View File

@ -34,7 +34,6 @@ GUIDE_INCLUDES = \
doc/guide/feature-selinux.xml \
doc/guide/feature-sosreport.xml \
doc/guide/feature-storaged.xml \
doc/guide/feature-subscription.xml \
doc/guide/feature-systemd.xml \
doc/guide/feature-terminal.xml \
doc/guide/feature-tuned.xml \

View File

@ -44,7 +44,6 @@
<xi:include href="feature-realmd.xml"/>
<xi:include href="feature-terminal.xml"/>
<xi:include href="feature-pcp.xml"/>
<xi:include href="feature-subscription.xml"/>
<xi:include href="feature-machines.xml"/>
<xi:include href="feature-selinux.xml"/>
<xi:include href="feature-tuned.xml"/>

View File

@ -1,28 +0,0 @@
<?xml version="1.0"?>
<!DOCTYPE chapter PUBLIC "-//OASIS//DTD DocBook XML V4.3//EN"
"http://www.oasis-open.org/docbook/xml/4.3/docbookx.dtd">
<chapter id="feature-subscription">
<title>Subscription Manager</title>
<para>Cockpit can use
<ulink url="https://www.candlepinproject.org/">Subscription Manager</ulink> to join the
attach the machine to a subscription, if the operating system (such as RHEL) requires this. This
functionality is in the Cockpit <emphasis>subscriptions</emphasis> package.</para>
<para>Subscription Manager limits its use to root only. Therefore if a non-root user
is logged into Cockpit, Cockpit tries to <link linkend="privileges">escalate privileges</link>
to root in order to access subscription information or make changes.</para>
<para>To perform similar tasks from the command line, use the
<ulink url="https://linux.die.net/man/8/subscription-manager"><code>subscription-manager</code></ulink>
tool:</para>
<programlisting>
$ <command>sudo subscription-manager status</command>
+-------------------------------------------+
System Status Details
+-------------------------------------------+
...
</programlisting>
</chapter>

View File

@ -1,38 +0,0 @@
<!DOCTYPE html>
<!--
This file is part of Cockpit.
Copyright (C) 2016 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/>.
-->
<html lang="en">
<head>
<title translatable="yes">Subscriptions</title>
<meta charset="utf-8">
<meta name="description" content="">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="../base1/patternfly.css">
<link href="subscriptions.css" rel="stylesheet">
<script type="text/javascript" src="../base1/cockpit.js"></script>
<script type="text/javascript" src="../*/po.js"></script>
<script type="text/javascript" src="subscriptions.js"></script>
</head>
<body>
<div id="app"></div>
</body>
</html>

View File

@ -1,117 +0,0 @@
/*
* 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 cockpit from "cockpit";
import React from "react";
import ReactDOM from "react-dom";
import { client } from "./subscriptions-client";
import * as subscriptionsRegister from "./subscriptions-register.jsx";
import { SubscriptionsPage } from "./subscriptions-view.jsx";
import { show_modal_dialog } from "cockpit-components-dialog.jsx";
const _ = cockpit.gettext;
var dataStore = { };
function dismissStatusError() {
client.subscriptionStatus.error = undefined;
dataStore.render();
}
var registerDialogDetails;
function registerSystem () {
return client.registerSystem(registerDialogDetails);
}
var footerProps = {
'actions': [
{ 'clicked': registerSystem,
'caption': _("Register"),
'style': 'primary',
},
]
};
function openRegisterDialog() {
registerDialogDetails = subscriptionsRegister.defaultSettings();
// show dialog to register
var renderDialog;
var updatedData = function(prop, data) {
if (prop) {
if (data.target) {
if (data.target.type == "checkbox") {
registerDialogDetails[prop] = data.target.checked;
} else {
registerDialogDetails[prop] = data.target.value;
}
} else {
registerDialogDetails[prop] = data;
}
}
registerDialogDetails.onChange = updatedData;
var dialogProps = {
'title': _("Register system"),
'body': React.createElement(subscriptionsRegister.DialogBody, registerDialogDetails),
};
if (renderDialog)
renderDialog.setProps(dialogProps);
else
renderDialog = show_modal_dialog(dialogProps, footerProps);
};
updatedData();
}
function unregisterSystem() {
client.unregisterSystem();
}
function initStore(rootElement) {
client.addEventListener("dataChanged",
function() {
dataStore.render();
}
);
client.init();
dataStore.render = function() {
ReactDOM.render(React.createElement(
SubscriptionsPage,
{
status: client.subscriptionStatus.status,
products:client.subscriptionStatus.products,
error: client.subscriptionStatus.error,
dismissError: dismissStatusError,
register: openRegisterDialog,
unregister: unregisterSystem,
}),
rootElement
);
};
}
document.addEventListener("DOMContentLoaded", function() {
initStore(document.getElementById('app'));
dataStore.render();
});

View File

@ -1,13 +0,0 @@
{
"version": "@VERSION@",
"requires": {
"cockpit": "122"
},
"tools": {
"index": {
"label": "Subscriptions"
}
},
"content-security-policy": "img-src 'self' data:"
}

View File

@ -1,416 +0,0 @@
/*
* This file is part of Cockpit.
*
* Copyright (C) 2016 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 cockpit from "cockpit";
const _ = cockpit.gettext;
export var client = { };
cockpit.event_target(client);
client.subscriptionStatus = {
serviceStatus: undefined,
status: undefined,
products: [],
error: undefined,
};
// we trigger an event called "dataChanged" when the data has changed
// DBUS service
var service;
function needRender() {
var ev = new Event("dataChanged", { bubbles: false, cancelable: false });
client.dispatchEvent(ev);
}
/* we trigger status update via dbus
* if we don't get a timely reply, consider subscription-manager failure
*/
var updateTimeout;
/*
* Parses lines like:
*
* id: text
*/
function parseSingleSubscription(text) {
var ret = { };
text.split('\n').forEach(function(line, i) {
var pos = line.indexOf(':');
if (pos !== -1)
ret[line.substring(0, pos).trim()] = line.substring(pos + 1).trim();
});
return ret;
}
function parseMultipleSubscriptions(text) {
var ret = [ ];
var segmentInfo, status;
text.split('\n\n').forEach(function(segment) {
if (segment.indexOf('Product Name:') === -1)
return;
segmentInfo = parseSingleSubscription(segment);
status = segmentInfo['Status'];
/* if we have status details, add those to the status */
if (segmentInfo['Status Details'] !== '')
status = status + ' (' + segmentInfo['Status Details'] + ')';
/* convert text output to mustache template variables */
ret.push({
'productName': segmentInfo['Product Name'],
'productId': segmentInfo['Product ID'],
'version': segmentInfo['Version'],
'arch': segmentInfo['Arch'],
'status': status,
'starts': segmentInfo['Starts'],
'ends': segmentInfo['Ends'],
});
});
return ret;
}
var gettingDetails = false;
var getDetailsRequested = false;
function getSubscriptionDetails() {
/* TODO DBus API doesn't deliver what we need, so we call subscription manager
* without translations and parse the output
* https://bugzilla.redhat.com/show_bug.cgi?id=1304056
*/
if (gettingDetails) {
getDetailsRequested = true;
return;
}
getDetailsRequested = false;
gettingDetails = true;
cockpit.spawn(['subscription-manager', 'list'],
{ directory: '/', superuser: "require", environ: ['LC_ALL=C'] })
.done(function(output) {
client.subscriptionStatus.products = parseMultipleSubscriptions(output);
})
.fail(function(ex) {
client.subscriptionStatus.error = ex;
console.warn("Subscriptions [getSubscriptionDetails]: couldn't get details: " + ex);
})
.always(function(output) {
gettingDetails = false;
if (getDetailsRequested)
getSubscriptionDetails();
needRender();
});
}
client.registerSystem = function(subscriptionDetails) {
var dfd = cockpit.defer();
var args = ['subscription-manager', 'register'];
if (subscriptionDetails.url != 'default')
args.push('--serverurl', subscriptionDetails.serverUrl);
// activation keys can't be used with auto-attach
if (subscriptionDetails.activationKeys)
args.push('--activationkey', subscriptionDetails.activationKeys);
else
args.push('--auto-attach');
if (subscriptionDetails.user || subscriptionDetails.password) {
if (!subscriptionDetails.user)
subscriptionDetails.user = '';
if (!subscriptionDetails.password)
subscriptionDetails.password = '';
args.push('--username', subscriptionDetails.user, '--password', subscriptionDetails.password);
}
// proxy is optional
if (subscriptionDetails.proxy) {
if (!subscriptionDetails.proxyServer)
subscriptionDetails.proxyServer = '';
if (!subscriptionDetails.proxyUser)
subscriptionDetails.proxyUser = '';
if (!subscriptionDetails.proxyPass)
subscriptionDetails.proxyPass = '';
args.push('--proxy', subscriptionDetails.proxyServer,
'--proxyuser', subscriptionDetails.proxyUser,
'--proxypass', subscriptionDetails.proxyPass);
}
// only pass org info if user provided it
if (subscriptionDetails.org)
args.push('--org', subscriptionDetails.org);
/* TODO DBus API doesn't deliver what we need, so we call subscription manager
* without translations and parse the output
* https://bugzilla.redhat.com/show_bug.cgi?id=1304056
*/
var process = cockpit.spawn(args, {
directory: '/',
superuser: "require",
environ: ['LC_ALL=C'],
err: "out"
});
var promise;
var buffer = '';
process
.input('')
.stream(function(text) {
buffer += text;
})
.done(function(output) {
dfd.resolve();
})
.fail(function(ex) {
if (ex.problem === "cancelled") {
dfd.reject(ex);
return;
}
/* detect error types we recognize, fall back is generic error */
var invalidUsernameString = 'Invalid username or password.';
var invalidCredentialsString = 'Invalid Credentials';
var message = buffer.trim();
if (message.indexOf(invalidUsernameString) !== -1) {
message = cockpit.format("$0 ($1)", _("Invalid username or password"), message.substring(invalidUsernameString.length).trim());
} else if (message.indexOf(invalidCredentialsString) !== -1) {
message = cockpit.format("$0 ($1)", _("Invalid credentials"), message.substring(invalidCredentialsString.length).trim());
} else if ((message.indexOf('EOF') === 0) && (message.indexOf('Organization:') !== -1)) {
message = _("'Organization' required to register.");
} else if ((message.indexOf('EOF') === 0) && (message.indexOf('Username:') !== -1)) {
message = _("Login/password or activation key required to register.");
} else if (message.indexOf('Must provide --org with activation keys') !== -1) {
message = _("'Organization' required when using activation keys.");
} else if (message.indexOf('The system has been registered') !== -1) {
/*
* Currently we don't separate registration & subscription.
* Our auto-attach may have failed, so close the dialog and
* update status.
*/
dfd.resolve();
return;
} else {
// unrecognized output
console.log("unrecognized subscription-manager failure output: ", ex, message);
}
var error = new Error(message);
dfd.reject(error);
});
promise = dfd.promise();
promise.cancel = function cancel() {
process.close("cancelled");
// we have no idea what the current state is
requestUpdate();
};
return promise;
};
client.unregisterSystem = function() {
var dfd = cockpit.defer();
var args = ['subscription-manager', 'unregister'];
/* TODO DBus API doesn't deliver what we need, so we call subscription manager
* without translations and parse the output
*/
var process = cockpit.spawn(args, {
directory: '/',
superuser: "require",
environ: ['LC_ALL=C'],
err: "out"
});
client.subscriptionStatus.status = "unregistering";
needRender();
var promise;
var buffer = '';
process
.input('')
.stream(function(text) {
buffer += text;
})
.done(function(output) {
dfd.resolve();
})
.fail(function(ex) {
if (ex.problem === "cancelled") {
dfd.reject(ex);
return;
}
var error = new Error(buffer.trim());
dfd.reject(error);
requestUpdate();
});
promise = dfd.promise();
promise.cancel = function cancel() {
process.close("cancelled");
// we have no idea what the current state is
requestUpdate();
};
return promise;
};
function statusUpdateFailed(reason) {
console.warn("Subscription status update failed:", reason);
client.subscriptionStatus.status = "not-found";
needRender();
}
/* request update via DBus
* possible status values: https://github.com/candlepin/subscription-manager/blob/30c3b52320c3e73ebd7435b4fc8b0b6319985d19/src/rhsm_icon/rhsm_icon.c#L98
* [ RHSM_VALID, RHSM_EXPIRED, RHSM_WARNING, RHN_CLASSIC, RHSM_PARTIALLY_VALID, RHSM_REGISTRATION_REQUIRED ]
*/
var subscriptionStatusValues = [
'RHSM_VALID',
'RHSM_EXPIRED',
'RHSM_WARNING',
'RHN_CLASSIC',
'RHSM_PARTIALLY_VALID',
'RHSM_REGISTRATION_REQUIRED'
];
function requestUpdate() {
service.call(
'/EntitlementStatus',
'com.redhat.SubscriptionManager.EntitlementStatus',
'check_status',
[])
.always(function() {
window.clearTimeout(updateTimeout);
})
.done(function(result) {
client.subscriptionStatus.serviceStatus = subscriptionStatusValues[result[0]];
client.getSubscriptionStatus();
})
.catch(function(ex) {
statusUpdateFailed("EntitlementStatus.check_status() failed:", ex);
});
/* TODO: Don't use a timeout here. Needs better API */
updateTimeout = window.setTimeout(
function() {
statusUpdateFailed("timeout");
}, 60000);
}
function processStatusOutput(text, exitDetails) {
if (exitDetails && exitDetails.problem === 'access-denied') {
client.subscriptionStatus.status = "access-denied";
needRender();
return;
}
/* if output isn't as expected, maybe not properly installed? */
if (text.indexOf('Overall Status:') === -1) {
console.warn(text, exitDetails);
client.subscriptionStatus.status = "not-found";
return;
}
/* clear old subscription details */
client.subscriptionStatus.products = [];
var status = parseSingleSubscription(text);
client.subscriptionStatus.status = status['Overall Status'];
/* if refresh was requested, try again - otherwise get details */
if (client.subscriptionStatus !== 'Unknown')
getSubscriptionDetails();
else
needRender();
}
var gettingStatus = false;
var getStatusRequested = false;
/* get subscription summary using 'subscription-manager status' */
client.getSubscriptionStatus = function() {
if (gettingStatus) {
getStatusRequested = true;
return;
}
getStatusRequested = false;
gettingStatus = true;
/* we need a buffer for 'subscription-manager status' output, since that can fail with a return code != 0
* even if we need its output (no valid subscription)
*/
var status_buffer = '';
/* TODO DBus API doesn't deliver what we need, so we call subscription manager
* without translations and parse the output
*
* 'subscription-manager status' will only return with exit code 0 if all is well (and subscriptions current)
*/
cockpit.spawn(['subscription-manager', 'status'],
{ directory: '/', superuser: "require", environ: ['LC_ALL=C'], err: "out" })
.stream(function(text) {
status_buffer += text;
})
.done(function(text) {
processStatusOutput(status_buffer + text, undefined);
})
.fail(function(ex) {
processStatusOutput(status_buffer, ex);
})
.always(function() {
gettingStatus = false;
if (getStatusRequested)
client.getSubscriptionStatus();
});
};
client.init = function() {
service = cockpit.dbus('com.redhat.SubscriptionManager');
/* we want to get notified if subscription status of the system changes */
service.subscribe(
{ path: '/EntitlementStatus',
interface: 'com.redhat.SubscriptionManager.EntitlementStatus',
member: 'entitlement_status_changed'
},
function(path, dbus_interface, signal, args) {
window.clearTimeout(updateTimeout);
/*
* status has changed, now get actual status via command line
* since older versions of subscription-manager don't deliver this via DBus
* note: subscription-manager needs superuser privileges
*/
client.getSubscriptionStatus();
}
);
/* ideally we could get detailed subscription info via DBus, but we
* can't rely on this being present on all systems we work on
*/
service.subscribe(
{ path: "/EntitlementStatus",
interface: "org.freedesktop.DBUS.Properties",
member: "PropertiesChanged"
},
function(path, iface, signal, args) {
client.getSubscriptionStatus();
}
);
// get initial status
requestUpdate();
};

View File

@ -1,194 +0,0 @@
/*
* This file is part of Cockpit.
*
* Copyright (C) 2016 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 React from "react";
import cockpit from "cockpit";
import * as Select from "cockpit-components-select.jsx";
const _ = cockpit.gettext;
export function defaultSettings() {
return {
url: 'default',
serverUrl: 'subscription.rhn.redhat.com',
proxy: false,
proxyServer: '',
proxyUser: '',
proxyPassword: '',
user: '',
password: '',
activationKeys: '',
org: '',
};
}
/* Subscriptions: registration dialog body
* Expected props:
* - onChange callback to signal when the data has changed
* - properties as in defaultRegisterDialogSettings()
*/
export class DialogBody extends React.Component {
render() {
var customURL;
if (this.props.url == 'custom') {
customURL = (
<input id="subscription-register-url-custom" className="form-control" type="text"
value={this.props.serverUrl} onChange={this.props.onChange.bind(this, 'serverUrl')} />
);
}
var proxy;
if (this.props.proxy) {
proxy = [
<br />,
<table className="form-group-ct">
<tbody>
<tr>
<td>
<label className="control-label" htmlFor="subscription-proxy-server">
{_("Server")}
</label>
</td>
<td><input className="form-control" id="subscription-proxy-server" type="text"
placeholder="hostname:port" value={this.props.proxyServer}
onChange={this.props.onChange.bind(this, 'proxyServer')} />
</td>
</tr>
<tr>
<td>
<label className="control-label" htmlFor="subscription-proxy-user">
{_("User")}
</label>
</td>
<td><input className="form-control" id="subscription-proxy-user" type="text"
value={this.props.proxyUser}
onChange={this.props.onChange.bind(this, 'proxyUser')} />
</td>
</tr>
<tr>
<td>
<label className="control-label" htmlFor="subscription-proxy-password">
{_("Password")}
</label>
</td>
<td><input className="form-control" id="subscription-proxy-password" type="password"
value={this.props.proxyPassword}
onChange={this.props.onChange.bind(this, 'proxyPassword')} />
</td>
</tr>
</tbody>
</table>
];
}
var urlEntries = {
'default': _("Default"),
'custom': _("Custom URL"),
};
return (
<div className="modal-body">
<table className="form-table-ct">
<tbody>
<tr>
<td className="top">
<label className="control-label" htmlFor="subscription-register-url">
{_("URL")}
</label>
</td>
<td>
<Select.StatelessSelect key='urlSource'
onChange={ this.props.onChange.bind(this, 'url') }
id="subscription-register-url"
selected={this.props.url}>
<Select.SelectEntry data='default' key='default'>{ urlEntries['default'] }</Select.SelectEntry>
<Select.SelectEntry data='custom' key='custom'>{ urlEntries['custom'] }</Select.SelectEntry>
</Select.StatelessSelect>
{customURL}
</td>
</tr>
<tr>
<td className="top">
<label className="control-label">
{_("Proxy")}
</label>
</td>
<td>
<label>
<input id="subscription-proxy-use" type="checkbox" checked={this.props.proxy}
onChange={ this.props.onChange.bind(this, 'proxy') } />
{_("Use proxy server")}
</label>
{proxy}
</td>
</tr>
<tr>
<td className="top ">
<label className="control-label" htmlFor="subscription-register-username">
{_("Login")}
</label>
</td>
<td>
<input id="subscription-register-username" className="form-control" type="text"
value={this.props.user}
onChange={this.props.onChange.bind(this, 'user')} />
</td>
</tr>
<tr>
<td className="top">
<label className="control-label" htmlFor="subscription-register-password">
{_("Password")}
</label>
</td>
<td>
<input id="subscription-register-password" className="form-control" type="password"
value={this.props.password}
onChange={this.props.onChange.bind(this, 'password')} />
</td>
</tr>
<tr>
<td className="top">
<label className="control-label" htmlFor="subscription-register-key">
{_("Activation Key")}
</label>
</td>
<td>
<input id="subscription-register-key" className="form-control" type="text"
placeholder="key_one,key_two" value={this.props.activationKeys}
onChange={this.props.onChange.bind(this, 'activationKeys')} />
</td>
</tr>
<tr>
<td className="top">
<label className="control-label" htmlFor="subscription-register-org">
{_("Organization")}
</label>
</td>
<td>
<input id="subscription-register-org" className="form-control" type="text"
value={this.props.org}
onChange={this.props.onChange.bind(this, 'org')} />
</td>
</tr>
</tbody>
</table>
</div>
);
}
}

View File

@ -1,263 +0,0 @@
/*
* This file is part of Cockpit.
*
* Copyright (C) 2016 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 React from "react";
import cockpit from "cockpit";
import * as cockpitListing from "cockpit-components-listing.jsx";
const _ = cockpit.gettext;
// Show details for an installed product
class SubscriptionProductDetails extends React.Component {
render() {
return (
<table key={this.props.productId}>
<tbody>
<tr><td className="form-tr-ct-title">{_("Product name")}</td><td><span>{this.props.productName}</span></td></tr>
<tr><td className="form-tr-ct-title">{_("Product ID")}</td><td><span>{this.props.productId}</span></td></tr>
<tr><td className="form-tr-ct-title">{_("Version")}</td><td><span>{this.props.version}</span></td></tr>
<tr><td className="form-tr-ct-title">{_("Architecture")}</td><td><span>{this.props.arch}</span></td></tr>
<tr><td className="form-tr-ct-title">{_("Status")}</td><td><span>{this.props.status}</span></td></tr>
<tr><td className="form-tr-ct-title">{_("Starts")}</td><td><span>{this.props.starts}</span></td></tr>
<tr><td className="form-tr-ct-title">{_("Ends")}</td><td><span>{this.props.ends}</span></td></tr>
</tbody>
</table>
);
}
}
/* 'Curtains' implements a subset of the PatternFly Empty State pattern
* https://www.patternfly.org/patterns/empty-state/
* Special values for icon property:
* - 'waiting' - display spinner
* - 'error' - display error icon
*/
class Curtains extends React.Component {
render() {
var description = null;
if (this.props.description)
description = <h1>{this.props.description}</h1>;
var message = null;
if (this.props.message)
message = <p>{this.props.message}</p>;
var curtains = "curtains-ct";
var icon = this.props.icon;
if (icon == 'waiting')
icon = <div className="spinner spinner-lg" />;
else if (icon == 'error')
icon = <div className="pficon pficon-error-circle-o" />;
return (
<div className={ curtains + " blank-slate-pf" }>
<div className="blank-slate-pf-icon">
{icon}
</div>
{description}
{message}
</div>
);
}
}
/* Component to show a dismissable error, message as child text
* dismissError callback function triggered when the close button is pressed
*/
class DismissableError extends React.Component {
constructor(props) {
super(props);
this.handleDismissError = this.handleDismissError.bind(this);
}
handleDismissError(e) {
// only consider primary mouse button
if (!e || e.button !== 0)
return;
if (this.props.dismissError)
this.props.dismissError();
e.stopPropagation();
}
render() {
return (
<div className="alert alert-danger alert-dismissable alert-ct-top">
<span className="pficon pficon-error-circle-o" />
<span>{this.props.children}</span>
<button type="button" className="close" aria-hidden="true" onClick={this.handleDismissError}>
<span className="pficon pficon-close" />
</button>
</div>
);
}
}
/* Show subscriptions status of the system, offer to register/unregister the system
* Expected properties:
* status subscription status
* error error message to show (in Curtains if not connected, as a dismissable alert otherwise
* dismissError callback, triggered for the dismissable error in connected state
* register callback, triggered when user clicks on register
* unregister callback, triggered when user clicks on unregister
*/
class SubscriptionStatus extends React.Component {
constructor(props) {
super(props);
this.handleRegisterSystem = this.handleRegisterSystem.bind(this);
this.handleUnregisterSystem = this.handleUnregisterSystem.bind(this);
}
handleRegisterSystem(e) {
// only consider primary mouse button
if (!e || e.button !== 0)
return;
this.props.register();
e.stopPropagation();
}
handleUnregisterSystem(e) {
// only consider primary mouse button
if (!e || e.button !== 0)
return;
this.props.unregister();
e.stopPropagation();
}
render() {
var errorMessage;
if (this.props.error) {
errorMessage = (
<DismissableError dismissError={this.props.dismissError}>{this.props.error}</DismissableError>
);
}
var label;
var action;
var note;
var isUnregistering = (this.props.status == "unregistering");
if (this.props.status == 'Unknown') {
label = <label>{ _("Status: System isn't registered") }</label>;
action = (<button className="btn btn-primary"
onClick={this.handleRegisterSystem}>{_("Register")}</button>
);
} else {
label = <label>{ cockpit.format(_("Status: $0"), this.props.status) }</label>;
action = (<button className="btn btn-primary" disabled={isUnregistering}
onClick={this.handleUnregisterSystem}>{_("Unregister")}</button>
);
if (isUnregistering) {
note = (
<div className="dialog-wait-ct">
<div className="spinner spinner-sm" />
<span>{ _("Unregistering system...") }</span>
</div>
);
}
}
return (
<div className="subscription-status-ct">
<h2>{_("Subscriptions")}</h2>
{errorMessage}
{label}
{action}
{note}
</div>
);
}
}
/* Show subscriptions status of the system and registered products, offer to register/unregister the system
* Expected properties:
* status subscription status
* error error message to show (in Curtains if not connected, as a dismissable alert otherwise
* dismissError callback, triggered for the dismissable error in connected state
* products subscribed products (properties as in subscriptions-client)
* register callback, triggered when user clicks on register
* unregister callback, triggered when user clicks on unregister
*/
export class SubscriptionsPage extends React.Component {
constructor(props) {
super(props);
this.renderCurtains = this.renderCurtains.bind(this);
this.renderSubscriptions = this.renderSubscriptions.bind(this);
}
renderCurtains() {
var icon;
var description;
var message;
if (this.props.status === undefined) {
icon = <div className="spinner spinner-lg" />;
message = _("Updating");
description = _("Retrieving subscription status...");
} else if (this.props.status == 'access-denied') {
icon = <i className="fa fa-exclamation-circle" />;
message = _("Access denied");
description = _("The current user isn't allowed to access system subscription status.");
} else {
icon = <i className="fa fa-exclamation-circle" />;
message = _("Unable to connect");
description = _("Couldn't get system subscription status. Please ensure subscription-manager is installed.");
}
return (
<Curtains
icon={icon}
description={description}
message={message} />
);
}
renderSubscriptions() {
var entries = this.props.products.map(function(itm) {
var tabRenderers = [
{
name: _("Details"),
renderer: SubscriptionProductDetails,
data: itm,
},
];
var columns = [ { name: itm.productName, 'header': true } ];
return <cockpitListing.ListingRow key={itm.productId} columns={columns} tabRenderers={tabRenderers} />;
});
return (
<div className="container-fluid">
<SubscriptionStatus {...this.props } />
<cockpitListing.Listing
title={ _("Installed products") }
emptyCaption={ _("No installed products on the system.") }
>
{entries}
</cockpitListing.Listing>
</div>
);
}
render() {
if (this.props.status === undefined ||
this.props.status == 'not-found' ||
this.props.status == 'access-denied') {
return this.renderCurtains();
} else {
return this.renderSubscriptions();
}
}
}

View File

@ -1,88 +0,0 @@
/*
* This file is part of Cockpit.
*
* Copyright (C) 2013 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 "/page.css";
@import "/table.css";
.subscription-status-ct label {
font-size: 13px;
}
.subscription-status-ct button {
margin-left: 12px;
}
.container-fluid.alert {
margin: 20px;
}
#subscription-register-url {
margin-bottom: 3px;
}
#subscriptions-registering > span {
margin-left: 7px;
}
.form-tr-ct-title {
text-align: right;
white-space: nowrap;
color: #888888;
width: 5px; /* will be expanded by nowrap */
padding-right: 7px;
}
.form-td-ct-subscriptions {
width: 80px;
}
#subscription-proxy-use {
vertical-align: sub;
}
.form-table-ct .form-inline {
background: #f4f4f4;
border-width: 1px;
border-style: solid;
border-color: #bababa;
padding: 4px;
}
.form-group-ct {
background: #f4f4f4;
border-width: 1px;
border-style: solid;
border-color: #bababa;
padding: 4px;
width: 100%;
}
.form-group-ct tr td:last-child {
padding-right: 4px;
}
.form-group-ct tr:first-child td:last-child {
padding-top: 6px;
}
.form-group-ct tr:last-child td:last-child {
padding-bottom: 6px;
padding-top:;
}

View File

@ -43,12 +43,6 @@
%define __lib lib
# on RHEL 7.x we build subscriptions; superseded later by
# external subscription-manager-cockpit
%if (0%{?rhel} >= 7 && 0%{?rhel} < 8) && 0%{?centos} == 0
%define build_subscriptions 1
%endif
%if 0%{?rhel} >= 7
%define vdo_on_demand 1
%endif
@ -121,9 +115,7 @@ Recommends: (cockpit-docker if /usr/bin/docker)
Recommends: (cockpit-networkmanager if NetworkManager)
Recommends: (cockpit-storaged if udisks2)
Recommends: cockpit-packagekit
%if 0%{?rhel} >= 8 && 0%{?centos} == 0
Recommends: subscription-manager-cockpit
%endif
Suggests: cockpit-pcp
Suggests: cockpit-selinux
%endif
@ -205,13 +197,6 @@ find %{buildroot}%{_datadir}/cockpit/kdump -type f >> kdump.list
echo '%dir %{_datadir}/cockpit/sosreport' > sosreport.list
find %{buildroot}%{_datadir}/cockpit/sosreport -type f >> sosreport.list
%if %{defined build_subscriptions}
echo '%dir %{_datadir}/cockpit/subscriptions' >> system.list
find %{buildroot}%{_datadir}/cockpit/subscriptions -type f >> system.list
%else
rm -rf %{buildroot}/%{_datadir}/cockpit/subscriptions
%endif
echo '%dir %{_datadir}/cockpit/storaged' > storaged.list
find %{buildroot}%{_datadir}/cockpit/storaged -type f >> storaged.list
@ -411,10 +396,6 @@ Recommends: setroubleshoot-server >= 3.3.3
Provides: cockpit-selinux = %{version}-%{release}
Provides: cockpit-sosreport = %{version}-%{release}
%endif
%if %{defined build_subscriptions}
Provides: cockpit-subscriptions = %{version}-%{release}
Requires: subscription-manager >= 1.13
%endif
# NPM modules which are also available as packages
Provides: bundled(js-jquery) = %{npm-version:jquery}
Provides: bundled(js-moment) = %{npm-version:moment}

View File

@ -42,7 +42,7 @@ override_dh_install:
dpkg-vendor --derives-from ubuntu || rm -r debian/tmp/usr/share/cockpit/branding/ubuntu
# unpackaged modules
for m in kdump selinux subscriptions; do rm -r debian/tmp/usr/share/cockpit/$$m; done
for m in kdump selinux; do rm -r debian/tmp/usr/share/cockpit/$$m; done
rm debian/tmp/usr/share/metainfo/org.cockpit-project.cockpit-kdump.metainfo.xml
rm debian/tmp/usr/share/metainfo/org.cockpit-project.cockpit-selinux.metainfo.xml

View File

@ -95,11 +95,6 @@ var info = {
"storaged/devices.jsx"
],
"subscriptions/subscriptions": [
"subscriptions/main.js",
"subscriptions/subscriptions.css",
],
"systemd/services": [
"systemd/init.js",
"systemd/services.css",