Improve test coverage (#4)
This commit is contained in:
@@ -1,6 +1,9 @@
|
|||||||
CHANGES
|
CHANGES
|
||||||
=======
|
=======
|
||||||
|
|
||||||
|
* Improve test coverage
|
||||||
|
* Remove sys from uptime test (#3)
|
||||||
|
* Increase test coverage for uptime
|
||||||
* Added KVM Metric plugin (#2)
|
* Added KVM Metric plugin (#2)
|
||||||
* Couple of updates: telegraf line protocol, dynamic imports, metadata (#1)
|
* Couple of updates: telegraf line protocol, dynamic imports, metadata (#1)
|
||||||
* Proof of concept
|
* Proof of concept
|
||||||
|
|||||||
@@ -14,11 +14,9 @@
|
|||||||
# limitations under the License.
|
# limitations under the License.
|
||||||
"""Handle all shell commands/arguments/options."""
|
"""Handle all shell commands/arguments/options."""
|
||||||
import importlib
|
import importlib
|
||||||
import json
|
|
||||||
import os
|
import os
|
||||||
import pkgutil
|
import pkgutil
|
||||||
import sys
|
import sys
|
||||||
import time
|
|
||||||
|
|
||||||
import click
|
import click
|
||||||
|
|
||||||
@@ -26,12 +24,6 @@ import click
|
|||||||
context_settings = dict(auto_envvar_prefix='MonitorStack')
|
context_settings = dict(auto_envvar_prefix='MonitorStack')
|
||||||
|
|
||||||
|
|
||||||
def current_time():
|
|
||||||
"""Return the current time in nanoseconds"""
|
|
||||||
|
|
||||||
return int(time.time() * 1000000000)
|
|
||||||
|
|
||||||
|
|
||||||
class Context(object):
|
class Context(object):
|
||||||
"""Set up a context object that we can pass."""
|
"""Set up a context object that we can pass."""
|
||||||
|
|
||||||
@@ -42,8 +34,6 @@ class Context(object):
|
|||||||
|
|
||||||
def log(self, msg, *args):
|
def log(self, msg, *args):
|
||||||
"""Log a message to stderr."""
|
"""Log a message to stderr."""
|
||||||
if args:
|
|
||||||
msg %= args
|
|
||||||
click.echo(msg, file=sys.stderr)
|
click.echo(msg, file=sys.stderr)
|
||||||
|
|
||||||
def vlog(self, msg, *args):
|
def vlog(self, msg, *args):
|
||||||
@@ -60,6 +50,7 @@ class MonitorStackCLI(click.MultiCommand):
|
|||||||
|
|
||||||
@property
|
@property
|
||||||
def cmd_folder(self):
|
def cmd_folder(self):
|
||||||
|
"""Get the path to the plugin directory."""
|
||||||
return os.path.abspath(
|
return os.path.abspath(
|
||||||
os.path.join(
|
os.path.join(
|
||||||
os.path.dirname(__file__),
|
os.path.dirname(__file__),
|
||||||
@@ -115,40 +106,13 @@ def cli(ctx, output_format, verbose):
|
|||||||
@cli.resultcallback(replace=True)
|
@cli.resultcallback(replace=True)
|
||||||
def process_result(result, output_format, verbose):
|
def process_result(result, output_format, verbose):
|
||||||
"""Render the output into the proper format."""
|
"""Render the output into the proper format."""
|
||||||
if output_format == 'json':
|
module_name = 'monitorstack.common.formatters'
|
||||||
click.echo(json.dumps(result, indent=2))
|
method_name = 'write_{}'.format(output_format)
|
||||||
|
output_formatter = getattr(
|
||||||
elif output_format == 'line':
|
importlib.import_module(module_name),
|
||||||
for key, value in result['variables'].items():
|
method_name
|
||||||
click.echo("{} {}".format(key, value))
|
)
|
||||||
|
output_formatter(result)
|
||||||
elif output_format == 'telegraf':
|
|
||||||
def line_format(sets, quote=False):
|
|
||||||
store = list()
|
|
||||||
for k, v in sets.items():
|
|
||||||
k = k.replace(' ', '_')
|
|
||||||
for v_type in [int, float]:
|
|
||||||
try:
|
|
||||||
v = v_type(v)
|
|
||||||
except ValueError:
|
|
||||||
pass # v was not a int, float, or long
|
|
||||||
else:
|
|
||||||
break
|
|
||||||
if not isinstance(v, (int, float, bool)) and quote:
|
|
||||||
store.append('{}="{}"'.format(k, v))
|
|
||||||
else:
|
|
||||||
store.append('{}={}'.format(k, v))
|
|
||||||
return ','.join(store).rstrip(',')
|
|
||||||
|
|
||||||
resultant = [result['measurement_name']]
|
|
||||||
if 'meta' in result:
|
|
||||||
resultant.append(line_format(sets=result['meta']))
|
|
||||||
resultant.append(line_format(sets=result['variables'], quote=True))
|
|
||||||
resultant.append(current_time())
|
|
||||||
click.echo(' '.join(resultant))
|
|
||||||
|
|
||||||
elif output_format == 'csv':
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
|
|||||||
0
monitorstack/common/__init__.py
Normal file
0
monitorstack/common/__init__.py
Normal file
67
monitorstack/common/formatters.py
Normal file
67
monitorstack/common/formatters.py
Normal file
@@ -0,0 +1,67 @@
|
|||||||
|
# Copyright 2017, Major Hayden <major@mhtx.net>
|
||||||
|
#
|
||||||
|
# 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.
|
||||||
|
"""Output methods."""
|
||||||
|
import json
|
||||||
|
import time
|
||||||
|
|
||||||
|
import click
|
||||||
|
|
||||||
|
|
||||||
|
def current_time():
|
||||||
|
"""Return the current time in nanoseconds."""
|
||||||
|
return int(time.time() * 1000000000)
|
||||||
|
|
||||||
|
|
||||||
|
def write_json(result):
|
||||||
|
"""Output in raw JSON format."""
|
||||||
|
output = json.dumps(result, indent=2)
|
||||||
|
click.echo(output)
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
def write_line(result):
|
||||||
|
"""Output in line format."""
|
||||||
|
for key, value in result['variables'].items():
|
||||||
|
click.echo("{} {}".format(key, value))
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
def write_telegraf(result):
|
||||||
|
"""Output in telegraf format."""
|
||||||
|
def line_format(sets, quote=False):
|
||||||
|
store = list()
|
||||||
|
for k, v in sets.items():
|
||||||
|
k = k.replace(' ', '_')
|
||||||
|
for v_type in [int, float]:
|
||||||
|
try:
|
||||||
|
v = v_type(v)
|
||||||
|
except ValueError:
|
||||||
|
pass # v was not a int, float, or long
|
||||||
|
else:
|
||||||
|
break
|
||||||
|
if not isinstance(v, (int, float, bool)) and quote:
|
||||||
|
store.append('{}="{}"'.format(k, v))
|
||||||
|
else:
|
||||||
|
store.append('{}={}'.format(k, v))
|
||||||
|
return ','.join(store).rstrip(',')
|
||||||
|
|
||||||
|
resultant = [result['measurement_name']]
|
||||||
|
if 'meta' in result:
|
||||||
|
resultant.append(line_format(sets=result['meta']))
|
||||||
|
resultant.append(line_format(sets=result['variables'], quote=True))
|
||||||
|
resultant.append(str(current_time()))
|
||||||
|
click.echo(' '.join(resultant))
|
||||||
|
|
||||||
|
return True
|
||||||
@@ -1,4 +1,3 @@
|
|||||||
#!/usr/bin/env python
|
|
||||||
# Copyright 2017, Kevin Carter <kevin@cloudnull.com>
|
# Copyright 2017, Kevin Carter <kevin@cloudnull.com>
|
||||||
#
|
#
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
@@ -12,6 +11,7 @@
|
|||||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
# See the License for the specific language governing permissions and
|
# See the License for the specific language governing permissions and
|
||||||
# limitations under the License.
|
# limitations under the License.
|
||||||
|
"""Plugin for KVM metrics."""
|
||||||
|
|
||||||
import platform
|
import platform
|
||||||
import socket
|
import socket
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
#!/usr/bin/env python
|
|
||||||
# Copyright 2017, Major Hayden <major@mhtx.net>
|
# Copyright 2017, Major Hayden <major@mhtx.net>
|
||||||
#
|
#
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
@@ -12,8 +11,7 @@
|
|||||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
# See the License for the specific language governing permissions and
|
# See the License for the specific language governing permissions and
|
||||||
# limitations under the License.
|
# limitations under the License.
|
||||||
|
"""Get system uptime."""
|
||||||
"""Base monitoring class."""
|
|
||||||
|
|
||||||
import platform
|
import platform
|
||||||
|
|
||||||
|
|||||||
85
tests/test_cli.py
Normal file
85
tests/test_cli.py
Normal file
@@ -0,0 +1,85 @@
|
|||||||
|
# Copyright 2017, Major Hayden <major@mhtx.net>
|
||||||
|
#
|
||||||
|
# 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 cli module."""
|
||||||
|
import click
|
||||||
|
|
||||||
|
from monitorstack.cli import Context
|
||||||
|
from monitorstack.cli import MonitorStackCLI
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
|
||||||
|
class TestCLI(object):
|
||||||
|
"""Tests for the base cli module."""
|
||||||
|
|
||||||
|
def test_context_log(self, monkeypatch):
|
||||||
|
"""Test log() method of Context class."""
|
||||||
|
def echofixer(msg, file):
|
||||||
|
return msg
|
||||||
|
monkeypatch.setattr(click, 'echo', echofixer)
|
||||||
|
|
||||||
|
context = Context()
|
||||||
|
result = context.log("TEST", 'test')
|
||||||
|
assert callable(context.log)
|
||||||
|
assert result is None
|
||||||
|
|
||||||
|
def test_context_vlog_verbose_disabled(self, monkeypatch):
|
||||||
|
"""Test vlog() method of Context class."""
|
||||||
|
def echofixer(msg, file):
|
||||||
|
return msg
|
||||||
|
monkeypatch.setattr(click, 'echo', echofixer)
|
||||||
|
|
||||||
|
context = Context()
|
||||||
|
context.verbose = False
|
||||||
|
result = context.vlog("TEST", 'test')
|
||||||
|
assert callable(context.vlog)
|
||||||
|
assert result is None
|
||||||
|
|
||||||
|
def test_context_vlog_verbose_enabled(self, monkeypatch):
|
||||||
|
"""Test vlog() method of Context class."""
|
||||||
|
def echofixer(msg, file):
|
||||||
|
return msg
|
||||||
|
monkeypatch.setattr(click, 'echo', echofixer)
|
||||||
|
|
||||||
|
context = Context()
|
||||||
|
context.verbose = True
|
||||||
|
result = context.vlog("TEST", 'test')
|
||||||
|
assert callable(context.vlog)
|
||||||
|
assert result is None
|
||||||
|
|
||||||
|
def test_get_command_invalid(self):
|
||||||
|
"""Test MonitorStackCLI.get_command()."""
|
||||||
|
ctx = Context()
|
||||||
|
cli = MonitorStackCLI()
|
||||||
|
with pytest.raises(SystemExit) as excinfo:
|
||||||
|
cli.get_command(ctx, 'silly_rabbit_trix_are_for_kids')
|
||||||
|
assert 'silly_rabbit_trix_are_for_kids' in str(excinfo.value)
|
||||||
|
assert 'Not Found' in str(excinfo.value)
|
||||||
|
|
||||||
|
def test_get_command_valid(self):
|
||||||
|
"""Test MonitorStackCLI.get_command()."""
|
||||||
|
ctx = Context()
|
||||||
|
cli = MonitorStackCLI()
|
||||||
|
result = cli.get_command(ctx, 'uptime')
|
||||||
|
assert isinstance(result, object)
|
||||||
|
assert callable(result)
|
||||||
|
|
||||||
|
def test_list_commands(self):
|
||||||
|
"""Test MonitorStackCLI.list_commands()."""
|
||||||
|
ctx = Context()
|
||||||
|
cli = MonitorStackCLI()
|
||||||
|
result = cli.list_commands(ctx)
|
||||||
|
assert isinstance(result, list)
|
||||||
|
assert len(result) > 1
|
||||||
|
assert 'uptime' in result
|
||||||
63
tests/test_formatters.py
Normal file
63
tests/test_formatters.py
Normal file
@@ -0,0 +1,63 @@
|
|||||||
|
# Copyright 2017, Major Hayden <major@mhtx.net>
|
||||||
|
#
|
||||||
|
# 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 the output methods."""
|
||||||
|
import json
|
||||||
|
import platform
|
||||||
|
|
||||||
|
from monitorstack.common import formatters
|
||||||
|
|
||||||
|
SAMPLE_RESULT = {
|
||||||
|
'exit_code': 0,
|
||||||
|
'message': 'uptime is ok',
|
||||||
|
'measurement_name': 'system_uptime',
|
||||||
|
'meta': {
|
||||||
|
'platform': platform.platform(),
|
||||||
|
},
|
||||||
|
'variables': {
|
||||||
|
'uptime': '29587.75'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class TestFormatters(object):
|
||||||
|
"""Tests for the base cli module."""
|
||||||
|
|
||||||
|
def test_current_time(self):
|
||||||
|
"""Test current_time()."""
|
||||||
|
result = formatters.current_time()
|
||||||
|
assert isinstance(result, int)
|
||||||
|
assert result > 0
|
||||||
|
|
||||||
|
def test_write_json(self, capsys):
|
||||||
|
"""Test write_json() module."""
|
||||||
|
formatters.write_json(SAMPLE_RESULT)
|
||||||
|
out, err = capsys.readouterr()
|
||||||
|
result_json = json.loads(out)
|
||||||
|
assert isinstance(result_json, dict)
|
||||||
|
assert result_json['measurement_name'] == \
|
||||||
|
SAMPLE_RESULT['measurement_name']
|
||||||
|
|
||||||
|
def test_write_line(self, capsys):
|
||||||
|
"""Test write_line() module."""
|
||||||
|
formatters.write_line(SAMPLE_RESULT)
|
||||||
|
out, err = capsys.readouterr()
|
||||||
|
assert out == "uptime {}\n".format(
|
||||||
|
SAMPLE_RESULT['variables']['uptime']
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_write_telegraf(self, capsys):
|
||||||
|
"""Test write_telegraf() module."""
|
||||||
|
formatters.write_telegraf(SAMPLE_RESULT)
|
||||||
|
out, err = capsys.readouterr()
|
||||||
|
assert out.startswith(SAMPLE_RESULT['measurement_name'])
|
||||||
@@ -11,8 +11,7 @@
|
|||||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
# See the License for the specific language governing permissions and
|
# See the License for the specific language governing permissions and
|
||||||
# limitations under the License.
|
# limitations under the License.
|
||||||
|
"""Tests for the KVM plugin."""
|
||||||
"""Tests for the base class."""
|
|
||||||
|
|
||||||
import json
|
import json
|
||||||
import sys
|
import sys
|
||||||
@@ -11,8 +11,7 @@
|
|||||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
# See the License for the specific language governing permissions and
|
# See the License for the specific language governing permissions and
|
||||||
# limitations under the License.
|
# limitations under the License.
|
||||||
|
"""Tests for the uptime plugin."""
|
||||||
"""Tests for the base class."""
|
|
||||||
|
|
||||||
import json
|
import json
|
||||||
|
|
||||||
Reference in New Issue
Block a user