output: add color support to text output

When --warning or --critical thresholds are defined with text output lines
that exceed threshold will be either yellow (warning) or red (critical).

Signed-off-by: Sami Kerola <kerolasa@iki.fi>
This commit is contained in:
Sami Kerola 2017-11-05 17:07:16 +00:00
parent 48962004b8
commit 344ed2900d
No known key found for this signature in database
GPG key ID: A9553245FDE9B739
8 changed files with 128 additions and 28 deletions

View file

@ -10,6 +10,7 @@ dhcpd-pools \- ISC dhcpd pools usage analysis
.OP \-\-format tHcxXjJ .OP \-\-format tHcxXjJ
.OP \-\-output file .OP \-\-output file
.OP \-\-limit nr .OP \-\-limit nr
.OP \-\-color when
.OP \-\-warning percent .OP \-\-warning percent
.OP \-\-critical percent .OP \-\-critical percent
.OP \-\-warn\-count number .OP \-\-warn\-count number
@ -140,6 +141,24 @@ alarming context. When the alarming is in use, and total is not wanted
to be seen then in the case of alarming returning success nothing is to be seen then in the case of alarming returning success nothing is
printed. printed.
.TP .TP
\fB\-\-color\fR=\fIwhen\fR
Use yellow for warning, red for critical, green for suppressed by \-\-minsize
and blue when \-\-snet\-alarms is the cause of supression. The
.I when
string can be
.BR always ,
.BR never ,
or
.BR auto .
Default is auto, that uses colors when command is running in interactive
terminal. With use of
.B \-\-warning
or
.B \-\-critical
coloring thresholds can be changed, but one must also use
.B \-\-format=text
to avoid turning on alarting mode.
.TP
\fB\-\-warning\fR=\fIpercent\fR \fB\-\-warning\fR=\fIpercent\fR
Turn on alarm output format, and specify percentage number which will Turn on alarm output format, and specify percentage number which will
cause an alarm. If either a range or shared network will exceed cause an alarm. If either a range or shared network will exceed

View file

@ -39,6 +39,7 @@
#include <config.h> #include <config.h>
#include <errno.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include <getopt.h> #include <getopt.h>
@ -109,6 +110,7 @@ int main(int argc, char **argv)
OPT_MINSIZE, OPT_MINSIZE,
OPT_WARN_COUNT, OPT_WARN_COUNT,
OPT_CRIT_COUNT, OPT_CRIT_COUNT,
OPT_COLOR,
OPT_SET_IPV OPT_SET_IPV
}; };
int ret_val; int ret_val;
@ -116,6 +118,7 @@ int main(int argc, char **argv)
static struct option const long_options[] = { static struct option const long_options[] = {
{"config", required_argument, NULL, 'c'}, {"config", required_argument, NULL, 'c'},
{"leases", required_argument, NULL, 'l'}, {"leases", required_argument, NULL, 'l'},
{"color", required_argument, NULL, OPT_COLOR},
{"format", required_argument, NULL, 'f'}, {"format", required_argument, NULL, 'f'},
{"sort", required_argument, NULL, 's'}, {"sort", required_argument, NULL, 's'},
{"reverse", no_argument, NULL, 'r'}, {"reverse", no_argument, NULL, 'r'},
@ -152,6 +155,7 @@ int main(int argc, char **argv)
config.warn_count = 0x100000000; /* == 2^32 that is the entire IPv4 space */ config.warn_count = 0x100000000; /* == 2^32 that is the entire IPv4 space */
config.crit_count = 0x100000000; /* basically turns off the count criteria */ config.crit_count = 0x100000000; /* basically turns off the count criteria */
config.perfdata = 0; config.perfdata = 0;
config.color_mode = color_auto;
/* File location defaults */ /* File location defaults */
strncpy(config.dhcpdconf_file, DHCPDCONF_FILE, MAXLEN - 1); strncpy(config.dhcpdconf_file, DHCPDCONF_FILE, MAXLEN - 1);
strncpy(config.dhcpdlease_file, DHCPDLEASE_FILE, MAXLEN - 1); strncpy(config.dhcpdlease_file, DHCPDLEASE_FILE, MAXLEN - 1);
@ -219,6 +223,11 @@ int main(int argc, char **argv)
config.header_limit = return_limit(optarg[0]); config.header_limit = return_limit(optarg[0]);
config.number_limit = return_limit(optarg[1]); config.number_limit = return_limit(optarg[1]);
break; break;
case OPT_COLOR:
config.color_mode = parse_color_mode(optarg);
if (config.color_mode == color_unknown)
error(EXIT_FAILURE, errno, "unknown color mode: %s", quote(optarg));
break;
case OPT_SNET_ALARMS: case OPT_SNET_ALARMS:
config.snet_alarms = 1; config.snet_alarms = 1;
break; break;

View file

@ -187,6 +187,21 @@ enum limbits {
# define STATE_WARNING 1 # define STATE_WARNING 1
# define STATE_CRITICAL 2 # define STATE_CRITICAL 2
/*! \def COLOR_BOLD_RED
* \brief Shell warning color.
*/
# define COLOR_BOLD_RED "\033[1;31m"
# define COLOR_BOLD_YELLOW "\033[1;33m"
# define COLOR_BOLD_GREEN "\033[1;32m"
# define COLOR_BOLD_BLUE "\033[1;34m"
# define COLOR_RESET "\033[0m"
enum color_mode {
color_unknown,
color_off,
color_on,
color_auto /* default */
};
/*! \var comparer_t /*! \var comparer_t
* \brief Function pointer holding sort algorithm. * \brief Function pointer holding sort algorithm.
*/ */
@ -222,8 +237,10 @@ struct configuration_t {
perfdata:1, perfdata:1,
all_as_shared:1, all_as_shared:1,
header_limit:3, header_limit:3,
number_limit:3; number_limit:3,
color_mode:2;
}; };
/* Global variables */ /* Global variables */
/* \var prefix_length Length of each prefix. */ /* \var prefix_length Length of each prefix. */
extern int prefix_length[2][NUM_OF_PREFIX]; extern int prefix_length[2][NUM_OF_PREFIX];
@ -284,6 +301,7 @@ _DP_ATTRIBUTE_HOT;
extern int xstrstr_v6(const char *restrict str) extern int xstrstr_v6(const char *restrict str)
_DP_ATTRIBUTE_HOT; _DP_ATTRIBUTE_HOT;
extern int parse_color_mode(const char *restrict optarg);
extern double strtod_or_err(const char *restrict str, const char *restrict errmesg); extern double strtod_or_err(const char *restrict str, const char *restrict errmesg);
extern void __attribute__ ((noreturn)) print_version(void); extern void __attribute__ ((noreturn)) print_version(void);
extern void __attribute__ ((noreturn)) usage(int status); extern void __attribute__ ((noreturn)) usage(int status);

View file

@ -454,6 +454,22 @@ int
return NUM_OF_PREFIX; return NUM_OF_PREFIX;
} }
/*! \brief Parse option argument color mode.
*
* \param Color mode string.
* \return color mode enum.
*/
int parse_color_mode(const char *restrict optarg)
{
if (!strcmp(optarg, "always"))
return color_on;
if (!strcmp(optarg, "auto"))
return color_auto;
if (!strcmp(optarg, "never"))
return color_off;
return color_unknown;
}
/*! \brief Return a double floating point value. /*! \brief Return a double floating point value.
* *
* \param str String to be converted to a double. * \param str String to be converted to a double.
@ -562,6 +578,7 @@ void __attribute__ ((__noreturn__)) usage(int status)
fputs( " -r, --reverse reverse order sort\n", out); fputs( " -r, --reverse reverse order sort\n", out);
fputs( " -o, --output=FILE output into a file\n", out); fputs( " -o, --output=FILE output into a file\n", out);
fputs( " -L, --limit=NR output limit mask 77 - 00\n", out); fputs( " -L, --limit=NR output limit mask 77 - 00\n", out);
fputs( " --color=WHEN use colors 'always', 'never', or 'auto'\n", out);
fputs( " --warning=PERC set warning alarming limit\n", out); fputs( " --warning=PERC set warning alarming limit\n", out);
fputs( " --critical=PERC set critical alarming limit\n", out); fputs( " --critical=PERC set critical alarming limit\n", out);
fputs( " --warn-count=NR a number of free leases before warning raised\n", out); fputs( " --warn-count=NR a number of free leases before warning raised\n", out);

View file

@ -50,6 +50,7 @@
#include <stdlib.h> #include <stdlib.h>
#include <sys/stat.h> #include <sys/stat.h>
#include <time.h> #include <time.h>
#include <unistd.h>
#include "close-stream.h" #include "close-stream.h"
#include "error.h" #include "error.h"
@ -70,25 +71,19 @@ static void range_output_helper(struct output_helper_t *oh, struct range_t *rang
oh->bup = (double)(100 * range_p->backups) / oh->range_size; oh->bup = (double)(100 * range_p->backups) / oh->range_size;
} }
/* set status */ /* set status */
if (config.snet_alarms && range_p->shared_net != shared_networks) {
oh->status = STATUS_SUPPRESSED;
return;
}
if (oh->range_size <= config.minsize) {
oh->status = STATUS_IGNORED;
return;
}
if (config.critical < oh->percent
&& (oh->range_size - range_p->count) < config.crit_count) {
oh->status = STATUS_CRIT;
return;
}
if (config.warning < oh->percent
&& (oh->range_size - range_p->count) < config.warn_count) {
oh->status = STATUS_WARN;
return;
}
oh->status = STATUS_OK; oh->status = STATUS_OK;
if (config.critical < oh->percent
&& (oh->range_size - range_p->count) < config.crit_count)
oh->status = STATUS_CRIT;
else if (config.warning < oh->percent
&& (oh->range_size - range_p->count) < config.warn_count)
oh->status = STATUS_WARN;
if (oh->status != STATUS_OK) {
if (oh->range_size <= config.minsize)
oh->status = STATUS_IGNORED;
else if (config.snet_alarms && range_p->shared_net != shared_networks)
oh->status = STATUS_SUPPRESSED;
}
} }
/*! \brief Calculate shared network percentages and such. */ /*! \brief Calculate shared network percentages and such. */
@ -109,7 +104,11 @@ static void shnet_output_helper(struct output_helper_t *oh, struct shared_networ
} }
} }
/* set status */ /* set status */
if (oh->percent == NAN || shared_p->available <= config.minsize) { if (oh->percent == NAN) {
oh->status = STATUS_SUPPRESSED;
return;
}
if (shared_p->available <= config.minsize) {
oh->status = STATUS_IGNORED; oh->status = STATUS_IGNORED;
return; return;
} }
@ -124,6 +123,26 @@ static void shnet_output_helper(struct output_helper_t *oh, struct shared_networ
oh->status = STATUS_OK; oh->status = STATUS_OK;
} }
static int start_color(struct output_helper_t *oh, FILE *outfile)
{
if (oh->status == STATUS_CRIT) {
fputs(COLOR_BOLD_RED, outfile);
return 1;
}
if (oh->status == STATUS_WARN) {
fputs(COLOR_BOLD_YELLOW, outfile);
return 1;
}
if (oh->status == STATUS_IGNORED) {
fputs(COLOR_BOLD_GREEN, outfile);
return 1;
}
if (oh->status == STATUS_SUPPRESSED) {
fputs(COLOR_BOLD_BLUE, outfile);
return 1;
}
return 0;
}
/*! \brief Text output format, which is the default. */ /*! \brief Text output format, which is the default. */
static int output_txt(void) static int output_txt(void)
{ {
@ -135,6 +154,10 @@ static int output_txt(void)
FILE *outfile; FILE *outfile;
int max_ipaddr_length = config.ip_version == IPv6 ? 39 : 16; int max_ipaddr_length = config.ip_version == IPv6 ? 39 : 16;
if (config.color_mode == color_auto && isatty(STDIN_FILENO)) {
config.color_mode = color_on;
}
if (config.output_file[0]) { if (config.output_file[0]) {
outfile = fopen(config.output_file, "w+"); outfile = fopen(config.output_file, "w+");
if (outfile == NULL) { if (outfile == NULL) {
@ -164,7 +187,10 @@ static int output_txt(void)
} }
if (config.number_limit & R_BIT) { if (config.number_limit & R_BIT) {
for (i = 0; i < num_ranges; i++) { for (i = 0; i < num_ranges; i++) {
int color_set = 0;
range_output_helper(&oh, range_p); range_output_helper(&oh, range_p);
if (config.color_mode == color_on)
color_set = start_color(&oh, outfile);
if (range_p->shared_net) { if (range_p->shared_net) {
fprintf(outfile, "%-20s", range_p->shared_net->name); fprintf(outfile, "%-20s", range_p->shared_net->name);
} else { } else {
@ -189,6 +215,8 @@ static int output_txt(void)
fprintf(outfile, "%7g %8.3f", fprintf(outfile, "%7g %8.3f",
range_p->backups, oh.bup); range_p->backups, oh.bup);
} }
if (color_set)
fputs(COLOR_RESET, outfile);
fprintf(outfile, "\n"); fprintf(outfile, "\n");
range_p++; range_p++;
} }
@ -207,8 +235,11 @@ static int output_txt(void)
} }
if (config.number_limit & S_BIT) { if (config.number_limit & S_BIT) {
for (i = 0; i < num_shared_networks; i++) { for (i = 0; i < num_shared_networks; i++) {
int color_set = 0;
shared_p++; shared_p++;
shnet_output_helper(&oh, shared_p); shnet_output_helper(&oh, shared_p);
if (config.color_mode == color_on)
color_set = start_color(&oh, outfile);
fprintf(outfile, fprintf(outfile,
"%-20s %5g %5g %10.3f %7g %6g %9.3f", "%-20s %5g %5g %10.3f %7g %6g %9.3f",
shared_p->name, shared_p->name,
@ -223,7 +254,8 @@ static int output_txt(void)
shared_p->backups, shared_p->backups,
oh.bup); oh.bup);
} }
if (color_set)
fputs(COLOR_RESET, outfile);
fprintf(outfile, "\n"); fprintf(outfile, "\n");
} }
} }
@ -241,7 +273,10 @@ static int output_txt(void)
fprintf(outfile, "\n"); fprintf(outfile, "\n");
} }
if (config.number_limit & A_BIT) { if (config.number_limit & A_BIT) {
int color_set = 0;
shnet_output_helper(&oh, shared_networks); shnet_output_helper(&oh, shared_networks);
if (config.color_mode == color_on)
color_set = start_color(&oh, outfile);
fprintf(outfile, "%-20s %5g %5g %10.3f %7g %6g %9.3f", fprintf(outfile, "%-20s %5g %5g %10.3f %7g %6g %9.3f",
shared_networks->name, shared_networks->name,
shared_networks->available, shared_networks->available,
@ -256,6 +291,8 @@ static int output_txt(void)
shared_networks->backups, shared_networks->backups,
oh.bup); oh.bup);
} }
if (color_set)
fputs(COLOR_RESET, outfile);
fprintf(outfile, "\n"); fprintf(outfile, "\n");
} }
if (outfile == stdout) { if (outfile == stdout) {

View file

@ -10,25 +10,25 @@ fi
echo '== warn count ==' > tests/outputs/$IAM echo '== warn count ==' > tests/outputs/$IAM
dhcpd-pools --config $top_srcdir/tests/confs/complete --leases $top_srcdir/tests/leases/complete \ dhcpd-pools --config $top_srcdir/tests/confs/complete --leases $top_srcdir/tests/leases/complete \
--warning=40 --warn-count=20 --output=tests/outputs/$IAM-too --color=never --warning=40 --warn-count=20 --output=tests/outputs/$IAM-too
echo $? >> tests/outputs/$IAM-too echo $? >> tests/outputs/$IAM-too
cat tests/outputs/$IAM-too >> tests/outputs/$IAM cat tests/outputs/$IAM-too >> tests/outputs/$IAM
echo '== crit count ==' >> tests/outputs/$IAM echo '== crit count ==' >> tests/outputs/$IAM
dhcpd-pools --config $top_srcdir/tests/confs/complete --leases $top_srcdir/tests/leases/complete \ dhcpd-pools --config $top_srcdir/tests/confs/complete --leases $top_srcdir/tests/leases/complete \
--critical=40 --crit-count=20 --output=tests/outputs/$IAM-too --color=never --critical=40 --crit-count=20 --output=tests/outputs/$IAM-too
echo $? >> tests/outputs/$IAM-too echo $? >> tests/outputs/$IAM-too
cat tests/outputs/$IAM-too >> tests/outputs/$IAM cat tests/outputs/$IAM-too >> tests/outputs/$IAM
echo '== minsize ==' >> tests/outputs/$IAM echo '== minsize ==' >> tests/outputs/$IAM
dhcpd-pools -c $top_srcdir/tests/confs/complete -l $top_srcdir/tests/leases/complete \ dhcpd-pools -c $top_srcdir/tests/confs/complete -l $top_srcdir/tests/leases/complete \
--warning=40 --warn-count=20 --minsize=40 -o tests/outputs/$IAM-too --color=never --warning=40 --warn-count=20 --minsize=40 -o tests/outputs/$IAM-too
echo $? >> tests/outputs/$IAM-too echo $? >> tests/outputs/$IAM-too
cat tests/outputs/$IAM-too >> tests/outputs/$IAM cat tests/outputs/$IAM-too >> tests/outputs/$IAM
echo '== snet alarms ==' >> tests/outputs/$IAM echo '== snet alarms ==' >> tests/outputs/$IAM
dhcpd-pools -c $top_srcdir/tests/confs/complete -l $top_srcdir/tests/leases/complete \ dhcpd-pools -c $top_srcdir/tests/confs/complete -l $top_srcdir/tests/leases/complete \
--warning=40 --snet-alarms -o tests/outputs/$IAM-too --color=never --warning=40 --snet-alarms -o tests/outputs/$IAM-too
echo $? >> tests/outputs/$IAM-too echo $? >> tests/outputs/$IAM-too
cat tests/outputs/$IAM-too >> tests/outputs/$IAM cat tests/outputs/$IAM-too >> tests/outputs/$IAM

View file

@ -7,10 +7,10 @@ CRITICAL: dhcpd-pools: Ranges - crit: 3 warn: 0 ok: 2; | range_crit=3 range_warn
Shared nets - crit: 1 warn: 0 ok: 1; | snet_crit=1 snet_warn=0 snet_ok=1 Shared nets - crit: 1 warn: 0 ok: 1; | snet_crit=1 snet_warn=0 snet_ok=1
2 2
== minsize == == minsize ==
OK: Ranges - crit: 0 warn: 0 ok: 0 ignored: 5; | range_crit=0 range_warn=0 range_ok=0 range_ignored=5 OK: Ranges - crit: 0 warn: 0 ok: 2 ignored: 3; | range_crit=0 range_warn=0 range_ok=2 range_ignored=3
Shared nets - crit: 0 warn: 0 ok: 0 ignored: 2; | snet_crit=0 snet_warn=0 snet_ok=0 snet_ignored=2 Shared nets - crit: 0 warn: 0 ok: 0 ignored: 2; | snet_crit=0 snet_warn=0 snet_ok=0 snet_ignored=2
0 0
== snet alarms == == snet alarms ==
WARNING: dhcpd-pools: Ranges - crit: 0 warn: 0 ok: 1; | range_crit=0 range_warn=0 range_ok=1 WARNING: dhcpd-pools: Ranges - crit: 0 warn: 0 ok: 2; | range_crit=0 range_warn=0 range_ok=2
Shared nets - crit: 0 warn: 2 ok: 0; | snet_crit=0 snet_warn=2 snet_ok=0 Shared nets - crit: 0 warn: 2 ok: 0; | snet_crit=0 snet_warn=2 snet_ok=0
1 1

View file

@ -8,7 +8,7 @@ if [ ! -d tests/outputs ]; then
mkdir tests/outputs mkdir tests/outputs
fi fi
dhcpd-pools -c $top_srcdir/tests/confs/$IAM \ dhcpd-pools -c $top_srcdir/tests/confs/$IAM --color=never \
-l $top_srcdir/tests/leases/$IAM -o tests/outputs/$IAM -l $top_srcdir/tests/leases/$IAM -o tests/outputs/$IAM
diff -u $top_srcdir/tests/expected/$IAM tests/outputs/$IAM diff -u $top_srcdir/tests/expected/$IAM tests/outputs/$IAM
exit $? exit $?