Rabu, 09 Desember 2020

Using Ubuntu Cloud Image 20.04 on CentOS 8 Cockpit KVM

To use ubuntu cloud image on CentOS 8 Cockpit KVM, we would need to download the daily image

There are many image format, but I only tested 2 of them, 

  • https://cloud-images.ubuntu.com/focal/current/focal-server-cloudimg-amd64-disk-kvm.img, this is not booting
  • https://cloud-images.ubuntu.com/focal/current/focal-server-cloudimg-amd64.img, this boots fine
$ wget https://cloud-images.ubuntu.com/focal/current/focal-server-cloudimg-amd64.img

After downloading we can verify that this is in qcow format by using

$ file focal-server-cloudimg-amd64.img 
focal-server-cloudimg-amd64.img: QEMU QCOW Image (v2), 2361393152 bytes
or
$ qemu-img info focal-server-cloudimg-amd64.img
image: focal-server-cloudimg-amd64.img
file format: qcow2
virtual size: 2.2G (2361393152 bytes)
disk size: 522M
cluster_size: 65536
Format specific information:
    compat: 0.10
    refcount bits: 16

Let's resize the image, since 2.2 GB looks a bit too small for me
$ qemu-img resize focal-server-cloudimg-amd64.img 15G

You can verify the result of resize by running qemu-img info again.

By default, this image expect cloud-init operation. We will try to bypass that.
First we will resize the partition table to use the extra space allocated from resize operation before, then we mount the image for further adjustment.

$ sudo modprobe nbd
$ sudo qemu-nbd -c /dev/nbd0 focal-server-cloudimg-amd64.img
$ sudo blkid /dev/nbd0p1
/dev/nbd0p1: LABEL="cloudimg-rootfs" UUID="9be2d018-238c-4f91-a3a2-9041df4c3f47" TYPE="ext4" PARTUUID="old-uuid"

we will delete partition 1 and recreate it
$ sudo fdisk /dev/nbd0
(fdisk) p
note the partition start and end
(fdisk) d
delete partition 1
(fdisk) n
create partition 1 again with the same start sector and updated end sector
(fdisk) w
there will be warning about disk signature, leave them as is, and exit fdisk

since we recreate partition 1, the PARTUUID will change and we need to adjust grub.cfg

$ sudo blkid /dev/nbd0p1
/dev/nbd0p1: LABEL="cloudimg-rootfs" UUID="9be2d018-238c-4f91-a3a2-9041df4c3f47" TYPE="ext4" PARTUUID="new-uuid"

note down this new uuid, we will need it later

$ ls /image || mkdir /image
$ sudo mount /dev/nbd0p1 /image/

Then we chroot to the new environment and change root password, so we can login to the VM later

$ sudo chroot /image/
# passwd
set your new password

The default grub.cfg also require serial port to be available, otherwise it will freeze while booting. See here for the discussion https://bugs.launchpad.net/cloud-images/+bug/1573095

We can disable serial port requirement by editing grub.cfg and removing all reference to console=ttyS0. Also edit file /etc/default/grub.d/50-cloudimg-settings.cfg

Do not forget to change all reference to PARTUUID from the old uuid to the new uuid. The following files need to be changed
/boot/grub/grub.cfg
/etc/default/grub.d/40-force-partuuid.cfg


Exit the chroot environment and umount everything

# exit
$ sudo umount /image/
$ sudo qemu-nbd -d /dev/nbd0

Import VM from Cockpit interface, we can use virtio as the disk interface without issue. If cockpit do not list Ubuntu 20.04 as OS choice, we can use the nearest Ubuntu. In my case I am using Ubuntu 19.10.

Test your VM by login as root in the console interface. 
To enable ssh, you have to generate hostkey and enable password authentication in sshd_config

# ssh-keygen -A

in sshd_config
PasswordAuthentication yes
# if needed
PermitRootLogin yes



Enjoy !!


Rabu, 22 Februari 2012

Thold Host UP Event History

The following patch will integrate sms capability (see this link) and host up event history to thold plugin (version 0.4.9) for cacti

diff -wur thold/includes/polling.php /var/www/html/cacti/plugins/thold/includes/polling.php
--- thold/includes/polling.php  2011-11-13 01:29:49.000000000 +0700
+++ /var/www/html/cacti/plugins/thold/includes/polling.php      2012-02-22 09:20:48.833702651 +0700
@@ -159,6 +159,8 @@
        }

        $alert_email = read_config_option('alert_email');
+       $save_sql = read_config_option('thold_save_sql');
+       $alert_sms = read_config_option('thold_send_sms');
        $ping_failure_count = read_config_option('ping_failure_count');

        // Lets find hosts that were down, but are now back up
@@ -291,6 +293,32 @@
                                        } elseif ($alert_email != '') {
                                                thold_mail($alert_email, '', $subject, $msg, '');
                                        }
+
+                                       //cacti_log('THOLD: before save_sql', true, 'POLLER');
+
+                                       if ($save_sql == 'on') {
+                                               $thold_sql = 'INSERT INTO thold_history (host_id, status, cur_time, avg_time, availability, total_polls, failed_polls, status_fail_date, downtime) VALUES (<HOSTID>, <DOWN/UP>, "<CUR_TIME>", "<AVG_TIME>", <AVAILABILITY>, <TOT_POLL>, <FAIL_POLL>, "<LAST_FAIL>", <DOWNTIME>)';
+
+                                               $thold_sql = str_replace('<HOSTID>', $fh['host_id'], $thold_sql);
+                                               $thold_sql = str_replace('<DOWN/UP>', HOST_UP, $thold_sql);
+                                               $thold_sql = str_replace('<CUR_TIME>', round(($host['cur_time']), 2), $thold_sql);
+                                               $thold_sql = str_replace('<AVG_TIME>', round(($host['avg_time']), 2), $thold_sql);
+                                               $thold_sql = str_replace('<AVAILABILITY>', round(($host['availability']), 2), $thold_sql);
+                                               $thold_sql = str_replace('<TOT_POLL>', $host['total_polls'], $thold_sql);
+                                               $thold_sql = str_replace('<FAIL_POLL>', $host['failed_polls'], $thold_sql);
+                                               $thold_sql = str_replace('<LAST_FAIL>', $host['status_fail_date'], $thold_sql);
+                                               $thold_sql = str_replace('<DOWNTIME>', $downtime, $thold_sql);
+
+                                               $thold_sql = sql_sanitize($thold_sql);
+                                               cacti_log('THOLD: thold_sql = ' . $thold_sql, true, 'POLLER');
+                                               db_execute($thold_sql, true);
+                                       }
+
+                                       //cacti_log('THOLD: after save_sql', true, 'POLLER');
+
+                                       if ($alert_sms == 'on' && $host['monitor'] == 'on') {
+                                               thold_sms('On ' . date('l, d-M-Y')  . ' => ' . $subject);
+                                       }
                                }
                        }
                }
@@ -380,6 +408,10 @@
                        } elseif ($alert_email != '') {
                                thold_mail($alert_email, '', $subject, $msg, '');
                        }
+
+                       if ($alert_sms == 'on' && $host['monitor'] == 'on') {
+                               thold_sms('On ' . date('l, d-M-Y')  . ' => ' . $subject);
+                       }
                }
        }

diff -wur thold/includes/settings.php /var/www/html/cacti/plugins/thold/includes/settings.php
--- thold/includes/settings.php 2011-11-13 01:29:49.000000000 +0700
+++ /var/www/html/cacti/plugins/thold/includes/settings.php     2012-02-21 11:49:22.875742130 +0700
@@ -336,6 +336,30 @@
                        'method' => 'checkbox',
                        'default' => 'off'
                        ),
+                'thold_send_sms' => array(
+                        'friendly_name' => 'Also Send Alerts as SMS',
+                        'description' => 'If checked, this will cause all Alerts to also be sent as SMS.',
+                        'method' => 'checkbox',
+                        'default' => 'off',
+                        ),
+                'thold_gammu_smsd_inject_path' => array(
+                        'friendly_name' => 'Path to gammu-smsd-inject',
+                        'description' => 'This is the path to gammu sms inject binary',
+                        'method' => 'textbox',
+                        'max_length' => 255,
+                        ),
+                'thold_sms_number' => array(
+                        'friendly_name' => 'SMS Contact',
+                        'description' => 'This is the SMS Number to contact. Separated by semicolon (;)',
+                        'method' => 'textbox',
+                        'max_length' => 255,
+                       ),
+               'thold_save_sql' => array(
+                       'friendly_name' => 'Save History to SQL Table',
+                       'description' => 'This will save host recovering event to thold SQL table',
+                       'method' => 'checkbox',
+                       'default' => 'off'
+                       ),
                'thold_baseline_header' => array(
                        'friendly_name' => 'Default Baseline Settings',
                        'method' => 'spacer',

diff -wur thold/thold_functions.php /var/www/html/cacti/plugins/thold/thold_functions.php
--- thold/thold_functions.php   2011-11-13 01:29:49.000000000 +0700
+++ /var/www/html/cacti/plugins/thold/thold_functions.php       2012-02-14 13:01:58.867790532 +0700
@@ -3105,3 +3105,26 @@

        return $xml;
 }
+
+function thold_sms($msg) {
+       global $debug;
+
+       $gammu_output = array();
+       $gammu_return = 999;
+       $gammu_smsd_inject_path = trim(read_config_option('thold_gammu_smsd_inject_path'));
+       $sms = trim(read_config_option('thold_sms_number'));
+
+       $sms_numbers = explode(';', $sms);
+       foreach($sms_numbers as $key => $value) {
+               $gammu_smsd_inject_command = $gammu_smsd_inject_path . ' EMS ' . trim($value) . ' -text "' . $msg . '"';
+               exec($gammu_smsd_inject_command, $gammu_output, $gammu_return);
+
+               if (read_config_option("log_verbosity") >= POLLER_VERBOSITY_DEBUG || $debug) {
+                       cacti_log('DEBUG: gammu-sms-inject called with command ==>' . $gammu_smsd_inject_command, true, 'POLLER');
+                       foreach ($gammu_output as $key => $value) {
+                               cacti_log('DEBUG: gammu-sms-inject output ==> ' . $key . ' = ' . $value, true, 'POLLER');
+                       }
+                       cacti_log('DEBUG: gammu-sms-inject return value ==> ' . $gammu_return, true, 'POLLER');
+               }
+       }
+}


diff -wur thold/thold_graph.php /var/www/html/cacti/plugins/thold/thold_graph.php
--- thold/thold_graph.php       2011-11-13 01:29:49.000000000 +0700
+++ /var/www/html/cacti/plugins/thold/thold_graph.php   2012-02-22 16:23:52.008693571 +0700
@@ -78,7 +78,8 @@
 $tabs_thold = array(
        "thold" => "Thresholds",
        "log" => "Log",
-       "hoststat" => "Host Status");
+       "hoststat" => "Host Status",
+       "history" => "History");

 /* set the default tab */
 if (isset($_REQUEST["tab"])) {
@@ -111,6 +112,8 @@
        tholds();
 }elseif ($current_tab == 'hoststat') {
        hosts();
+}elseif ($current_tab == 'history') {
+       thold_show_history();
 }else{
        thold_show_log();
 }
@@ -962,6 +965,300 @@
        <?php
 }

+function thold_show_history() {
+       global $config, $colors, $item_rows;
+
+       $thold_background = array(
+               '0'     => 'CCFFCC',
+               '1'     => 'CDCFC4');
+
+       /* ================= input validation ================= */
+       input_validate_input_number(get_request_var_request("host_id"));
+       input_validate_input_number(get_request_var_request("page"));
+       input_validate_input_number(get_request_var_request("rows"));
+       input_validate_input_number(get_request_var_request("month"));
+       /* ==================================================== */
+
+       /* clean up search string */
+       if (isset($_REQUEST["filter"])) {
+               $_REQUEST["filter"] = sanitize_search_string(get_request_var("filter"));
+       }
+
+       /* clean up sort_column */
+       if (isset($_REQUEST["sort_column"])) {
+               $_REQUEST["sort_column"] = sanitize_search_string(get_request_var("sort_column"));
+       }
+
+       /* clean up search string */
+       if (isset($_REQUEST["sort_direction"])) {
+               $_REQUEST["sort_direction"] = sanitize_search_string(get_request_var("sort_direction"));
+       }
+
+       /* clean up search string */
+       if (isset($_REQUEST["month"])) {
+               $_REQUEST["month"] = sanitize_search_string(get_request_var("month"));
+       }
+       /* if the user pushed the 'clear' button */
+       if (isset($_REQUEST["clear"])) {
+               kill_session_var("sess_thold_history_current_page");
+               kill_session_var("sess_thold_history_host_id");
+               kill_session_var("sess_thold_history_rows");
+               kill_session_var("sess_thold_history_sort_column");
+               kill_session_var("sess_thold_history_sort_direction");
+               kill_session_var("sess_thold_history_month");
+
+               unset($_REQUEST["page"]);
+               unset($_REQUEST["filter"]);
+               unset($_REQUEST["host_id"]);
+               unset($_REQUEST["rows"]);
+               unset($_REQUEST["sort_column"]);
+               unset($_REQUEST["sort_direction"]);
+               unset($_REQUEST["month"]);
+       } else {
+               /* if any of the settings changed, reset the page number */
+               $changed = 0;
+               $changed += thold_request_check_changed('filter', 'sess_thold_history_filter');
+               $changed += thold_request_check_changed('host_id', 'sess_thold_history_host_id');
+               $changed += thold_request_check_changed('rows', 'sess_thold_history_rows');
+               $changed += thold_request_check_changed('sort_column', 'sess_thold_history_sort_column');
+               $changed += thold_request_check_changed('sort_direction', 'sess_thold_history_sort_direction');
+               $changed += thold_request_check_changed('month', 'sess_thold_history_month');
+               if ($changed) {
+                       $_REQUEST['page'] = '1';
+               }
+       }
+
+       /* remember these search fields in session vars so we don't have to keep passing them around */
+       load_current_session_value("page", "sess_thold_history_current_page", "1");
+       load_current_session_value("host_id", "sess_thold_history_host_id", "-1");
+       load_current_session_value("rows", "sess_thold_history_rows", read_config_option("num_rows_device"));
+       load_current_session_value("sort_column", "sess_thold_history_sort_column", "host_id");
+       load_current_session_value("sort_direction", "sess_thold_history_sort_direction", "DESC");
+       load_current_session_value("month", "sess_thold_history_month", "0");
+
+       /* if the number of rows is -1, set it to the default */
+       if ($_REQUEST["rows"] == -1) {
+               $_REQUEST["rows"] = read_config_option("num_rows_device");
+       }
+
+       ?>
+       <script type="text/javascript">
+       <!--
+       function filterChange(objForm) {
+               strURL = '?tab=history';
+               strURL = strURL + '&host_id=' + objForm.host_id.value;
+               strURL = strURL + '&rows=' + objForm.rows.value;
+               strURL = strURL + '&month=' + objForm.month.value;
+               document.location = strURL;
+       }
+       -->
+       </script>
+       <?php
+
+       html_start_box("<strong>Host History</strong>", "100%", $colors["header"], "3", "center", "");
+       form_thold_history_filter();
+       html_end_box();
+
+       $sql_where = '';
+
+       if ($_REQUEST["host_id"] == "-1") {
+               /* Show all items */
+       }elseif ($_REQUEST["host_id"] == "0") {
+               $sql_where .= (strlen($sql_where) ? " AND":"WHERE") . " host.id IS NULL";
+       }elseif (!empty($_REQUEST["host_id"])) {
+               $sql_where .= (strlen($sql_where) ? " AND":"WHERE") . " thold_history.host_id=" . $_REQUEST["host_id"];
+       }
+
+       if ($_REQUEST['month'] == '0') {
+               //show all item
+       }elseif ($_REQUEST['month'] > 0 && $_REQUEST['month'] < 13) {
+               $sql_where .= (strlen($sql_where) ? " AND":"WHERE") . " MONTH(thold_history.status_fail_date) = " . $_REQUEST['month'];
+       }
+
+       html_start_box("", "100%", $colors["header"], "3", "center", "");
+
+       $sortby = $_REQUEST["sort_column"];
+
+       $total_rows = db_fetch_cell("SELECT COUNT(*) FROM thold_history " . $sql_where);
+
+       $sql_query = "SELECT thold_history.host_id, thold_history.status, thold_history.cur_time, thold_history.avg_time, thold_history.availability, thold_history.total_polls, thold_history.failed_polls, thold_history.status_fail_date, thold_history.downtime, host.description AS hdescription FROM thold_history LEFT JOIN host ON thold_history.host_id=host.id " . $sql_where . " ORDER BY " . $sortby . " " . $_REQUEST["sort_direction"] . " LIMIT " . ($_REQUEST["rows"]*($_REQUEST["page"]-1)) . "," . $_REQUEST["rows"];
+
+       //print $sql_query;
+
+       $logs = db_fetch_assoc($sql_query);
+
+       /* generate page list */
+       $url_page_select = get_page_list($_REQUEST["page"], MAX_DISPLAY_PAGES, $_REQUEST["rows"], $total_rows, "thold_graph.php?tab=history");
+
+       if ($total_rows) {
+               $nav = "<tr bgcolor='#" . $colors["header"] . "'>
+                               <td colspan='11'>
+                                       <table width='100%' cellspacing='0' cellpadding='0' border='0'>
+                                               <tr>
+                                                       <td align='left' class='textHeaderDark'>
+                                                               <strong>&lt;&lt; "; if ($_REQUEST["page"] > 1) { $nav .= "<a class='linkOverDark' href='" . htmlspecialchars("thold_graph.php?tab=history&page=" . ($_REQUEST["page"]-1)) . "'>"; } $nav .= "Previous"; if ($_REQUEST["page"] > 1) { $nav .= "</a>"; } $nav .= "</strong>
+                                                       </td>\n
+                                                       <td align='center' class='textHeaderDark'>
+                                                               Showing Rows " . (($_REQUEST["rows"]*($_REQUEST["page"]-1))+1) . " to " . ((($total_rows < read_config_option("num_rows_device")) || ($total_rows < ($_REQUEST["rows"]*$_REQUEST["page"]))) ? $total_rows : ($_REQUEST["rows"]*$_REQUEST["page"])) . " of $total_rows [$url_page_select]
+                                                       </td>\n
+                                                       <td align='right' class='textHeaderDark'>
+                                                               <strong>"; if (($_REQUEST["page"] * $_REQUEST["rows"]) < $total_rows) { $nav .= "<a class='linkOverDark' href='" . htmlspecialchars("thold_graph.php?tab=history&page=" . ($_REQUEST["page"]+1)) . "'>"; } $nav .= "Next"; if (($_REQUEST["page"] * $_REQUEST["rows"]) < $total_rows) { $nav .= "</a>"; } $nav .= " &gt;&gt;</strong>
+                                                       </td>\n
+                                               </tr>
+                                       </table>
+                               </td>
+                       </tr>\n";
+       }else{
+               $nav = "<tr bgcolor='#" . $colors["header"] . "'>
+                               <td colspan='11'>
+                                       <table width='100%' cellspacing='0' cellpadding='0' border='0'>
+                                               <tr>
+                                                       <td align='center' class='textHeaderDark'>
+                                                               No Rows Found
+                                                       </td>\n
+                                               </tr>
+                                       </table>
+                               </td>
+                       </tr>\n";
+       }
+
+       print $nav;
+
+       $display_text = array(
+               "hdescription" => array("<br>Host", "ASC"),
+               "status" => array("<br>STATUS", "ASC"),
+               "cur_time" => array("Current (ms)", "ASC"),
+               "avg_time" => array("Average (ms)", "ASC"),
+               "availability" => array("<br>Availability", "DESC"),
+               "total_polls" => array("<br>Total Polls", "DESC"),
+               "failed_polls" => array("<br>Failed Polls", "DESC"),
+               "status_fail_date" => array("<br>Failed Date", "DESC"),
+               "downtime" => array("<br>Down Time", "ASC"));
+
+       html_header_sort($display_text, $_REQUEST["sort_column"], $_REQUEST["sort_direction"]);
+
+       $i = 0;
+       $total_downtime = 0;
+       if (sizeof($logs)) {
+               foreach ($logs as $l) {
+                       $downtime = (int) $l["downtime"];
+                       $total_downtime += $downtime;
+                       $fail_date = strtotime($l["status_fail_date"], 0);
+                       $start_fail = date('n', $fail_date);
+                       $end_fail = date('n', $fail_date + $downtime);
+                       if ($end_fail == $start_fail) {
+                       ?>
+                               <tr style='background-color:#<?php print $thold_background[$i%2];?>'>
+                               <td nowrap style='white-space:nowrap;'><?php print $l["hdescription"];?></td>
+                               <td nowrap style='white-space:nowrap;'><?php print $l["status"];?></td>
+                               <td nowrap style='white-space:nowrap;'><?php print round(($l["cur_time"]), 2);?></td>
+                               <td nowrap style='white-space:nowrap;'><?php print round(($l["avg_time"]), 2);?></td>
+                               <td nowrap style='white-space:nowrap;'><?php print round(($l["availability"]), 2);?></td>
+                               <td nowrap style='white-space:nowrap;'><?php print $l["total_polls"];?></td>
+                               <td nowrap style='white-space:nowrap;'><?php print $l["failed_polls"];?></td>
+                               <td nowrap style='white-space:nowrap;'><?php print $l["status_fail_date"];?></td>
+                               <?php
+                                       $downtime = (int) $l["downtime"];
+                                       $downtime_days = floor($downtime/86400);
+                                       $downtime_hours = floor(($downtime - ($downtime_days * 86400))/3600);
+                                       $downtime_minutes = floor(($downtime - ($downtime_days * 86400) - ($downtime_hours * 3600))/60);
+                                       $downtime_seconds = $downtime - ($downtime_days * 86400) - ($downtime_hours * 3600) - ($downtime_minutes * 60);
+                                       if ($downtime_days > 0 ) {
+                                               $downtimemsg = $downtime_days . "d " . $downtime_hours . "h " . $downtime_minutes . "m " . $downtime_seconds . "s ";
+                                       } elseif ($downtime_hours > 0 ) {
+                                               $downtimemsg = $downtime_hours . "h " . $downtime_minutes . "m " . $downtime_seconds . "s";
+                                       } elseif ($downtime_minutes > 0 ) {
+                                               $downtimemsg = $downtime_minutes . "m " . $downtime_seconds . "s";
+                                       } else {
+                                               $downtimemsg = $downtime_seconds . "s ";
+                                       }//if $downtime_days > 0
+                                       $i++;
+
+                               ?>
+                               <td nowrap style='white-space:nowrap;'><?php print $downtimemsg;?></td>
+                       <?php
+                               form_end_row();
+                       } else {
+                               for($z=0; $z<2; $z++) {
+                       ?>
+                                       <tr style='background-color:#<?php print $thold_background[$i%2];?>'>
+                                       <td nowrap style='white-space:nowrap;'><?php print $l["hdescription"];?></td>
+                                       <td nowrap style='white-space:nowrap;'><?php print $l["status"];?></td>
+                                       <td nowrap style='white-space:nowrap;'><?php print round(($l["cur_time"]), 2);?></td>
+                                       <td nowrap style='white-space:nowrap;'><?php print round(($l["avg_time"]), 2);?></td>
+                                       <td nowrap style='white-space:nowrap;'><?php print round(($l["availability"]), 2);?></td>
+                                       <td nowrap style='white-space:nowrap;'><?php print $l["total_polls"];?></td>
+                                       <td nowrap style='white-space:nowrap;'><?php print $l["failed_polls"];?></td>
+                                       <?php
+                                       if($z == 0) {
+                                       ?>
+                                               <td nowrap style='white-space:nowrap;'><?php print $l["status_fail_date"];?></td>
+                                       <?php
+                                               $midnight = mktime(23, 59, 59, date('n', $fail_date), date('j', $fail_date), date('Y', $fail_date));
+                                               $downtime = $midnight - $fail_date;
+                                       } else {
+                                       ?>
+                                               <td nowrap style='white-space:nowrap;'><?php print date('Y-m-d H:i:s', mktime(0, 0, 0, date('n', $fail_date), date('j', $fail_date) + 1, date('Y', $fail_date)));?></td>
+                                       <?php
+                                               $downtime = ((int) $l['downtime']) - $downtime;
+                                       }
+                                               $downtime_days = floor($downtime/86400);
+                                               $downtime_hours = floor(($downtime - ($downtime_days * 86400))/3600);
+                                               $downtime_minutes = floor(($downtime - ($downtime_days * 86400) - ($downtime_hours * 3600))/60);
+                                               $downtime_seconds = $downtime - ($downtime_days * 86400) - ($downtime_hours * 3600) - ($downtime_minutes * 60);
+                                               if ($downtime_days > 0 ) {
+                                                       $downtimemsg = $downtime_days . "d " . $downtime_hours . "h " . $downtime_minutes . "m " . $downtime_seconds . "s ";
+                                               } elseif ($downtime_hours > 0 ) {
+                                                       $downtimemsg = $downtime_hours . "h " . $downtime_minutes . "m " . $downtime_seconds . "s";
+                                               } elseif ($downtime_minutes > 0 ) {
+                                                       $downtimemsg = $downtime_minutes . "m " . $downtime_seconds . "s";
+                                               } else {
+                                                       $downtimemsg = $downtime_seconds . "s ";
+                                               }//if
+
+                                       ?>
+                                       <td nowrap style='white-space:nowrap;'><?php print $downtimemsg;?></td>
+
+                       <?php
+                                       form_end_row();
+
+                               }//for
+                               $i++;
+                       }//if
+
+               }//foreach
+       ?>
+               <tr><td colspan='7'></td><td>Total:</td><td>
+       <?php
+               $downtime_days = floor($total_downtime/86400);
+               $downtime_hours = floor(($total_downtime - ($downtime_days * 86400))/3600);
+               $downtime_minutes = floor(($total_downtime - ($downtime_days * 86400) - ($downtime_hours * 3600))/60);
+               $downtime_seconds = $total_downtime - ($downtime_days * 86400) - ($downtime_hours * 3600) - ($downtime_minutes * 60);
+               if ($downtime_days > 0 ) {
+                       $total_downtimemsg = $downtime_days . "d " . $downtime_hours . "h " . $downtime_minutes . "m " . $downtime_seconds . "s ";
+               } elseif ($downtime_hours > 0 ) {
+                       $total_downtimemsg = $downtime_hours . "h " . $downtime_minutes . "m " . $downtime_seconds . "s";
+               } elseif ($downtime_minutes > 0 ) {
+                       $total_downtimemsg = $downtime_minutes . "m " . $downtime_seconds . "s";
+               } else {
+                       $total_downtimemsg = $downtime_seconds . "s ";
+               }//if
+               print $total_downtimemsg;
+       ?>
+               </td></tr>
+       <?php
+       }else{
+               print "<tr><td><em>No Host Up History Found</em></td></tr>";
+       }//if
+
+       /* put the nav bar on the bottom as well */
+       print $nav;
+
+       html_end_box(false);
+
+       //thold_display_rusage();
+}
+
 function thold_show_log() {
        global $config, $colors, $item_rows;

@@ -1328,3 +1625,84 @@
        </tr>
        <?php
 }
+
+function form_thold_history_filter() {
+       global $item_rows, $config, $colors;
+
+       ?>
+       <tr bgcolor='#<?php print $colors["panel"];?>'>
+               <td>
+               <form name='form_thold_history' action='thold_graph.php?tab=history'>
+                       <table cellpadding='0' cellspacing='0'>
+                               <tr>
+                                       <td width='50'>
+                                               &nbsp;Host:&nbsp;
+                                       </td>
+                                       <td width='1'>
+                                               <select name='host_id' onChange='filterChange(document.form_thold_history)'>
+                                                       <option value='-1'<?php if ($_REQUEST["host_id"] == "-1") {?> selected<?php }?>>All</option>
+                                                       <option value='0'<?php if ($_REQUEST["host_id"] == "0") {?> selected<?php }?>>None</option>
+                                                       <?php
+                                                       $ids = db_fetch_assoc("SELECT DISTINCT host.id, host.description " .
+                                                               "FROM host " .
+                                                               "INNER JOIN thold_history ON host.id=thold_history.host_id " .
+                                                               " ORDER by host.description");
+
+                                                       if (sizeof($ids)) {
+                                                               foreach ($ids as $id) {
+                                                                       print "<option value='" . $id["id"] . "'"; if ($_REQUEST["host_id"] == $id["id"]) { print " selected"; } print ">" . $id["description"] . "</option>\n";
+                                                               }
+                                                       }
+                                                       ?>
+                                               </select>
+                                       </td>
+                                       <td width='1'>
+                                               &nbsp;Rows:&nbsp;
+                                       </td>
+                                       <td width='1'>
+                                               <select name='rows' onChange='filterChange(document.form_thold_history)'>
+                                                       <option value='-1'<?php if ($_REQUEST["rows"] == "-1") {?> selected<?php }?>>Default</option>
+                                                       <?php
+                                                       if (sizeof($item_rows)) {
+                                                       foreach ($item_rows as $key => $value) {
+                                                               print "<option value='" . $key . "'"; if ($_REQUEST["rows"] == $key) { print " selected"; } print ">" . $value . "</option>\n";
+                                                       }
+                                                       }
+                                                       ?>
+                                               </select>
+                                       </td>
+                                       <td width='50'>
+                                               &nbsp;Month:&nbsp;
+                                       </td>
+                                       <td width='1'>
+                                               <select name='month' onChange='filterChange(document.form_thold_history)'>
+                                                       <option value='0'<?php if ($_REQUEST["month"] == "0") {?> selected<?php }?>>All</option>
+                                                       <option value='1'<?php if ($_REQUEST["month"] == "1") {?> selected<?php }?>>January</option>
+                                                       <option value='2'<?php if ($_REQUEST["month"] == "2") {?> selected<?php }?>>February</option>
+                                                       <option value='3'<?php if ($_REQUEST["month"] == "3") {?> selected<?php }?>>March</option>
+                                                       <option value='4'<?php if ($_REQUEST["month"] == "4") {?> selected<?php }?>>April</option>
+                                                       <option value='5'<?php if ($_REQUEST["month"] == "5") {?> selected<?php }?>>May</option>
+                                                       <option value='6'<?php if ($_REQUEST["month"] == "6") {?> selected<?php }?>>June</option>
+                                                       <option value='7'<?php if ($_REQUEST["month"] == "7") {?> selected<?php }?>>July</option>
+                                                       <option value='8'<?php if ($_REQUEST["month"] == "8") {?> selected<?php }?>>August</option>
+                                                       <option value='9'<?php if ($_REQUEST["month"] == "9") {?> selected<?php }?>>September</option>
+                                                       <option value='10'<?php if ($_REQUEST["month"] == "10") {?> selected<?php }?>>October</option>
+                                                       <option value='11'<?php if ($_REQUEST["month"] == "11") {?> selected<?php }?>>November</option>
+                                                       <option value='12'<?php if ($_REQUEST["month"] == "12") {?> selected<?php }?>>December</option>
+                                               </select>
+                                       </td>
+                                       <td width='1'>
+                                               <input type="submit" value="Go">
+                                       </td>
+                                       <td width='1'>
+                                               <input id="clear" name="clear" type="submit" value="Clear">
+                                       </td>
+                               </tr>
+                       </table>
+                       <input type='hidden' name='page' value='1'>
+                       <input type='hidden' name='tab' value='history'>
+               </form>
+               </td>
+       </tr>
+       <?php
+}



Create the necessary table in mysql
create table thold_history (id bigint auto_increment, host_id mediumint(8) unsigned not null, status tinyint(2) not null default 0, cur_time decimal(10,5) default 0, avg_time decimal(10,5) default 0, availability decimal(8,5) not null default 100, total_polls int(12) unsigned default 0, failed_polls int(12) unsigned default 0, status_fail_date timestamp not null, downtime bigint not null default 0, PRIMARY KEY (id));