diff --git a/README.md b/README.md index 21d82e6..19b0995 100644 --- a/README.md +++ b/README.md @@ -4,20 +4,23 @@ FeePBX Call Status Forked from http://sysadminman.net/blog/2013/asterisk-outbound-call-status-page-5600 and modified for FreePBX system running version 2.9 or higher. This is a simple HTML page that displays all active calls on a FreePBX system. This is not a FreePBX module, and does not require system credentials to view. +Note that I'm weird about dashes in directories and filenames, so I removed the original developer's dashes from call-status making it callstatus. Small detail, but it might trip you up if you weren't aware of it. + ## Requirements * FreePBX version 2.9 or later * Tested with Asterisk 1.8 and 11 ## Installation -* On a FreePBX system, download the files to the folder `[webroot]/call-status` +* On a FreePBX system, download the files to the folder `[webroot]/callstatus` * chown and chmod folder and files as necessary -* In a browser, navigate to `http:///call-status`. +* In a browser, navigate to `http:///callstatus`. +* To add to the FreePBX System Status dashboard, modify the `[webroot]/admin/modules/dashboard/page.index.php` file according to the example included here under `httproot...` As an example, to install on a Centos based distro such as PIAF or the FreePBX distro, you might use the following commands: ``` cd /var/www/html -git clone https://github.com/POSSA/freepbx-Call_Status.git call-status -chown -R asterisk:asterisk /var/www/html/call-status +git clone https://github.com/shdwlynx/freepbx-Call_Status.git callstatus +chown -R asterisk:asterisk /var/www/html/callstatus ``` ## License This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. diff --git a/httproot/admin/modules/dashboard/page.index.php b/httproot/admin/modules/dashboard/page.index.php new file mode 100644 index 0000000..36056a5 --- /dev/null +++ b/httproot/admin/modules/dashboard/page.index.php @@ -0,0 +1,757 @@ +'graphok', + 70=>'graphwarn', + 90=>'grapherror', + ); + } + + $chars_per_pixel = 7; + if (strlen($text) * $chars_per_pixel > $total_width - 35) { + $text_trimmed = substr($text,0, floor(($total_width - 35) / $chars_per_pixel)).'..'; + } else { + $text_trimmed = $text; + } + + $clean_val = preg_replace("/[^0-9\.]*/","",$val); + + if ($total == 0) { + $percent = ($clean_val == 0) ? 0 : 100; + } else { + $percent = round($clean_val/$total*100); + } + + $graph_class = false; + foreach ($classes as $limit=>$class) { + if (!$graph_class) { + $graph_class = $class; + } + if ($limit <= $percent) { + $graph_class = $class; + } else { + break; + } + } + $width = $total_width * ($percent/100); + if ($width > $total_width) { + $width = $total_width; + } + + $tooltip = $text.": ".$val.$real_units." / ".$total.$real_units." (".$percent."%)"; + $display_value = ($show_percent ? $percent."%" : $val.$real_units); + + $out = "
\n"; + $out .= "
\n"; + $out .= "
".$text_trimmed."
\n"; + $out .= "
".$display_value."
\n"; + $out .= "
\n"; + + return $out; +} + +function draw_status_box($text, $status, $tooltip = false, $total_width = 200) { + switch ($status) { + case "ok": + $status_text = _("OK"); + $class = "graphok"; + break; + case "warn": + $status_text = _("Warn"); + $class = "graphwarn"; + break; + case "error": + $status_text = _("ERROR"); + $class = "grapherror"; + break; + case "disabled": + $status_text = _("Disabled"); + $class = ""; + break; + } + if ($tooltip !== false) { + $status_text = ''.$status_text.''; + } + + $out = "
\n"; + $out .= "
".$text."
\n"; + $out .= "
".$status_text."
\n"; + $out .= "
\n"; + + return $out; +} + +function draw_box($text, $value, $total_width = 200) { + $tooltip = $text.": ".$value; + + $out = "
\n"; + $out .= "
".$text."
\n"; + $out .= " \n"; + $out .= "
\n"; + + return $out; +} + +function time_string($seconds) { + if ($seconds == 0) { + return "0 "._("minutes"); + } elseif ($seconds < 60) { + return "$seconds "._("seconds"); + } + + $minutes = floor($seconds / 60); + $seconds = $seconds % 60; + + $hours = floor($minutes / 60); + $minutes = $minutes % 60; + + $days = floor($hours / 24); + $hours = $hours % 24; + + $weeks = floor($days / 7); + $days = $days % 7; + + $output = array(); + if ($weeks) { + $output[] = $weeks." ".($weeks == 1 ? _("week") : _("weeks")); + } + if ($days) { + $output[] = $days." ".($days == 1 ? _("days") : _("days")); + } + if ($hours) { + $output[] = $hours." ".($hours == 1 ? _("hour") : _("hours")); + } + if ($minutes) { + $output[] = $minutes." ".($minutes == 1 ? _("minute") : _("minutes")); + } + + return implode(", ",$output); +} + +function show_sysstats() { + global $sysinfo; + $out = ''; + + $out .= "

"._("System Statistics")."

"; + $out .= "

"._("Processor")."

"; + $loadavg = $sysinfo->loadavg(true); + $out .= draw_box(_("Load Average"), $loadavg['avg'][0]); + $out .= draw_graph(_("CPU"), "", number_format($loadavg['cpupercent'],2), 100); + + $out .= "

"._("Memory")."

"; + $memory = $sysinfo->memory(); + $app_memory = isset($memory["ram"]["app"]) ? + $memory["ram"]["app"] : + $memory["ram"]["total"] - $memory["ram"]["t_free"] - $memory['ram']['cached'] - $memory['ram']['buffers']; + + $out .= draw_graph(_("App Memory"), "MB", number_format($app_memory/1024,2), $memory["ram"]["total"]/1024); + $out .= draw_graph(_("Swap"), "MB", number_format(($memory["swap"]["total"]-$memory["swap"]["free"])/1024,2), $memory["swap"]["total"]/1024); + + $out .= "

"._("Disks")."

"; + foreach ($sysinfo->filesystems() as $fs) { + $out .= draw_graph($fs["mount"], "GB", number_format($fs["used"]/1024/1024, 2,".",""), number_format($fs["size"]/1024/1024,2,".",""), strpos( $fs["options"],"ro" )!==false ? array(0=>"graphok"):null); + } + + $out .= "

"._("Networks")."

"; + foreach ($sysinfo->network() as $net_name=>$net) { + $net_name = trim($net_name); + if ($net_name == 'lo' || $net_name == 'sit0' || preg_match('/w.g./',$net_name)) continue; + + $tx = new average_rate_calculator($_SESSION["netstats"][$net_name]["tx"], 10); // 30s max age + $rx = new average_rate_calculator($_SESSION["netstats"][$net_name]["rx"], 10); // 30s max age + + $rx->add( $net["rx_bytes"] ); + $tx->add( $net["tx_bytes"] ); + + $out .= draw_box($net_name." "._("receive"), number_format($rx->average()/1000,2)." KB/s"); + $out .= draw_box($net_name." "._("transmit"), number_format($tx->average()/1000,2)." KB/s"); + } + return $out; +} + +function show_aststats() { + global $amp_conf; + global $astinfo; + global $db; + $out = ''; + + $channels = $astinfo->get_channel_totals(); + // figure out max_calls + + // guess at the max calls: number of users + if (!isset($_SESSION["calculated_max_calls"])) { + // set max calls to either MAXCALLS in amportal.conf, or the number of users in the system + if (isset($amp_conf['MAXCALLS'])) { + $_SESSION["calculated_max_calls"] = $amp_conf["MAXCALLS"]; + } else if (function_exists('core_users_list')) { + $_SESSION["calculated_max_calls"] = count(core_users_list()); + } else { + $_SESSION["calculated_max_calls"] = 1; + } + } + // we currently see more calls than we guessed, increase it + if ($channels['total_calls'] > $_SESSION["calculated_max_calls"]) { + $_SESSION["calculated_max_calls"] = $channels['total_calls']; + } + $max_calls = $_SESSION["calculated_max_calls"]; + + $classes = array(0=>'graphok'); + $max_chans = $max_calls * 2; + + $out .= "

".sprintf(_("%s Statistics"), DASHBOARD_FREEPBX_BRAND)."

"; + $out .= draw_graph(_('Total active calls'), '', $channels['total_calls'], $max_calls, $classes , false, BAR_WIDTH_LEFT); + $out .= draw_graph(_('Internal calls'), '', $channels['internal_calls'], $max_calls, $classes , false, BAR_WIDTH_LEFT); + $out .= draw_graph(_('External calls'), '', $channels['external_calls'], $max_calls, $classes , false, BAR_WIDTH_LEFT); + $out .= draw_graph(_('Total active channels'), '', $channels['total_channels'], $max_chans, $classes , false, BAR_WIDTH_LEFT); + + $out .= "

".sprintf(_("%s Connections"), DASHBOARD_FREEPBX_BRAND)."

"; + + /* This is generally very bad style, and we should look at adding this to core_devices_list or another core + * function. However, since this is in Ajax lite weight code, it is currently the cleanest way to get the sip and iax2 + * devices in a hash format that we would like to pass to the class + */ + $sql = "SELECT `id` FROM `devices` WHERE `tech` IN ('sip', 'iax2')"; + $devices = $db->getCol($sql); + if(DB::IsError($devices)) { + $devices = false; + } else { + $devices = array_flip($devices); + } + + $conns = $astinfo->get_connections( $devices ); + + if ($conns['users_total'] > 0) { + $out .= draw_graph(_('IP Phones Online'), '', $conns['users_online'], $conns['users_total'], $classes, false, BAR_WIDTH_LEFT); + } + if ($conns['trunks_total'] > 0) { + $out .= draw_graph(_('IP Trunks Online'), '', $conns['trunks_online'], $conns['trunks_total'], $classes, false, BAR_WIDTH_LEFT); + } + if ($conns['registrations_total'] > 0) { + $out .= draw_graph(_('IP Trunk Registrations'), '', $conns['registrations_online'], $conns['registrations_total'], $classes, false, BAR_WIDTH_LEFT); + } + + return $out; +} + +function show_sysinfo() { + global $sysinfo; + global $astinfo; + $out = "

"._("Uptime")."


"; + $out .= ''; + /* + $out .= ''; + $out .= ''; + $cpu = $sysinfo->cpu_info(); + $out .= ''; + */ + + $out .= ''; + $ast_uptime = $astinfo->get_uptime(); + if (empty($ast_uptime['system'])) { + $ast_uptime['system'] = time_string(0); + } + if (empty($ast_uptime['reload'])) { + $ast_uptime['reload'] = time_string(0); + } + $out .= ''; + $out .= ''; + + $out .= '
Distro:'.$sysinfo->distro().'
Kernel:'.$sysinfo->kernel().'
CPU:'.$cpu['model'].' '.$cpu['cpuspeed'].'
'._('System Uptime').':'.time_string($sysinfo->uptime()).'
'._('Asterisk Uptime').':'.$ast_uptime['system'].'
'._('Last Reload').':'.$ast_uptime['reload'].'
'; + return $out; +} + +function show_procinfo() { + global $procinfo; + global $astinfo; + global $amp_conf; + $out = ''; + + $out .= "

"._("Server Status")."

"; + // asterisk + if ($astver = $astinfo->check_asterisk()) { + $out .= draw_status_box(_("Asterisk"), "ok", sprintf(_('Asterisk is running: %s'),$astver)); + } else { + $out .= draw_status_box(_("Asterisk"), "error", _('Asterisk is not running, this is a critical service!')); + } + + // asterisk proxy (optionally) + if ($amp_conf['ASTMANAGERPROXYPORT']) { + if ($procinfo->check_port($amp_conf['ASTMANAGERPROXYPORT'])) { + $out .= draw_status_box(_("Manager Proxy"), "ok", _('Asterisk Manager Proxy is running')); + } else { + $out .= draw_status_box(_("Manager Proxy"), "warn", sprintf(_('Asterisk Manager Proxy is not running, %s will fall back to using Asterisk directly, which may result in poor performance'), DASHBOARD_FREEPBX_BRAND)); + } + } + + // fop + /* FOP has been removed (optional un-supported module currenlty) + $warn = draw_status_box(_("Op Panel"), "warn", _('FOP Operator Panel Server is not running, you will not be able to use the operator panel, but the system will run fine without it.')); + if($amp_conf['FOPDISABLE']) { // FOP is disabled, display that on the dashboard + $out .= draw_status_box(_("Op Panel"), "disabled", _('FOP Operator Panel is disabled in Advanced Settings')); + } else { + if (!$amp_conf['FOPRUN']) { + $out .= $warn; // if FOPRUN is false, display warning on the dashboard + } elseif ($procinfo->check_fop_server()) { // if FOPRUN is true, then check the fop tcp port, if OK display that on dashboard + $out .= draw_status_box(_("Op Panel"), "ok", _('FOP Operator Panel Server is running')); + } else { // check_fop_server returned an error, display warning + $out .= $warn; + } + } + */ + + // mysql + if ($amp_conf['AMPDBENGINE'] == "mysql") { + /* this is silly- it's always running, if the web interface loads + if ($procinfo->check_mysql($amp_conf['AMPDBHOST'])) { + $out .= draw_status_box(_("MySQL"), "ok", _('MySQL Server is running')); + } else { + $out .= draw_status_box(_("MySQL"), "error", _('MySQL Server is not running, this is a critical service for the web interface and call logs!')); + } + */ + $out .= draw_status_box(_("MySQL"), "ok", _('MySQL Server is running')); + } + + // web always runs .. HOWEVER, we can turn it off with dhtml + $out .= draw_status_box(_("Web Server"), "ok", _('Web Server is running')); + + // ssh + $ssh_port = (isset($amp_conf['SSHPORT']) && (ctype_digit($amp_conf['SSHPORT']) || is_int($amp_conf['SSHPORT'])) && ($amp_conf['SSHPORT'] > 0) && ($amp_conf['SSHPORT'] < 65536))?$amp_conf['SSHPORT']:22; + if ($procinfo->check_port($ssh_port)) { + $out .= draw_status_box(_("SSH Server"), "ok", _('SSH Server is running')); + } else { + $out .= draw_status_box(_("SSH Server"), "warn", _('SSH Server is not running, you will not be able to connect to the system console remotely')); + } + return $out; +} + +function show_syslog(&$md5_checksum) { + global $db; + $out = ''; + $checksum = ''; + + // notify_classes are also used as the image names + $notify_classes = array( + NOTIFICATION_TYPE_CRITICAL => 'notify_critical', + NOTIFICATION_TYPE_SECURITY => 'notify_security', + NOTIFICATION_TYPE_UPDATE => 'notify_update', + NOTIFICATION_TYPE_ERROR => 'notify_error', + NOTIFICATION_TYPE_WARNING => 'notify_warning', + NOTIFICATION_TYPE_NOTICE => 'notify_notice', + ); + $notify_descriptions = array( + NOTIFICATION_TYPE_CRITICAL => _('Critical Error'), + NOTIFICATION_TYPE_SECURITY => _('Security Update'), + NOTIFICATION_TYPE_UPDATE => _('Update'), + NOTIFICATION_TYPE_ERROR => _('Error'), + NOTIFICATION_TYPE_WARNING => _('Warning'), + NOTIFICATION_TYPE_NOTICE => _('Notice'), + ); + + $notify =& notifications::create($db); + + $showall = (isset($_SESSION['syslog_showall']) ? $_SESSION['syslog_showall'] : false); + + $items = $notify->list_all($showall); + + $out .= "

".sprintf(_("%s Notices"), DASHBOARD_FREEPBX_BRAND)."

"; + + if (count($items)) { + $out .= ''; + } else { + if ($showall) { + $out .= _('No notifications'); + } else { + $out .= _('No new notifications'); + } + } + + $md5_checksum = md5($checksum); + + $out .= '
'; + + if ($showall) { + $out .= ''._('show new').''; + } else { + $out .= ''._('show all').''; + } + $out .= '
'; + return $out; +} + +function do_syslog_ack() { + global $db; + $notify =& notifications::create($db); + + if (isset($_REQUEST['module']) && $_REQUEST['id']) { + $notify->reset($_REQUEST['module'], $_REQUEST['id']); + } +} +function do_syslog_delete() { + global $db; + $notify =& notifications::create($db); + + if (isset($_REQUEST['module']) && $_REQUEST['id']) { + var_dump($_REQUEST); + $notify->safe_delete($_REQUEST['module'], $_REQUEST['id']); + } +} + +/********************************************************************************************/ + + +define("IN_PHPSYSINFO", "1"); +define("APP_ROOT", dirname(__FILE__).'/phpsysinfo'); +include APP_ROOT."/common_functions.php"; +include APP_ROOT."/class.".PHP_OS.".inc.php"; +include dirname(__FILE__)."/class.astinfo.php"; +include dirname(__FILE__)."/class.average_rate_calculator.php"; +include dirname(__FILE__)."/class.procinfo.php"; +include dirname(__FILE__)."/class.error.inc.php"; + +$error = new Error; + + +$sysinfo = new sysinfo; +$astinfo = new astinfo($astman); +$procinfo = new procinfo; + + +if (!$quietmode) { + ?> + + + +

+
+ '; + + // regular page + echo '
'; + echo show_syslog($syslog_md5); + // syslog_md5 is used by javascript updateInfo() to determine if the syslog div contents have changed + echo ''; + //echo "log goes here


"; + echo '
'; + + echo '
'; + echo show_aststats(); + echo '
'; + + echo '
'; + echo show_sysinfo(); + echo '
'; + + + echo '
'; + + echo '
'; + echo show_sysstats(); + echo '
'; + + echo '
'; + echo show_procinfo(); + echo '
'; + + //echo '
'; + echo '
'; // #sysinfo-right, #dashboard +?> + + +
+ + '; + + if($dashboard_debug && $error->ErrorsExist()) { + $fh = fopen($amp_conf['ASTLOGDIR']."/dashboard-error.log","a"); + fwrite($fh, $error->ErrorsAsText()); + fclose($fh); + } + +} else { + // Handle AJAX updates + + switch ($info) { + case "sysstats": + echo show_sysstats(); + break; + case "aststats": + echo show_aststats(); + break; + case "procinfo": + echo show_procinfo(); + break; + case 'sysinfo': + echo show_sysinfo(); + break; + case 'syslog': + echo show_syslog($syslog_md5); + // syslog_md5 is used by javascript updateInfo() to determine if the syslog div contents have changed + echo ''; + break; + case 'syslog_ack': + do_syslog_ack(); + break; + case 'syslog_delete': + do_syslog_delete(); + break; + + case 'info': + header("Content-type: application/json"); + echo json_encode( + array( + 'procinfo'=>show_procinfo(), + 'sysinfo'=>show_sysinfo(), + 'syslog'=>show_syslog($syslog_md5), + 'syslog_md5'=>$syslog_md5, + ) + ); + break; + case 'stats': + header("Content-type: application/json"); + echo json_encode( + array( + 'sysstats'=>show_sysstats(), + 'aststats'=>show_aststats(), + ) + ); + break; + case 'all': + header("Content-type: application/json"); + echo json_encode( + array( + 'sysstats'=>show_sysstats(), + 'aststats'=>show_aststats(), + 'procinfo'=>show_procinfo(), + 'sysinfo'=>show_sysinfo(), + 'syslog'=>show_syslog(), + ) + ); + break; + } +} + +?> diff --git a/index.php b/index.php index bbbe446..9bc03d0 100644 --- a/index.php +++ b/index.php @@ -16,7 +16,7 @@ function() { var status = $("#status").val(); $('#status').load('status.php', {status:status}); -}, 1000); +}, 5000);