#!/usr/local/bin/perl
# Display Kea runtime lease, pool, statistics, and log information.

use strict;
use warnings;
require './kea-dhcp-lib.pl';    ## no critic
&ReadParse();
our (%in, %text);
&error_setup($text{'eacl_aviol'});
my %access = &get_module_acl();
&error("$text{'eacl_np'} $text{'eacl_pruntime'}") if (!$access{'runtime'});

&ui_print_header(undef, $text{'runtime_title'}, "", undef, 1, 1);

# Keep runtime views grouped in one page so the module index stays focused on
# configuration, while admins still get the operational data old dhcpd exposed.
my @tabs = (
	[ 'active', $text{'tab_leases_active'} ],
	[ 'pools', $text{'tab_leases_pools'} ],
	[ 'stats', $text{'tab_leases_stats'} ],
	[ 'logs', $text{'tab_leases_logs'} ],
	);
my %valid = map { $_->[0] => 1 } @tabs;
my $mode = $in{'mode'} && $valid{$in{'mode'}} ? $in{'mode'} : 'active';
print &ui_tabs_start(\@tabs, "mode", $mode, 1);

print &ui_tabs_start_tab("mode", "active");
my ($dhcp4_lease_file) = &kea_lease_file(4);
my ($dhcp6_lease_file) = &kea_lease_file(6);
print &ui_div(&text('leases_desc',
		    &sane_file_cell($dhcp4_lease_file),
		    &sane_file_cell($dhcp6_lease_file)));
&print_active_leases(4);
&print_active_leases(6);
print &ui_tabs_end_tab("mode", "active");

print &ui_tabs_start_tab("mode", "pools");
print &ui_div($text{'leases_pools_desc'});
&print_pool_usage(4);
&print_pool_usage(6);
print &ui_tabs_end_tab("mode", "pools");

print &ui_tabs_start_tab("mode", "stats");
print &ui_div($text{'leases_stats_desc'});
&print_lease_statistics();
print &ui_tabs_end_tab("mode", "stats");

print &ui_tabs_start_tab("mode", "logs");
print &ui_div($text{'leases_logs_desc'});
&print_recent_logs();
print &ui_tabs_end_tab("mode", "logs");

print &ui_tabs_end(1);
&ui_print_footer("index.cgi", $text{'index_return'});

# print_active_leases(version)
# Renders active leases for one DHCP protocol version.
sub print_active_leases
{
my ($ver) = @_;
my ($leases, $err) = &kea_active_leases($ver);
print &ui_subheading(&text('comp_dhcp'.$ver));
&print_lease_error_notice($err);
return if ($err);
if (!@$leases) {
	print &ui_p(&text('leases_none', $ver));
	return;
	}

# Lease CSV files use different columns across DHCPv4/DHCPv6 and Kea versions;
# these helpers normalize the common fields administrators need most.
print &ui_columns_start([
	$text{'col_address'},
	$text{'col_id'},
	$text{'col_identifier'},
	$text{'col_hostname'},
	$text{'col_expires'},
	$text{'col_state'},
	], 100);
foreach my $lease (@$leases) {
	print &ui_columns_row([
		&html_escape(&kea_lease_address($lease)),
		&html_escape($lease->{'subnet_id'} || ""),
		&html_escape(&kea_lease_identifier($lease)),
		&html_escape($lease->{'hostname'} || ""),
		&html_escape(&kea_lease_expires($lease)),
		&html_escape(&kea_lease_state($lease)),
		]);
	}
print &ui_columns_end();
}

# print_pool_usage(version)
# Renders configured subnet pool counts beside active lease counts.
sub print_pool_usage
{
my ($ver) = @_;
my ($rows, $err) = &kea_pool_usage_rows($ver);
print &ui_subheading(&text('comp_dhcp'.$ver));
if ($err) {
	print &ui_alert_box(&html_escape($err), "warning", undef, undef, "");
	return;
	}
if (!@$rows) {
	print &ui_p(&text('leases_no_subnets', $ver));
	return;
	}

# This is intentionally per-subnet rather than per-range: Kea pools can be
# ranges, prefixes, and delegated prefix pools, and active lease CSV files only
# reliably tie leases back to subnet IDs.
print &ui_columns_start([
	$text{'col_id'},
	$text{'col_subnet'},
	$text{'col_pools'},
	$text{'col_pd_pools'},
	$text{'col_reservations'},
	$text{'col_active_leases'},
	], 100);
foreach my $row (@$rows) {
	print &ui_columns_row([
		&html_escape($row->{'id'}),
		&html_escape($row->{'subnet'}),
		$row->{'pools'},
		$row->{'pd_pools'},
		$row->{'reservations'},
		$row->{'active'},
		]);
	}
print &ui_columns_end();
}

# print_lease_statistics()
# Renders lease-file counters for DHCPv4 and DHCPv6.
sub print_lease_statistics
{
print &ui_columns_start([
	$text{'col_service'},
	$text{'col_active_leases'},
	$text{'col_total'},
	$text{'col_inactive'},
	$text{'col_file'},
	$text{'col_summary'},
	], 100);
foreach my $ver (4, 6) {
	my $s = &kea_lease_summary($ver);
	print &ui_columns_row([
		&html_escape(&text('comp_dhcp'.$ver)),
		$s->{'active'},
		$s->{'total'},
		$s->{'inactive'},
		&sane_file_cell($s->{'file'}),
		&html_escape($s->{'error'} || ""),
		]);
	}
print &ui_columns_end();
}

# print_recent_logs()
# Renders recent journal entries for configured Kea services.
sub print_recent_logs
{
print &ui_columns_start([
	$text{'col_service'},
	$text{'col_logs'},
	], 100, 0, [ "width=20%", "width=80%" ]);
foreach my $c (&kea_components()) {
	my $logs = &kea_recent_component_log_lines($c, 20);
	my $body = @$logs ? join(&ui_br(), map { &html_escape($_) } @$logs) :
		   &html_escape($text{'leases_no_logs'});
	print &ui_columns_row([
		&html_escape($c->{'label'}),
		$body,
		]);
	}
print &ui_columns_end();
}

# print_lease_error_notice(error)
# Shows non-fatal lease-file read errors for one active-lease table.
sub print_lease_error_notice
{
my ($err) = @_;
print &ui_alert_box(&html_escape($err), "warning", undef, undef, "") if ($err);
}

# sane_file_cell(file)
# Formats a configured file path for display without assuming it exists.
sub sane_file_cell
{
my ($file) = @_;
return "" if (!$file);
return &ui_tag('tt', &html_escape($file));
}
