1
0
mirror of https://gitlab.xiph.org/xiph/icecast-server.git synced 2024-06-23 06:25:24 +00:00

Feature: Added a basic dashboard

This commit is contained in:
Philipp Schafft 2020-10-02 21:30:11 +00:00
parent 15dd9af54d
commit 5da8971b2e
4 changed files with 274 additions and 2 deletions

67
admin/dashboard.xsl Normal file
View File

@ -0,0 +1,67 @@
<xsl:stylesheet xmlns:xsl = "http://www.w3.org/1999/XSL/Transform" version = "1.0" xmlns="http://www.w3.org/1999/xhtml">
<xsl:output omit-xml-declaration="no" method="xml" doctype-public="-//W3C//DTD XHTML 1.0 Strict//EN" doctype-system="http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd" indent="yes" encoding="UTF-8" />
<xsl:include href="includes/page.xsl"/>
<xsl:variable name="title">Dashboard</xsl:variable>
<xsl:template name="content">
<h2><xsl:value-of select="$title" /></h2>
<xsl:for-each select="/report/incident">
<xsl:for-each select="resource[@name='overall-status']">
<section class="box">
<h3 class="box_title">Overview for <code><xsl:value-of select="value[@member='global-config']/value[@member='hostname']/@value" /></code></h3>
<ul class="boxnav">
<li><a href="reloadconfig.xsl">Reload Configuration</a></li>
</ul>
<div class="side-by-side">
<div>
<h4>Health</h4>
<div class="trafficlight colour-{value[@member='status']/@value}">&#160;</div>
</div>
<div>
<h4>Current load</h4>
<table class="table-block">
<tbody>
<xsl:for-each select="value[@member='global-current']/value">
<tr>
<xsl:variable name="member" select="@member" />
<xsl:variable name="of" select="../../value[@member='global-config']/value[@member=$member]/@value" />
<td><xsl:value-of select="$member" /></td>
<td class="barmeter">
<span><xsl:value-of select="@value" /> of <xsl:value-of select="$of" /></span>
<div style="width: calc(100% * {@value} / {$of});">&#160;</div>
</td>
</tr>
</xsl:for-each>
</tbody>
</table>
</div>
</div>
</section>
</xsl:for-each>
<xsl:for-each select="resource[@name='maintenance']">
<section class="box">
<h3 class="box_title">Maintenance</h3>
<xsl:choose>
<xsl:when test="value">
<ul class="maintenance-container">
<xsl:for-each select="value">
<li class="maintenance-level-{value[@member='type']/@value}">
<p><xsl:value-of select="text/text()" /></p>
<ul class="references">
<xsl:for-each select="reference">
<li><a href="{@href}"><xsl:value-of select="concat(translate(substring(@type, 1, 1), 'abcdefghijklmnopqrstuvwxyz', 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'), substring(@type, 2))" /></a></li>
</xsl:for-each>
</ul>
</li>
</xsl:for-each>
</ul>
</xsl:when>
<xsl:otherwise>
<p>Nothing to do.</p>
</xsl:otherwise>
</xsl:choose>
</section>
</xsl:for-each>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>

View File

@ -9,6 +9,7 @@
<h1>Icecast Server administration</h1>
</a>
<ul>
<li class="adminlink"><a href="/admin/dashboard.xsl">Dashboard</a></li>
<li class="adminlink"><a href="/admin/stats.xsl">Server status</a></li>
<li class="adminlink"><a href="/admin/listmounts.xsl">Mountpoint list</a></li>
<li class="adminlink"><a href="/admin/showlog.xsl">Logfiles</a></li>

View File

@ -94,6 +94,8 @@
#define SHOWLOG_HTML_REQUEST "showlog.xsl"
#define MARKLOG_RAW_REQUEST "marklog"
#define MARKLOG_HTML_REQUEST "marklog.xsl"
#define DASHBOARD_RAW_REQUEST "dashboard"
#define DASHBOARD_HTML_REQUEST "dashboard.xsl"
#define DEFAULT_RAW_REQUEST ""
#define DEFAULT_HTML_REQUEST ""
#define BUILDM3U_RAW_REQUEST "buildm3u"
@ -119,6 +121,7 @@ static void command_updatemetadata (client_t *client, source_t *source, adm
static void command_buildm3u (client_t *client, source_t *source, admin_format_t response);
static void command_show_log (client_t *client, source_t *source, admin_format_t response);
static void command_mark_log (client_t *client, source_t *source, admin_format_t response);
static void command_dashboard (client_t *client, source_t *source, admin_format_t response);
static const admin_command_handler_t handlers[] = {
{ "*", ADMINTYPE_GENERAL, ADMIN_FORMAT_HTML, NULL, NULL}, /* for ACL framework */
@ -154,8 +157,10 @@ static const admin_command_handler_t handlers[] = {
{ SHOWLOG_HTML_REQUEST, ADMINTYPE_GENERAL, ADMIN_FORMAT_HTML, command_show_log, NULL},
{ MARKLOG_RAW_REQUEST, ADMINTYPE_GENERAL, ADMIN_FORMAT_RAW, command_mark_log, NULL},
{ MARKLOG_HTML_REQUEST, ADMINTYPE_GENERAL, ADMIN_FORMAT_HTML, command_mark_log, NULL},
{ DEFAULT_HTML_REQUEST, ADMINTYPE_HYBRID, ADMIN_FORMAT_HTML, command_stats, NULL},
{ DEFAULT_RAW_REQUEST, ADMINTYPE_HYBRID, ADMIN_FORMAT_HTML, command_stats, NULL}
{ DASHBOARD_RAW_REQUEST, ADMINTYPE_GENERAL, ADMIN_FORMAT_RAW, command_dashboard, NULL},
{ DASHBOARD_HTML_REQUEST, ADMINTYPE_GENERAL, ADMIN_FORMAT_HTML, command_dashboard, NULL},
{ DEFAULT_HTML_REQUEST, ADMINTYPE_HYBRID, ADMIN_FORMAT_HTML, command_dashboard, NULL},
{ DEFAULT_RAW_REQUEST, ADMINTYPE_HYBRID, ADMIN_FORMAT_HTML, command_dashboard, NULL}
};
static admin_command_table_t command_tables[ADMIN_MAX_COMMAND_TABLES] = {
@ -1336,3 +1341,133 @@ static void command_mark_log (client_t *client, source_t *source, adm
admin_send_response_simple(client, source, response, "Logfiles marked", 1);
}
static void __reportxml_add_value(reportxml_node_t *parent, const char *type, const char *member, const char *str)
{
reportxml_node_t *value = reportxml_node_new(REPORTXML_NODE_TYPE_VALUE, NULL, NULL, NULL);
reportxml_node_set_attribute(value, "type", type);
if (member)
reportxml_node_set_attribute(value, "member", member);
reportxml_node_set_attribute(value, "value", str);
reportxml_node_add_child(parent, value);
refobject_unref(value);
}
#define __reportxml_add_value_string(parent,member,value) __reportxml_add_value((parent), "string", (member), (value))
#define __reportxml_add_value_enum(parent,member,value) __reportxml_add_value((parent), "enum", (member), (value))
static void __reportxml_add_value_int(reportxml_node_t *parent, const char *member, long long int value)
{
char buf[80];
snprintf(buf, sizeof(buf), "%lli", value);
__reportxml_add_value(parent, "int", member, buf);
}
static void __reportxml_add_maintenance(reportxml_node_t *parent, const char *type, const char *text, const char *docs)
{
reportxml_node_t *maintenance = reportxml_node_new(REPORTXML_NODE_TYPE_VALUE, NULL, NULL, NULL);
reportxml_node_set_attribute(maintenance, "type", "structure");
reportxml_node_add_child(parent, maintenance);
__reportxml_add_value_enum(maintenance, "type", type);
if (text) {
reportxml_node_t *textnode = reportxml_node_new(REPORTXML_NODE_TYPE_TEXT, NULL, NULL, NULL);
reportxml_node_set_content(textnode, text);
reportxml_node_add_child(maintenance, textnode);
refobject_unref(textnode);
}
if (docs) {
reportxml_node_t *referenenode = reportxml_node_new(REPORTXML_NODE_TYPE_REFERENCE, NULL, NULL, NULL);
reportxml_node_set_attribute(referenenode, "type", "documentation");
reportxml_node_set_attribute(referenenode, "href", docs);
reportxml_node_add_child(maintenance, referenenode);
refobject_unref(referenenode);
}
refobject_unref(maintenance);
}
static void command_dashboard (client_t *client, source_t *source, admin_format_t response)
{
ice_config_t *config = config_get_config();
reportxml_t *report = client_get_reportxml("0aa76ea1-bf42-49d1-887e-ca95fb307dc4", NULL, NULL);
reportxml_node_t *incident = reportxml_get_node_by_type(report, REPORTXML_NODE_TYPE_INCIDENT, 0);
reportxml_node_t *resource;
reportxml_node_t *node;
int has_sources;
int has_many_clients;
int has_too_many_clients;
resource = reportxml_node_new(REPORTXML_NODE_TYPE_RESOURCE, NULL, NULL, NULL);
reportxml_node_set_attribute(resource, "type", "result");
reportxml_node_set_attribute(resource, "name", "overall-status");
node = reportxml_node_new(REPORTXML_NODE_TYPE_VALUE, NULL, NULL, NULL);
reportxml_node_set_attribute(node, "type", "structure");
reportxml_node_set_attribute(node, "member", "global-config");
__reportxml_add_value_string(node, "hostname", config->hostname);
__reportxml_add_value_int(node, "clients", config->client_limit);
__reportxml_add_value_int(node, "sources", config->source_limit);
reportxml_node_add_child(resource, node);
refobject_unref(node);
node = reportxml_node_new(REPORTXML_NODE_TYPE_VALUE, NULL, NULL, NULL);
reportxml_node_set_attribute(node, "type", "structure");
reportxml_node_set_attribute(node, "member", "global-current");
global_lock();
__reportxml_add_value_int(node, "clients", global.clients);
__reportxml_add_value_int(node, "sources", global.sources);
has_sources = global.sources > 0;
has_many_clients = global.clients > ((75 * config->client_limit) / 100);
has_too_many_clients = global.clients > ((90 * config->client_limit) / 100);
global_unlock();
reportxml_node_add_child(resource, node);
refobject_unref(node);
if (config->config_problems || has_too_many_clients) {
__reportxml_add_value_enum(resource, "status", "red");
} else if (!has_sources || has_many_clients) {
__reportxml_add_value_enum(resource, "status", "yellow");
} else {
__reportxml_add_value_enum(resource, "status", "green");
}
reportxml_node_add_child(incident, resource);
refobject_unref(resource);
resource = reportxml_node_new(REPORTXML_NODE_TYPE_RESOURCE, NULL, NULL, NULL);
reportxml_node_set_attribute(resource, "type", "result");
reportxml_node_set_attribute(resource, "name", "maintenance");
if (config->config_problems & CONFIG_PROBLEM_HOSTNAME)
__reportxml_add_maintenance(resource, "warning", "Hostname is not set to anything useful in <hostname>.", NULL);
if (config->config_problems & CONFIG_PROBLEM_LOCATION)
__reportxml_add_maintenance(resource, "warning", "No useful location is given in <location>.", NULL);
if (config->config_problems & CONFIG_PROBLEM_ADMIN)
__reportxml_add_maintenance(resource, "warning", "No admin contact given in <admin>. YP directory support will is disabled.", NULL);
if (!has_sources)
__reportxml_add_maintenance(resource, "info", "Currently no sources are connected to this server.", NULL);
if (has_too_many_clients) {
__reportxml_add_maintenance(resource, "warning", "More than 90% of the server's configured maximum clients are connected", NULL);
} else if (has_many_clients) {
__reportxml_add_maintenance(resource, "info", "More than 75% of the server's configured maximum clients are connected", NULL);
}
__reportxml_add_maintenance(resource, "todo", "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.", "https://icecast.org/");
reportxml_node_add_child(incident, resource);
refobject_unref(resource);
refobject_unref(incident);
config_release_config();
client_send_reportxml(client, report, DOCUMENT_DOMAIN_ADMIN, DASHBOARD_HTML_REQUEST, response, 200, NULL);
}

View File

@ -302,6 +302,19 @@ aside {
margin: 0.4em;
}
.side-by-side {
display: flex;
width: 100%;
}
.side-by-side > *:first-child {
margin-top: 0;
}
.side-by-side > * {
padding-right: 1em;
}
.codeblock {
list-style: none;
width: 100%;
@ -317,6 +330,62 @@ aside {
background-color: #cccccc;
}
.barmeter > *:first-child {
float: right;
display: inline;
}
.barmeter > *:nth-child(2) {
background: #4f8cb0;
display: block;
}
.maintenance-container {
list-style: none;
}
.maintenance-level-warning > *:first-child::before {
font-weight: bold;
content: "Warning: ";
}
.maintenance-level-todo > *:first-child::before {
font-weight: bold;
content: "Todo: ";
}
.maintenance-level-info > *:first-child::before {
font-weight: bold;
content: "Info: ";
}
.references {
list-style: none;
padding: 0;
}
.references > li {
display: inline;
margin-right: 0.4em;
}
.trafficlight {
width: 4em;
height: 4em;
border-radius: 2em;
}
.colour-red {
background: red;
}
.colour-yellow {
background: yellow;
}
.colour-green {
background: green;
}
/* Error messages */
.error {