From 52e06b501b63dcab296013ad1df33af573006e55 Mon Sep 17 00:00:00 2001 From: Kevin Carter Date: Wed, 1 Mar 2017 13:22:10 -0600 Subject: [PATCH] Added KVM Metric plugin (#2) This plugin will pull metrics and meta-data from a KVM host. Signed-off-by: Kevin Carter --- AUTHORS | 2 +- ChangeLog | 1 + monitorstack/plugins/kvm.py | 67 ++++++++++++++++++++++++++++++++++ tests/test_kvm.py | 71 +++++++++++++++++++++++++++++++++++++ 4 files changed, 140 insertions(+), 1 deletion(-) create mode 100644 monitorstack/plugins/kvm.py create mode 100644 tests/test_kvm.py diff --git a/AUTHORS b/AUTHORS index 7579f64..2c34875 100644 --- a/AUTHORS +++ b/AUTHORS @@ -1,2 +1,2 @@ -Kevin Carter +Kevin Carter Major Hayden diff --git a/ChangeLog b/ChangeLog index f93e999..94e909a 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,6 +1,7 @@ CHANGES ======= +* Added KVM Metric plugin * Couple of updates: telegraf line protocol, dynamic imports, metadata * Proof of concept * Initial commit diff --git a/monitorstack/plugins/kvm.py b/monitorstack/plugins/kvm.py new file mode 100644 index 0000000..92c5cb0 --- /dev/null +++ b/monitorstack/plugins/kvm.py @@ -0,0 +1,67 @@ +#!/usr/bin/env python +# Copyright 2017, Kevin Carter +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import platform +import socket + +import click + +from monitorstack.cli import pass_context + + +DOC = """Get metrics from a KVM hypervisor""" + + +@click.command('kvm', short_help=DOC.split('\n')[0]) +@pass_context +def cli(ctx): + setattr(cli, '__doc__', DOC) + + # Lower level import because we only want to load this module + # when this plugin is called. + try: + import libvirt + except ImportError: + raise SystemExit('The "kvm plugin requires libvirt-python to be' + ' installed".') + + output = { + 'measurement_name': 'kvm', + 'meta': { + 'platform': platform.platform(), + 'kvm_host_id': abs(hash(socket.getfqdn())) + } + } + conn = libvirt.openReadOnly() + try: + variables = output['variables'] = dict() + domains = conn.listDomainsID() + variables['kvm_vms'] = len(domains) + variables['kvm_total_vcpus'] = conn.getCPUMap()[0] + variables['kvm_scheduled_vcpus'] = 0 + for domain in domains: + variables['kvm_scheduled_vcpus'] += conn.lookupByID( + domain + ).maxVcpus() + + except Exception as exp: + output['exit_code'] = 1 + output['message'] = 'kvm failed -- Error: {}'.format(exp) + else: + output['exit_code'] = 0 + output['message'] = 'kvm is ok' + finally: + conn.close() + return output diff --git a/tests/test_kvm.py b/tests/test_kvm.py new file mode 100644 index 0000000..3bfab83 --- /dev/null +++ b/tests/test_kvm.py @@ -0,0 +1,71 @@ +# Copyright 2017, Kevin Carter +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Tests for the base class.""" + +import json +import sys + +from click.testing import CliRunner + +from monitorstack.cli import cli + + +class LibvirtStub(object): + """Stubbed libvirt class.""" + + class openReadOnly(object): # noqa + """Stubbed openReadOnly class.""" + + def close(self, *args, **kwargs): # noqa + pass + + def listDomainsID(self, *args, **kwargs): # noqa + return ['a', 'b', 'c'] + + def getCPUMap(self, *args, **kwargs): # noqa + return [1, 1, 1] + + class lookupByID(object): # noqa + """Stubbed lookupByID class.""" + def __init__(self, *args, **kwargs): + pass + + def maxVcpus(self): # noqa + return 2 + + +class TestKvm(object): + """Tests for the uptime monitor class.""" + + def test_run(self): + """Ensure the run() method works.""" + + sys.modules['libvirt'] = LibvirtStub + + runner = CliRunner() + result = runner.invoke(cli, ['-f', 'json', 'kvm']) + result_json = json.loads(result.output) + + variables = result_json['variables'] + meta = result_json['meta'] + assert 'kvm_vms' in variables + assert variables['kvm_vms'] == 3 + assert 'kvm_total_vcpus' in variables + assert variables['kvm_total_vcpus'] == 1 + assert 'kvm_scheduled_vcpus' in variables + assert variables['kvm_scheduled_vcpus'] == 6 + assert 'platform' in meta + assert 'kvm_host_id' in meta + assert result.exit_code == 0