From 79d23cd7137fa72ab9ba90904efb2affb4091390 Mon Sep 17 00:00:00 2001 From: Major Hayden Date: Fri, 10 Mar 2017 15:49:11 -0600 Subject: [PATCH] Add process check (#23) This patch adds a check for running processes along with tests. --- monitorstack/cli.py | 1 + monitorstack/plugins/process.py | 79 +++++++++++++++++++ requirements.txt | 1 + tests/{test_os_vm.py => test_plugin_os_vm.py} | 1 - tests/test_plugin_process.py | 61 ++++++++++++++ 5 files changed, 142 insertions(+), 1 deletion(-) create mode 100644 monitorstack/plugins/process.py rename tests/{test_os_vm.py => test_plugin_os_vm.py} (99%) create mode 100644 tests/test_plugin_process.py diff --git a/monitorstack/cli.py b/monitorstack/cli.py index ef0e4f8..06ab0de 100755 --- a/monitorstack/cli.py +++ b/monitorstack/cli.py @@ -113,6 +113,7 @@ def process_result(result, output_format, verbose): method_name ) output_formatter(result) + sys.exit(result['exit_code']) if __name__ == '__main__': diff --git a/monitorstack/plugins/process.py b/monitorstack/plugins/process.py new file mode 100644 index 0000000..1862f52 --- /dev/null +++ b/monitorstack/plugins/process.py @@ -0,0 +1,79 @@ +# Copyright 2017, Major Hayden +# +# 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. +"""Get system uptime.""" +import os +import platform + +import click + +from monitorstack.cli import pass_context + +import psutil + +DOC = """Check if a process is running.""" +COMMAND_NAME = 'process' + + +@click.command(COMMAND_NAME, short_help=DOC) +@click.argument('process_name', nargs=1, type=str, required=True) +@pass_context +def cli(ctx, process_name): + """Check if a process is running.""" + setattr(cli, '__doc__', DOC) + + output = { + 'exit_code': 0, + 'message': 'process check is ok', + 'measurement_name': 'process', + 'meta': { + 'platform': platform.platform(), + }, + 'variables': {} + } + + if check_process(process_name): + output['variables'] = {process_name: 1} + else: + output['exit_code'] = 1 + output['message'] = '{} failed -- Process {} not found'.format( + COMMAND_NAME, + process_name + ) + output['variables'] = {process_name: 0} + + return output + + +def check_process(process_name): + """Check if a process is in the list of cmdlines.""" + matches = [x for x in get_cmdlines() if process_name in x] + return True if len(matches) > 0 else False + + +def get_cmdlines(): + """Retrieve the cmdline of each process running on the system.""" + processes = [] + + # Get our current PID as well as the parent so we can exclude them. + current_pid = os.getpid() + parent_pid = os.getppid() + + for proc in psutil.process_iter(): + try: + if proc.pid not in [current_pid, parent_pid]: + processes.append(' '.join(proc.cmdline())) + except psutil.NoSuchProcess: + pass + + return processes diff --git a/requirements.txt b/requirements.txt index 393b845..97d4491 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,4 +1,5 @@ click openstacksdk>=0.9.14 +psutil>=5.2.0 six stevedore diff --git a/tests/test_os_vm.py b/tests/test_plugin_os_vm.py similarity index 99% rename from tests/test_os_vm.py rename to tests/test_plugin_os_vm.py index af07dd2..0b1b736 100644 --- a/tests/test_os_vm.py +++ b/tests/test_plugin_os_vm.py @@ -48,7 +48,6 @@ def mock_get_consumer_usage(self): 'id': 1, 'name': 'flavor_one', } - }] diff --git a/tests/test_plugin_process.py b/tests/test_plugin_process.py new file mode 100644 index 0000000..5a29779 --- /dev/null +++ b/tests/test_plugin_process.py @@ -0,0 +1,61 @@ +# Copyright 2017, Major Hayden +# +# 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 process plugin.""" + +import json + +from click.testing import CliRunner + +from monitorstack.cli import cli +from monitorstack.plugins import process + + +class TestUptime(object): + """Tests for the uptime monitor class.""" + + def test_run_failure(self): + """Ensure the run() method works.""" + runner = CliRunner() + process_name = 'dont-go-chasing-waterfalls' + result = runner.invoke(cli, [ + '-f', 'json', + 'process', process_name]) + result_json = json.loads(result.output) + assert result_json['variables'] == {process_name: 0} + assert result.exit_code == 1 + + def test_run_success(self): + """Ensure the run() method works.""" + runner = CliRunner() + process_name = '/' + result = runner.invoke(cli, [ + '-f', 'json', + 'process', process_name]) + result_json = json.loads(result.output) + assert result_json['variables'] == {process_name: 1} + assert result.exit_code == 0 + + def test_check_process_success(self, monkeypatch): + """Ensure the check_process() method works.""" + def mock_get_cmdlines(): + return ['process1', 'process2'] + + monkeypatch.setattr(process, 'get_cmdlines', mock_get_cmdlines) + result = process.check_process('process2') + assert result + + def test_get_cmdlines(self): + """Ensure the get_cmdlines() method works.""" + cmdlines = process.get_cmdlines() + assert isinstance(cmdlines, list)