mirror of
https://gitlab.xiph.org/xiph/icecast-server.git
synced 2024-12-04 14:46:30 -05:00
Merge branch 'feature-dashboard'
This commit is contained in:
commit
1e105ff2e9
67
admin/dashboard.xsl
Normal file
67
admin/dashboard.xsl
Normal 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}"> </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="concat(translate(substring(@member, 1, 1), 'abcdefghijklmnopqrstuvwxyz', 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'), substring(@member, 2))" /></td>
|
||||
<td class="barmeter">
|
||||
<span><xsl:value-of select="@value" /> of <xsl:value-of select="$of" /></span>
|
||||
<div style="width: calc(100% * {@value} / {$of});"> </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>
|
@ -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>
|
||||
|
148
src/admin.c
148
src/admin.c
@ -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"
|
||||
@ -104,6 +106,7 @@ typedef struct {
|
||||
const admin_command_handler_t *handlers;
|
||||
} admin_command_table_t;
|
||||
|
||||
static void command_default_selector (client_t *client, source_t *source, admin_format_t response);
|
||||
static void command_fallback (client_t *client, source_t *source, admin_format_t response);
|
||||
static void command_metadata (client_t *client, source_t *source, admin_format_t response);
|
||||
static void command_shoutcast_metadata (client_t *client, source_t *source, admin_format_t response);
|
||||
@ -119,6 +122,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 +158,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_default_selector, NULL},
|
||||
{ DEFAULT_RAW_REQUEST, ADMINTYPE_HYBRID, ADMIN_FORMAT_HTML, command_default_selector, NULL}
|
||||
};
|
||||
|
||||
static admin_command_table_t command_tables[ADMIN_MAX_COMMAND_TABLES] = {
|
||||
@ -651,6 +657,16 @@ static void html_success(client_t *client, char *message)
|
||||
}
|
||||
|
||||
|
||||
static void command_default_selector (client_t *client, source_t *source, admin_format_t response)
|
||||
{
|
||||
if (client->mode == OMODE_LEGACY) {
|
||||
command_stats(client, source, response);
|
||||
} else {
|
||||
command_dashboard(client, source, response);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void command_move_clients(client_t *client,
|
||||
source_t *source,
|
||||
admin_format_t response)
|
||||
@ -1336,3 +1352,131 @@ 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_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);
|
||||
}
|
||||
|
@ -936,6 +936,7 @@ static void _set_defaults(ice_config_t *configuration)
|
||||
|
||||
static inline void __check_hostname(ice_config_t *configuration)
|
||||
{
|
||||
int sane_hostname = 0;
|
||||
char *p;
|
||||
|
||||
/* ensure we have a non-NULL buffer: */
|
||||
@ -948,10 +949,9 @@ static inline void __check_hostname(ice_config_t *configuration)
|
||||
*p += 'a' - 'A';
|
||||
}
|
||||
|
||||
configuration->sane_hostname = 0;
|
||||
switch (util_hostcheck(configuration->hostname)) {
|
||||
case HOSTCHECK_SANE:
|
||||
configuration->sane_hostname = 1;
|
||||
sane_hostname = 1;
|
||||
break;
|
||||
case HOSTCHECK_ERROR:
|
||||
ICECAST_LOG_ERROR("Can not check hostname \"%s\".",
|
||||
@ -985,6 +985,9 @@ static inline void __check_hostname(ice_config_t *configuration)
|
||||
"listings.");
|
||||
break;
|
||||
}
|
||||
|
||||
if (!sane_hostname)
|
||||
configuration->config_problems |= CONFIG_PROBLEM_HOSTNAME;
|
||||
}
|
||||
|
||||
static void _parse_root(xmlDocPtr doc,
|
||||
@ -1163,6 +1166,7 @@ static void _parse_root(xmlDocPtr doc,
|
||||
"value \"%s\".", CONFIG_DEFAULT_LOCATION);
|
||||
if (!configuration->location)
|
||||
configuration->location = (char *) xmlCharStrdup(CONFIG_DEFAULT_LOCATION);
|
||||
configuration->config_problems |= CONFIG_PROBLEM_LOCATION;
|
||||
}
|
||||
|
||||
if (!configuration->admin ||
|
||||
@ -1173,6 +1177,7 @@ static void _parse_root(xmlDocPtr doc,
|
||||
/* FIXME actually disable YP */
|
||||
if (!configuration->admin)
|
||||
configuration->admin = (char *) xmlCharStrdup(CONFIG_DEFAULT_ADMIN);
|
||||
configuration->config_problems |= CONFIG_PROBLEM_ADMIN;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -28,6 +28,10 @@
|
||||
|
||||
#define XMLSTR(str) ((xmlChar *)(str))
|
||||
|
||||
#define CONFIG_PROBLEM_HOSTNAME 0x0001U
|
||||
#define CONFIG_PROBLEM_LOCATION 0x0002U
|
||||
#define CONFIG_PROBLEM_ADMIN 0x0004U
|
||||
|
||||
typedef enum _http_header_type {
|
||||
/* static: headers are passed as is to the client. */
|
||||
HTTP_HEADER_TYPE_STATIC,
|
||||
@ -198,6 +202,8 @@ typedef struct {
|
||||
struct ice_config_tag {
|
||||
char *config_filename;
|
||||
|
||||
unsigned int config_problems;
|
||||
|
||||
char *location;
|
||||
char *admin;
|
||||
|
||||
@ -220,7 +226,6 @@ struct ice_config_tag {
|
||||
struct event_registration_tag *event;
|
||||
|
||||
char *hostname;
|
||||
int sane_hostname;
|
||||
int port;
|
||||
char *mimetypes_fn;
|
||||
|
||||
|
@ -556,7 +556,7 @@ static inline void __log_system_name(void) {
|
||||
|
||||
if (have_hostname) {
|
||||
config = config_get_config();
|
||||
if (!config->sane_hostname && util_hostcheck(hostname) == HOSTCHECK_SANE) {
|
||||
if ((config->config_problems & CONFIG_PROBLEM_HOSTNAME) && util_hostcheck(hostname) == HOSTCHECK_SANE) {
|
||||
ICECAST_LOG_WARN("Hostname is not set to anything useful in <hostname>, Consider setting it to the system's name \"%s\".", hostname);
|
||||
}
|
||||
config_release_config();
|
||||
|
@ -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: limegreen;
|
||||
}
|
||||
|
||||
/* Error messages */
|
||||
|
||||
.error {
|
||||
|
Loading…
Reference in New Issue
Block a user