Merge pull request #1789 from tuxis-ie/add-proxmox-app

Add proxmox monitoring.
This commit is contained in:
Daniel Preussker 2015-09-01 11:53:35 +00:00
commit b9bf61bbf6
12 changed files with 402 additions and 7 deletions

23
doc/Extensions/Proxmox.md Normal file
View File

@ -0,0 +1,23 @@
# Proxmox graphing
It is possible to create graphs of the Proxmox VMs that run on your monitored machines. Currently, only trafficgraphs are created. One for each interface on each VM. Possibly, IO grahps will be added later on.
The ultimate goal is to be able to create traffic bills for VMs, no matter on which physical machine that VM runs.
### Enabling Proxmox graphs
To enable Proxmox graphs, do the following:
In config.php, enable Proxmox:
```php
$config['enable_proxmox'] = 1
```
Then, install librenms-agent on the machines running Proxmox, and enable the Proxmox-plugin using:
```bash
mk_enplug proxmox
```
Then, enable the unix-agent on the machines running Proxmox.
You should now see an application in LibreNMS, as well as a new menu-item in the topmenu, allowing you to choose which cluster you want to look at.

View File

@ -0,0 +1,50 @@
<?php
/*
* Copyright (C) 2015 Mark Schouten <mark@tuxis.nl>
*
* 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; version 2 dated June,
* 1991.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* See http://www.gnu.org/licenses/gpl.txt for the full license
*/
/**
* Fetch all VM's in a Proxmox Cluster
* @param string $c Clustername
* @return array An array with all the VM's on this cluster
*/
function proxmox_cluster_vms($c) {
return dbFetchRows("SELECT * FROM proxmox WHERE cluster = ? ORDER BY vmid", array($c));
}
/**
* Fetch all VM's on a Proxmox node
* @param integer $n device_id
* @return array An array with all the VM's on this node
*/
function proxmox_node_vms($n) {
return dbFetchRows("SELECT * FROM proxmox WHERE device_id = ? ORDER BY vmid", array($n));
}
/**
* Fetch all info about a Proxmox VM
* @param integer $vmid Proxmox VM ID
* @param string $c Clustername
* @return array An array with all info of this VM on this cluster, including ports
*/
function proxmox_vm_info($vmid, $c) {
$vm = dbFetchRow("SELECT pm.*, d.hostname AS host, d.device_id FROM proxmox pm, devices d WHERE pm.device_id = d.device_id AND pm.vmid = ? AND pm.cluster = ?", array($vmid, $c));
$appid = dbFetchRow("SELECT app_id FROM applications WHERE device_id = ? AND app_type = ?", array($vm['device_id'], 'proxmox'));
$vm['ports'] = dbFetchRows("SELECT * FROM proxmox_ports WHERE vm_id = ?", array($vm['id']));
$vm['app_id'] = $appid['app_id'];
return $vm;
}

View File

@ -12,6 +12,35 @@
* @copyright (C) 2013 LibreNMS Group
*/
/**
* Compare $t with the value of $vars[$v], if that exists
* @param string $v Name of the var to test
* @param string $t Value to compare $vars[$v] to
* @return boolean true, if values are the same, false if $vars[$v] is unset or values differ
*/
function var_eq($v, $t) {
global $vars;
if (isset($vars[$v]) && $vars[$v] == $t) {
return true;
}
return false;
}
/**
* Get the value of $vars[$v], if it exists
* @param string $v Name of the var to get
* @return string|boolean The value of $vars[$v] if it exists, false if it does not exist
*/
function var_get($v) {
global $vars;
if (isset($vars[$v])) {
return $vars[$v];
}
return false;
}
function data_uri($file, $mime) {
$contents = file_get_contents($file);

View File

@ -3,7 +3,12 @@
if (is_numeric($vars['id']) && ($auth || application_permitted($vars['id']))) {
$app = get_application_by_id($vars['id']);
$device = device_by_id_cache($app['device_id']);
$title = generate_device_link($device);
$title .= $graph_subtype;
if ($app['app_type'] != 'proxmox') {
$title = generate_device_link($device);
$title .= $graph_subtype;
}
else {
$title = $vars['port'].'@'.$vars['hostname'].' on '.generate_device_link($device);
}
$auth = true;
}

View File

@ -0,0 +1,30 @@
<?php
/*
* Copyright (C) 2015 Mark Schouten <mark@tuxis.nl>
*
* 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; version 2 dated June,
* 1991.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* See http://www.gnu.org/licenses/gpl.txt for the full license
*/
require 'includes/graphs/common.inc.php';
$proxmox_rrd = $config['rrd_dir'].'/proxmox/'.$vars['cluster'].'/'.$vars['vmid'].'_netif_'.$vars['port'].'.rrd';
if (is_file($proxmox_rrd)) {
$rrd_filename = $proxmox_rrd;
}
$ds_in = 'INOCTETS';
$ds_out = 'OUTOCTETS';
require 'includes/graphs/generic_data.inc.php';

View File

@ -360,21 +360,32 @@ foreach (array_keys($menu_sensors) as $item) {
</li>
<?php
$app_count = dbFetchCell("SELECT COUNT(`app_id`) FROM `applications`");
$app_list = dbFetchRows("SELECT DISTINCT(`app_type`) AS `app_type` FROM `applications` ORDER BY `app_type`");
if ($_SESSION['userlevel'] >= '5' && ($app_count) > "0") {
if ($_SESSION['userlevel'] >= '5' && count($app_list) > "0") {
?>
<li class="dropdown">
<a href="apps/" class="dropdown-toggle" data-hover="dropdown" data-toggle="dropdown"><i class="fa fa-tasks fa-fw fa-lg fa-nav-icons"></i> Apps</a>
<ul class="dropdown-menu">
<?php
$app_list = dbFetchRows("SELECT `app_type` FROM `applications` GROUP BY `app_type` ORDER BY `app_type`");
foreach ($app_list as $app) {
if (isset($app['app_type'])) {
$app_i_list = dbFetchRows("SELECT DISTINCT(`app_instance`) FROM `applications` WHERE `app_type` = ? ORDER BY `app_instance`", array($app['app_type']));
$image = $config['html_dir']."/images/icons/".$app['app_type'].".png";
$icon = (file_exists($image) ? $app['app_type'] : "apps");
echo('<li><a href="apps/app='.$app['app_type'].'/"><i class="fa fa-angle-double-right fa-fw fa-lg"></i> '.nicecase($app['app_type']).' </a></li>');
if (count($app_i_list) > 1) {
echo '<li class="dropdown-submenu">';
echo '<a href="apps/app='.$app['app_type'].'/"><i class="fa fa-server fa-fw fa-lg"></i> '.nicecase($app['app_type']).' </a>';
echo '<ul class="dropdown-menu scrollable-menu">';
foreach ($app_i_list as $instance) {
echo ' <li><a href="apps/app='.$app['app_type'].'/instance='.$instance['app_instance'].'/"><i class="fa fa-angle-double-right fa-fw fa-lg"></i> ' . nicecase($instance['app_instance']) . '</a></li>';
}
echo '</ul></li>';
}
else {
echo('<li><a href="apps/app='.$app['app_type'].'/"><i class="fa fa-angle-double-right fa-fw fa-lg"></i> '.nicecase($app['app_type']).' </a></li>');
}
}
}
?>

View File

@ -0,0 +1,60 @@
<?php
require_once('includes/application/proxmox.inc.php');
$graphs['proxmox'] = array(
'netif'
);
$pmxcl = dbFetchRows("SELECT DISTINCT(`app_instance`) FROM `applications` WHERE `app_type` = ?", array('proxmox'));
print_optionbar_start();
echo "<span style='font-weight: bold;'>Proxmox</span> &#187; ";
unset($sep);
foreach ($pmxcl as $pmxc) {
if (isset($sep)) {
echo $sep;
};
if (var_eq('instance', $pmxc['app_instance'])) {
echo "<span class='pagemenu-selected'>";
}
echo generate_link(nicecase($pmxc['app_instance']), array('page' => 'apps', 'app' => 'proxmox', 'instance' => $pmxc['app_instance']));
if (var_eq('instance', $pmxc['app_instance'])) {
echo '</span>';
}
$sep = ' | ';
}
print_optionbar_end();
echo '<div class="container"><div class="row"><div class="col-md-12">';
if (!isset($vars['instance'])) {
echo 'Select a cluster:';
echo '<ul>';
foreach ($pmxcl as $pmxc) {
echo '<li>'.generate_link(nicecase($pmxc['app_instance']), array('page' => 'apps', 'app' => 'proxmox', 'instance' => $pmxc['app_instance'])).'</li>';
}
echo '</ul>';
}
elseif (!isset($vars['vmid'])) {
echo '<ul>';
foreach (proxmox_cluster_vms(var_get('instance')) as $pmxvm) {
echo '<li>'.generate_link($pmxvm['vmid']." (".$pmxvm['description'].")", array('page' => 'apps', 'app' => 'proxmox', 'instance' => var_get('instance'), 'vmid' => $pmxvm['vmid'])).'</li>';
}
echo '</ul>';
}
else {
include("pages/apps/proxmox/vm.inc.php");
}
echo '</div></div></div>';
$pagetitle[] = 'Proxmox';

View File

@ -0,0 +1,32 @@
<?php
$vm = proxmox_vm_info(var_get('vmid'), var_get('instance'));
$graphs = array(
'proxmox_traffic' => 'Traffic',
);
foreach ($vm['ports'] as $port) {
foreach ($graphs as $key => $text) {
$graph_type = 'proxmox_traffic';
$graph_array['height'] = '100';
$graph_array['width'] = '215';
$graph_array['to'] = $config['time']['now'];
$graph_array['id'] = $vm['app_id'];
$graph_array['device_id'] = $vm['device_id'];
$graph_array['type'] = 'application_'.$key;
$graph_array['port'] = $port['port'];
$graph_array['vmid'] = $vm['vmid'];
$graph_array['cluster'] = $vm['cluster'];
$graph_array['hostname'] = $vm['description'];
echo '<h3>'.$text.' '.$port['port'].'@'.$vm['description'].'</h3>';
echo "<tr bgcolor='$row_colour'><td colspan=5>";
include 'includes/print-graphrow.inc.php';
echo '</td></tr>';
}
}

View File

@ -0,0 +1,58 @@
<?php
/*
* Copyright (C) 2015 Mark Schouten <mark@tuxis.nl>
*
* 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; version 2 dated June,
* 1991.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* See http://www.gnu.org/licenses/gpl.txt for the full license
*/
include('includes/application/proxmox.inc.php');
if (!isset($config['enable_proxmox']) || !$config['enable_proxmox']) {
print_error('Proxmox agent was discovered on this host. Please enable Proxmox in your config.');
}
else {
$graphs = array(
'proxmox_traffic' => 'Traffic',
);
foreach (proxmox_node_vms(var_get('device')) as $nvm) {
$vm = proxmox_vm_info($nvm['vmid'], $nvm['cluster']);
foreach ($vm['ports'] as $port) {
foreach ($graphs as $key => $text) {
$graph_type = 'proxmox_traffic';
$graph_array['height'] = '100';
$graph_array['width'] = '215';
$graph_array['to'] = $config['time']['now'];
$graph_array['id'] = $vm['app_id'];
$graph_array['device_id'] = $vm['device_id'];
$graph_array['type'] = 'application_'.$key;
$graph_array['port'] = $port['port'];
$graph_array['vmid'] = $vm['vmid'];
$graph_array['cluster'] = $vm['cluster'];
$graph_array['hostname'] = $vm['description'];
echo '<h3>'.$text.' '.$port['port'].'@'.$vm['description'].'</h3>';
echo "<tr bgcolor='$row_colour'><td colspan=5>";
include 'includes/print-graphrow.inc.php';
echo '</td></tr>';
}
}
}
}

View File

@ -0,0 +1,93 @@
<?php
if (isset($config['enable_proxmox']) && $config['enable_proxmox'] && !empty($agent_data['app']['proxmox'])) {
$proxmox = $agent_data['app']['proxmox'];
}
/**
* Check if a port on a Proxmox VM exists
* @param string $p Port name
* @param string $c Clustername
* @param integer $i VM ID
* @return integer|boolean The port-ID if the port exists, false if it doesn't exist
*/
function proxmox_port_exists($i, $c, $p) {
if ($row = dbFetchRow("SELECT pmp.id FROM proxmox_ports pmp, proxmox pm WHERE pm.id = pmp.vm_id AND pmp.port = ? AND pm.cluster = ? AND pm.vmid = ?", array($p, $c, $i))) {
return $row['id'];
}
return false;
}
/**
* Check if a Proxmox VM exists
* @param integer $i VM ID
* @param string $c Clustername
* @param array $pmxcache Reference to the Proxmox VM Cache
* @return boolean true if the VM exists, false if it doesn't
*/
function proxmox_vm_exists($i, $c, &$pmxcache) {
if (isset($pmxcache[$c][$i]) && $pmxcache[$c][$i] > 0) {
return true;
}
if ($row = dbFetchRow("SELECT id FROM proxmox WHERE vmid = ? AND cluster = ?", array($i, $c))) {
$pmxcache[$c][$i] = (integer) $row['id'];
return true;
}
return false;
}
$pmxlines = explode("\n", $proxmox);
if (count($pmxlines) > 2) {
$pmxcluster = array_shift($pmxlines);
$pmxcdir = join('/', array($config['rrd_dir'],'proxmox',$pmxcluster));
if (!is_dir($pmxcdir)) {
mkdir($pmxcdir, 0775, true);
}
dbUpdate(array('device_id' => $device['device_id'], 'app_type' => 'proxmox', 'app_instance' => $pmxcluster), 'applications', '`device_id` = ? AND `app_type` = ?', array($device['device_id'], 'proxmox'));
$pmxcache = [];
foreach ($pmxlines as $vm) {
list($vmid, $vmport, $vmpin, $vmpout, $vmdesc) = explode('/', $vm, 5);
$rrd_filename = join('/', array(
$pmxcdir,
$vmid.'_netif_'.$vmport.'.rrd'));
if (!is_file($rrd_filename)) {
rrdtool_create(
$rrd_filename,
' --step 300 \
DS:INOCTETS:DERIVE:600:0:12500000000 \
DS:OUTOCTETS:DERIVE:600:0:12500000000 '.$config['rrd_rra']);
}
rrdtool_update($rrd_filename, 'N:'.$vmpin.':'.$vmpout);
if (proxmox_vm_exists($vmid, $pmxcluster, $pmxcache) === true) {
dbUpdate(array('device_id' => $device['device_id'], 'last_seen' => array('NOW()'), 'description' => $vmdesc), 'proxmox', '`vmid` = ? AND `cluster` = ?', array($vmid, $pmxcluster));
}
else {
$pmxcache[$pmxcluster][$vmid] = dbInsert(array('cluster' => $pmxcluster, 'vmid' => $vmid, 'description' => $vmdesc, 'device_id' => $device['device_id']), 'proxmox');
}
if ($portid = proxmox_port_exists($vmid, $pmxcluster, $vmport) !== false) {
dbUpdate(array('last_seen' => array('NOW()')), 'proxmox_ports', '`vm_id` = ? AND `port` = ?', array($pmxcache[$pmxcluster][$vmid], $vmport));
}
else {
dbInsert(array('vm_id' => $pmxcache[$pmxcluster][$vmid], 'port' => $vmport), 'proxmox_ports');
}
}
unset($pmxlines);
unset($pmxcluster);
unset($pmxcdir);
unset($proxmox);
unset($pmxcache);
}

View File

@ -50,6 +50,7 @@ if ($device['os_group'] == 'unix') {
"mysql",
"nginx",
"bind",
"proxmox",
"tinydns");
if (in_array($section, $agentapps)) {
@ -99,7 +100,7 @@ if ($device['os_group'] == 'unix') {
if (file_exists("includes/polling/applications/$key.inc.php")) {
d_echo("Enabling $key for ".$device['hostname']." if not yet enabled\n");
if (in_array($key, array('apache', 'mysql', 'nginx'))) {
if (in_array($key, array('apache', 'mysql', 'nginx', 'proxmox'))) {
if (dbFetchCell('SELECT COUNT(*) FROM `applications` WHERE `device_id` = ? AND `app_type` = ?', array($device['device_id'], $key)) == '0') {
echo "Found new application '$key'\n";
dbInsert(array('device_id' => $device['device_id'], 'app_type' => $key), 'applications');

3
sql-schema/067.sql Normal file
View File

@ -0,0 +1,3 @@
CREATE TABLE `proxmox` ( `id` int(11) NOT NULL AUTO_INCREMENT, `device_id` int(11) NOT NULL DEFAULT '0', `vmid` int(11) NOT NULL, `cluster` varchar(255) NOT NULL, `description` varchar(255) DEFAULT NULL, `last_seen` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (`id`), UNIQUE KEY `cluster_vm` (`cluster`,`vmid`)) ENGINE=InnoDB DEFAULT CHARSET=utf8;
CREATE TABLE `proxmox_ports` ( `id` int(11) NOT NULL AUTO_INCREMENT, `vm_id` int(11) NOT NULL, `port` varchar(10) NOT NULL, `last_seen` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (`id`), UNIQUE KEY `vm_port` (`vm_id`,`port`)) ENGINE=InnoDB DEFAULT CHARSET=utf8;
ALTER TABLE `applications` ADD COLUMN `app_instance` varchar(255) NOT NULL;