summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMichael Rice <michael.rice@rackspace.com>2017-05-11 14:35:32 -0500
committerMichael Rice <michael@michaelrice.org>2018-04-17 21:05:22 -0500
commitb676952bbed76a0d1991fa1f815e804955ff8682 (patch)
tree4fb691411899692559c2e6ba330e58cc031f8275
parentcee20d77405392303ce4089f0bc0a10044ebb5ae (diff)
Adding vg_check from the maas pluginsHEADmaster
This change adds the volume group check from the maas plugins, and also adds a cli helper to run bash command. Change-Id: I303a843b0abaea721758e182a9c8f3e2db33e85d Signed-off-by: Michael Rice <michael.rice@rackspace.com> Signed-off-by: Kevin Carter <kevin.carter@rackspace.com>
Notes
Notes (review): Code-Review+2: Kevin Carter (cloudnull) <kevin@cloudnull.com> Workflow+1: Kevin Carter (cloudnull) <kevin@cloudnull.com> Verified+2: Zuul Submitted-by: Zuul Submitted-at: Wed, 18 Apr 2018 03:02:46 +0000 Reviewed-on: https://review.openstack.org/470196 Project: openstack/monitorstack Branch: refs/heads/master
-rw-r--r--monitorstack/plugins/vg_check.py71
-rw-r--r--monitorstack/utils/cli.py26
-rw-r--r--tests/__init__.py3
-rw-r--r--tests/unit/__init__.py10
-rw-r--r--tests/unit/test_cli_utils.py56
-rw-r--r--tests/unit/test_plugin_kvm.py16
-rw-r--r--tests/unit/test_plugin_vg_check.py76
7 files changed, 242 insertions, 16 deletions
diff --git a/monitorstack/plugins/vg_check.py b/monitorstack/plugins/vg_check.py
new file mode 100644
index 0000000..1ea193f
--- /dev/null
+++ b/monitorstack/plugins/vg_check.py
@@ -0,0 +1,71 @@
1# Copyright 2017, Michael Rice <michael@michaelrice.org>
2#
3# Licensed under the Apache License, Version 2.0 (the "License");
4# you may not use this file except in compliance with the License.
5# You may obtain a copy of the License at
6#
7# http://www.apache.org/licenses/LICENSE-2.0
8#
9# Unless required by applicable law or agreed to in writing, software
10# distributed under the License is distributed on an "AS IS" BASIS,
11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12# See the License for the specific language governing permissions and
13# limitations under the License.
14
15import platform
16
17import click
18
19from monitorstack import utils
20from monitorstack.cli import pass_context
21from monitorstack.utils.cli import run_command
22
23DOC = """Check a given volume group"""
24COMMAND_NAME = 'vg_check'
25
26
27@click.command(COMMAND_NAME, short_help=DOC)
28@click.option('--volume_group', nargs=1, type=str, required=True)
29@pass_context
30def cli(ctx, volume_group):
31 """
32 Given volume group name get the total size and free space
33
34 :param ctx: Click context
35 :param volume_group: Name of volume group
36 :type volume_group: str
37 :return:
38 """
39 exit_code, total_size, free = check_volgrp(volume_group)
40 output = {
41 'exit_code': exit_code,
42 'measurement_name': COMMAND_NAME,
43 'meta': {
44 'platform': platform.platform(),
45 }
46 }
47 if exit_code == 0:
48 output['message'] = '{} check for volume group {} is ok'.format(
49 COMMAND_NAME, volume_group)
50 output['variables'] = {
51 'vg_{}_total_size_M'.format(volume_group): total_size,
52 'vg_{}_free_M'.format(volume_group): free,
53 'vg_{}_used_M'.format(volume_group): total_size - free
54 }
55 if exit_code != 0:
56 # if exit_code is not 0 then 'free' actually has our error output.
57 # and with py3 it is bytes so we convert to str first.
58 output['message'] = '{} for {} failed -- {}'.format(
59 COMMAND_NAME, volume_group, utils.log_exception(str(free))
60 )
61 return output
62
63
64def check_volgrp(name):
65 command = ('vgs {} --noheadings --units M '
66 '--nosuffix -o vg_size,vg_free'.format(name))
67 retcode, output, err = run_command(command)
68 if retcode != 0:
69 return retcode, output, err
70 totalsize, free = [int(float(x)) for x in output.split()]
71 return retcode, totalsize, free
diff --git a/monitorstack/utils/cli.py b/monitorstack/utils/cli.py
new file mode 100644
index 0000000..735185b
--- /dev/null
+++ b/monitorstack/utils/cli.py
@@ -0,0 +1,26 @@
1# Copyright 2017, Michael Rice <michael@michaelrice.org>
2#
3# Licensed under the Apache License, Version 2.0 (the "License");
4# you may not use this file except in compliance with the License.
5# You may obtain a copy of the License at
6#
7# http://www.apache.org/licenses/LICENSE-2.0
8#
9# Unless required by applicable law or agreed to in writing, software
10# distributed under the License is distributed on an "AS IS" BASIS,
11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12# See the License for the specific language governing permissions and
13# limitations under the License.
14import shlex
15import subprocess
16
17
18def run_command(arg):
19 proc = subprocess.Popen(shlex.split(arg),
20 stdout=subprocess.PIPE,
21 stderr=subprocess.PIPE,
22 shell=False)
23
24 out, err = proc.communicate()
25 ret = proc.returncode
26 return ret, out, err
diff --git a/tests/__init__.py b/tests/__init__.py
index cd921de..d4739b0 100644
--- a/tests/__init__.py
+++ b/tests/__init__.py
@@ -31,7 +31,8 @@ def runner(module, extra_args=None):
31 """ 31 """
32 _runner = CliRunner() 32 _runner = CliRunner()
33 args = [ 33 args = [
34 '-f', 'json', 34 '-f',
35 'json',
35 module 36 module
36 ] 37 ]
37 if extra_args: 38 if extra_args:
diff --git a/tests/unit/__init__.py b/tests/unit/__init__.py
index 3f13631..82ea468 100644
--- a/tests/unit/__init__.py
+++ b/tests/unit/__init__.py
@@ -34,3 +34,13 @@ def read_config():
34def fake_version_info(major, minor, serial): 34def fake_version_info(major, minor, serial):
35 """Return tuple for fake python version info.""" 35 """Return tuple for fake python version info."""
36 return major, minor, serial 36 return major, minor, serial
37
38
39class FakePopen(object):
40 """Fake Shell Commands."""
41 def __init__(self, return_code=0, *args, **kwargs):
42 self.returncode = return_code
43
44 @staticmethod
45 def communicate():
46 return 'stdout', 'stderr'
diff --git a/tests/unit/test_cli_utils.py b/tests/unit/test_cli_utils.py
new file mode 100644
index 0000000..656533e
--- /dev/null
+++ b/tests/unit/test_cli_utils.py
@@ -0,0 +1,56 @@
1# Copyright 2018, Kevin Carter <kevin@cloudnull.com>
2#
3# Licensed under the Apache License, Version 2.0 (the "License");
4# you may not use this file except in compliance with the License.
5# You may obtain a copy of the License at
6#
7# http://www.apache.org/licenses/LICENSE-2.0
8#
9# Unless required by applicable law or agreed to in writing, software
10# distributed under the License is distributed on an "AS IS" BASIS,
11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12# See the License for the specific language governing permissions and
13# limitations under the License.
14"""Tests for the cli utils plugin."""
15
16import unittest
17
18import mock
19
20from monitorstack.utils import cli
21
22import tests # Import the test base module
23
24
25class TestCliUtils(unittest.TestCase):
26 """Tests for the utilities."""
27
28 def setUp(self):
29 """Setup the test."""
30 # load the base class for these tests.
31 self.communicate_patched = mock.patch(
32 'monitorstack.utils.cli.subprocess.Popen'
33 )
34 self.communicate = self.communicate_patched.start()
35
36 def tearDown(self):
37 """Tear down the test."""
38 self.communicate_patched.stop()
39
40 def test_run_command_success(self):
41 self.communicate.return_value = tests.unit.FakePopen()
42 ret, out, err = cli.run_command(
43 arg='test_command'
44 )
45 self.assertEqual(out, 'stdout')
46 self.assertEqual(ret, 0)
47
48 def test_run_command_fail(self):
49 self.communicate.return_value = tests.unit.FakePopen(
50 return_code=1
51 )
52 ret, out, err = cli.run_command(
53 arg='test_command'
54 )
55 self.assertEqual(err, 'stderr')
56 self.assertEqual(ret, 1)
diff --git a/tests/unit/test_plugin_kvm.py b/tests/unit/test_plugin_kvm.py
index b4d8305..d52bd06 100644
--- a/tests/unit/test_plugin_kvm.py
+++ b/tests/unit/test_plugin_kvm.py
@@ -13,26 +13,12 @@
13# limitations under the License. 13# limitations under the License.
14"""Tests for the KVM plugin.""" 14"""Tests for the KVM plugin."""
15 15
16import json
17import sys 16import sys
18import unittest 17import unittest
19 18
20from click.testing import CliRunner
21
22from monitorstack.cli import cli
23
24import tests.unit # Import the test base module 19import tests.unit # Import the test base module
25 20
26 21
27def _runner(module):
28 runner = CliRunner()
29 result = runner.invoke(cli, ['-f', 'json', module])
30 try:
31 return json.loads(result.output)
32 except Exception:
33 return result.exception
34
35
36class LibvirtStub(object): 22class LibvirtStub(object):
37 """Stubbed libvirt class.""" 23 """Stubbed libvirt class."""
38 24
@@ -91,7 +77,7 @@ class TestKvm(unittest.TestCase):
91 77
92 def test_run_success(self): 78 def test_run_success(self):
93 """Ensure the run() method works.""" 79 """Ensure the run() method works."""
94 result = _runner('kvm') 80 result = tests.runner('kvm')
95 variables = result['variables'] 81 variables = result['variables']
96 meta = result['meta'] 82 meta = result['meta']
97 assert 'kvm_vms' in variables 83 assert 'kvm_vms' in variables
diff --git a/tests/unit/test_plugin_vg_check.py b/tests/unit/test_plugin_vg_check.py
new file mode 100644
index 0000000..d13e811
--- /dev/null
+++ b/tests/unit/test_plugin_vg_check.py
@@ -0,0 +1,76 @@
1# Copyright 2017, Michael Rice <michael@michaelrice.org>
2#
3# Licensed under the Apache License, Version 2.0 (the "License");
4# you may not use this file except in compliance with the License.
5# You may obtain a copy of the License at
6#
7# http://www.apache.org/licenses/LICENSE-2.0
8#
9# Unless required by applicable law or agreed to in writing, software
10# distributed under the License is distributed on an "AS IS" BASIS,
11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12# See the License for the specific language governing permissions and
13# limitations under the License.
14
15import unittest
16
17import mock
18
19from monitorstack.plugins import vg_check
20
21import tests.unit # Import the test base module
22
23
24class VolumeGroupTestCases(unittest.TestCase):
25
26 def setUp(self):
27 """Setup the test."""
28 # load the base class for these tests.
29 self.communicate_patched = mock.patch(
30 'monitorstack.utils.cli.subprocess.Popen'
31 )
32 self.communicate = self.communicate_patched.start()
33
34 def tearDown(self):
35 """Tear down the test."""
36 self.communicate_patched.stop()
37
38 @mock.patch("monitorstack.utils.cli.subprocess")
39 def test_check_volgrp_returns_with_vg_not_found(self, mock_path):
40 """
41 When the volume group is not found an exit status code of 5 is
42 returned by the system. When a non 0 is returned the expected
43 result from this call is is an error message with a blank total
44 """
45 mock_path.Popen.return_value.returncode = 5
46 mock_path.Popen.return_value.communicate.return_value = \
47 ("", "Volume group foo not found")
48 ret_code, total, free = vg_check.check_volgrp("foo")
49 assert ret_code == 5
50 assert total == ""
51 assert free == "Volume group foo not found"
52
53
54class TestVolumeGroup(object):
55 def test_cli_would_exec_command(self, monkeypatch):
56 def mock_get_vgs(name):
57 """Mock the check_volgrp() method."""
58 return 0, 100, 99
59
60 monkeypatch.setattr(
61 vg_check,
62 'check_volgrp',
63 mock_get_vgs
64 )
65
66 result = tests.runner(
67 'vg_check',
68 extra_args=['--volume_group', 'test']
69 )
70 variables = result['variables']
71 assert 'vg_test_used_M' in variables
72 assert variables['vg_test_used_M'] == 1
73 assert 'vg_test_free_M' in variables
74 assert variables['vg_test_free_M'] == 99
75 assert 'vg_test_total_size_M' in variables
76 assert variables['vg_test_total_size_M'] == 100