--- /dev/null
+#!/usr/bin/env python
+
+##############################################################################
+# Copyright (c) 2015 Ericsson AB and others.
+#
+# All rights reserved. This program and the accompanying materials
+# are made available under the terms of the Apache License, Version 2.0
+# which accompanies this distribution, and is available at
+# http://www.apache.org/licenses/LICENSE-2.0
+##############################################################################
+
+# Unittest for yardstick.benchmark.scenarios.compute.lmbench.Lmbench
+
+import mock
+import unittest
+import os
+
+from yardstick.benchmark.scenarios.compute import cpuload
+
+
+@mock.patch('yardstick.benchmark.scenarios.compute.cpuload.ssh')
+class CPULoadTestCase(unittest.TestCase):
+
+ def setUp(self):
+ self.ctx = {
+ 'host': {
+ 'ip': '172.16.0.137',
+ 'user': 'cirros',
+ 'key_filename': "mykey.key"
+ }
+ }
+
+ self.result = {}
+
+ def test_setup_mpstat_installed(self, mock_ssh):
+ l = cpuload.CPULoad({}, self.ctx)
+ mock_ssh.SSH().execute.return_value = (0, '', '')
+
+ l.setup()
+ self.assertIsNotNone(l.client)
+ self.assertTrue(l.setup_done)
+ self.assertTrue(l.has_mpstat)
+
+ def test_setup_mpstat_not_installed(self, mock_ssh):
+ l = cpuload.CPULoad({}, self.ctx)
+ mock_ssh.SSH().execute.return_value = (127, '', '')
+
+ l.setup()
+ self.assertIsNotNone(l.client)
+ self.assertTrue(l.setup_done)
+ self.assertFalse(l.has_mpstat)
+
+ def test_execute_command_success(self, mock_ssh):
+ l = cpuload.CPULoad({}, self.ctx)
+ mock_ssh.SSH().execute.return_value = (0, '', '')
+ l.setup()
+
+ expected_result = 'abcdefg'
+ mock_ssh.SSH().execute.return_value = (0, expected_result, '')
+ result = l._execute_command("foo")
+ self.assertEqual(result, expected_result)
+
+ def test_execute_command_failed(self, mock_ssh):
+ l = cpuload.CPULoad({}, self.ctx)
+ mock_ssh.SSH().execute.return_value = (0, '', '')
+ l.setup()
+
+ mock_ssh.SSH().execute.return_value = (127, '', 'abcdefg')
+ self.assertRaises(RuntimeError, l._execute_command,
+ "cat /proc/loadavg")
+
+ def test_get_loadavg(self, mock_ssh):
+ l = cpuload.CPULoad({}, self.ctx)
+ mock_ssh.SSH().execute.return_value = (0, '', '')
+ l.setup()
+
+ mock_ssh.SSH().execute.return_value = \
+ (0, '1.50 1.45 1.51 3/813 14322', '')
+ result = l._get_loadavg()
+ expected_result = \
+ {'loadavg': ['1.50', '1.45', '1.51', '3/813', '14322']}
+ self.assertEqual(result, expected_result)
+
+ def test_get_cpu_usage_mpstat(self, mock_ssh):
+ l = cpuload.CPULoad({}, self.ctx)
+ mock_ssh.SSH().execute.return_value = (0, '', '')
+ l.setup()
+
+ l.interval = 0
+ mpstat_output = self._read_file("cpuload_sample_output1.txt")
+ mock_ssh.SSH().execute.return_value = (0, mpstat_output, '')
+ result = l._get_cpu_usage_mpstat()
+
+ expected_result = \
+ {'mpstat':
+ {'cpu':
+ {'%gnice': '0.00',
+ '%guest': '5.51',
+ '%idle': '81.77',
+ '%iowait': '0.18',
+ '%irq': '0.00',
+ '%nice': '0.03',
+ '%soft': '0.01',
+ '%steal': '0.00',
+ '%sys': '1.19',
+ '%usr': '11.31'},
+ 'cpu0':
+ {'%gnice': '0.00',
+ '%guest': '6.62',
+ '%idle': '71.56',
+ '%iowait': '0.33',
+ '%irq': '0.00',
+ '%nice': '0.03',
+ '%soft': '0.06',
+ '%steal': '0.00',
+ '%sys': '1.36',
+ '%usr': '20.03'}}}
+
+ self.assertDictEqual(result, expected_result)
+
+ def test_get_cpu_usage(self, mock_ssh):
+ l = cpuload.CPULoad({}, self.ctx)
+ mock_ssh.SSH().execute.return_value = (0, '', '')
+ l.setup()
+
+ l.interval = 0
+ output = self._read_file("cpuload_sample_output2.txt")
+ mock_ssh.SSH().execute.return_value = (0, output, '')
+ result = l._get_cpu_usage()
+
+ expected_result = \
+ {'mpstat':
+ {'cpu':
+ {'%steal': '0.00',
+ '%usr': '11.31',
+ '%gnice': '0.00',
+ '%idle': '81.78',
+ '%iowait': '0.18',
+ '%guest': '5.50',
+ '%sys': '1.19',
+ '%soft': '0.01',
+ '%irq': '0.00',
+ '%nice': '0.03'},
+ 'cpu0':
+ {'%steal': '0.00',
+ '%usr': '20.00',
+ '%gnice': '0.00',
+ '%idle': '71.60',
+ '%iowait': '0.33',
+ '%guest': '6.61',
+ '%sys': '1.37',
+ '%soft': '0.06',
+ '%irq': '0.00',
+ '%nice': '0.03'}}}
+
+ self.assertDictEqual(result, expected_result)
+
+ def test_run_mpstat(self, mock_ssh):
+ l = cpuload.CPULoad({'options': {'interval': 1}}, self.ctx)
+ mock_ssh.SSH().execute.return_value = (0, '', '')
+
+ mpstat_output = self._read_file("cpuload_sample_output1.txt")
+ mock_ssh.SSH().execute.side_effect = \
+ [(0, '', ''), (0, '1.50 1.45 1.51 3/813 14322', ''), (0, mpstat_output, '')]
+
+ l.run(self.result)
+
+ expected_result = {
+ 'loadavg': ['1.50', '1.45', '1.51', '3/813', '14322'],
+ 'mpstat':
+ {'cpu': {'%gnice': '0.00',
+ '%guest': '5.51',
+ '%idle': '81.77',
+ '%iowait': '0.18',
+ '%irq': '0.00',
+ '%nice': '0.03',
+ '%soft': '0.01',
+ '%steal': '0.00',
+ '%sys': '1.19',
+ '%usr': '11.31'},
+ 'cpu0': {'%gnice': '0.00',
+ '%guest': '6.62',
+ '%idle': '71.56',
+ '%iowait': '0.33',
+ '%irq': '0.00',
+ '%nice': '0.03',
+ '%soft': '0.06',
+ '%steal': '0.00',
+ '%sys': '1.36',
+ '%usr': '20.03'}}}
+
+ self.assertDictEqual(self.result, expected_result)
+
+ def test_run_proc_stat(self, mock_ssh):
+ l = cpuload.CPULoad({}, self.ctx)
+ mock_ssh.SSH().execute.return_value = (1, '', '')
+ l.setup()
+
+ l.interval = 0
+ stat_output = self._read_file("cpuload_sample_output2.txt")
+ mock_ssh.SSH().execute.side_effect = \
+ [(0, '1.50 1.45 1.51 3/813 14322', ''), (0, stat_output, '')]
+
+ l.run(self.result)
+ expected_result = {
+ 'loadavg': ['1.50', '1.45', '1.51', '3/813', '14322'],
+ 'mpstat':
+ {'cpu':
+ {'%steal': '0.00',
+ '%usr': '11.31',
+ '%gnice': '0.00',
+ '%idle': '81.78',
+ '%iowait': '0.18',
+ '%guest': '5.50',
+ '%sys': '1.19',
+ '%soft': '0.01',
+ '%irq': '0.00',
+ '%nice': '0.03'},
+ 'cpu0':
+ {'%steal': '0.00',
+ '%usr': '20.00',
+ '%gnice': '0.00',
+ '%idle': '71.60',
+ '%iowait': '0.33',
+ '%guest': '6.61',
+ '%sys': '1.37',
+ '%soft': '0.06',
+ '%irq': '0.00',
+ '%nice': '0.03'}}}
+
+ self.assertDictEqual(self.result, expected_result)
+
+ def _read_file(self, filename):
+ curr_path = os.path.dirname(os.path.abspath(__file__))
+ output = os.path.join(curr_path, filename)
+ with open(output) as f:
+ sample_output = f.read()
+ return sample_output
--- /dev/null
+##############################################################################
+# Copyright (c) 2015 Ericsson AB and others.
+#
+# All rights reserved. This program and the accompanying materials
+# are made available under the terms of the Apache License, Version 2.0
+# which accompanies this distribution, and is available at
+# http://www.apache.org/licenses/LICENSE-2.0
+##############################################################################
+
+"""Processor statistics and system load."""
+
+import logging
+import time
+import re
+import yardstick.ssh as ssh
+
+from yardstick.benchmark.scenarios import base
+
+
+LOG = logging.getLogger(__name__)
+
+
+class CPULoad(base.Scenario):
+
+ """Collect processor statistics and system load.
+
+ This scenario reads system load averages and
+ CPU usage statistics on a Linux host.
+
+ CPU usage statistics are read using the utility 'mpstat'.
+
+ If 'mpstat' is not installed on the host usage statistics
+ are instead read directly from '/proc/stat'.
+
+ Load averages are read from the file '/proc/loadavg'
+ on the Linux host.
+
+ Parameters
+ interval - Time interval to measure CPU usage. A value of 0
+ indicates that processors statistics are to be
+ reported for the time since system startup (boot)
+
+ type: [int]
+ unit: seconds
+ default: 0
+
+ """
+
+ __scenario_type__ = "CPUload"
+
+ MPSTAT_FIELD_SIZE = 10
+
+ def __init__(self, scenario_cfg, context_cfg):
+ """Scenario construction."""
+ self.scenario_cfg = scenario_cfg
+ self.context_cfg = context_cfg
+ self.setup_done = False
+ self.has_mpstat = False
+
+ def setup(self):
+ """Scenario setup."""
+ host = self.context_cfg['host']
+ user = host.get('user', 'ubuntu')
+ ip = host.get('ip', None)
+ key_filename = host.get('key_filename', '~/.ssh/id_rsa')
+
+ LOG.info("user:%s, host:%s", user, ip)
+ self.client = ssh.SSH(user, ip, key_filename=key_filename)
+ self.client.wait(timeout=600)
+
+ # Check if mpstat prog is installed
+ status, _, _ = self.client.execute("mpstat -V >/dev/null 2>&1")
+ if status != 0:
+ LOG.info("MPSTAT is NOT installed")
+ self.has_mpstat = False
+ else:
+ LOG.info("MPSTAT is installed")
+ self.has_mpstat = True
+
+ if 'options' in self.scenario_cfg:
+ self.interval = self.scenario_cfg['options'].get("interval", 0)
+ else:
+ self.interval = 0
+
+ self.setup_done = True
+
+ def _execute_command(self, cmd):
+ """Execute a command on server."""
+ LOG.info("Executing: %s" % cmd)
+ status, stdout, stderr = self.client.execute(cmd)
+ if status != 0:
+ raise RuntimeError("Failed executing command: ",
+ cmd, stderr)
+ return stdout
+
+ def _get_loadavg(self):
+ """Get system load."""
+ return {'loadavg': self._execute_command("cat /proc/loadavg").split()}
+
+ def _get_cpu_usage_mpstat(self):
+ """Get processor usage using mpstat."""
+ if self.interval > 0:
+ cmd = "mpstat -P ON %s 1" % self.interval
+ else:
+ cmd = "mpstat -P ON"
+
+ result = self._execute_command(cmd)
+
+ fields = []
+ mpstat = {}
+
+ time_marker = re.compile("^([0-9]+):([0-9]+):([0-9]+)$")
+ ampm_marker = re.compile("(AM|PM)$")
+
+ # Parse CPU stats
+ for row in result.split('\n'):
+ line = row.split()
+
+ if line and re.match(time_marker, line[0]):
+
+ if re.match(ampm_marker, line[1]):
+ del line[:2]
+ else:
+ del line[:1]
+
+ if line[0] == 'CPU':
+ # header fields
+ fields = line[1:]
+ if len(fields) != CPULoad.MPSTAT_FIELD_SIZE:
+ raise RuntimeError("mpstat: unexpected field size",
+ fields)
+ else:
+ # value fields
+ cpu = 'cpu' if line[0] == 'all' else 'cpu' + line[0]
+ values = line[1:]
+ if values and len(values) == len(fields):
+ mpstat[cpu] = dict(zip(fields, values))
+ else:
+ raise RuntimeError("mpstat: parse error", fields, line)
+
+ return {'mpstat': mpstat}
+
+ def _get_cpu_usage(self):
+ """Get processor usage from /proc/stat."""
+ fields = ['%usr', '%nice', '%sys', '%idle', '%iowait',
+ '%irq', '%soft', '%steal', '%guest', '%gnice']
+
+ cmd = "grep '^cpu[0-9 ].' /proc/stat"
+
+ if self.interval > 0:
+ previous = self._execute_command(cmd).splitlines()
+ time.sleep(self.interval)
+ current = self._execute_command(cmd).splitlines()
+ else:
+ current = self._execute_command(cmd).splitlines()
+ previous = current
+
+ mpstat = {}
+
+ for (prev, cur) in zip(previous, current):
+
+ # Split string to list tokens
+ cur_list = cur.split()
+ prev_list = prev.split()
+
+ cpu = cur_list[0]
+
+ cur_stats = map(int, cur_list[1:])
+ if self.interval > 0:
+ prev_stats = map(int, prev_list[1:])
+ else:
+ prev_stats = [0] * len(cur_stats)
+
+ # NB: Don't add 'guest' and 'gnice' as they
+ # are already included in 'usr' and 'nice'.
+ uptime_prev = sum(prev_stats[0:8])
+ uptime_cur = sum(cur_stats[0:8])
+
+ # Remove 'guest' and 'gnice' from 'usr' and 'nice'
+ prev_stats[0] -= prev_stats[8]
+ prev_stats[1] -= prev_stats[9]
+ cur_stats[0] -= cur_stats[8]
+ cur_stats[1] -= cur_stats[9]
+
+ # number of samples (jiffies) in the interval
+ samples = (uptime_cur - uptime_prev) or 1
+
+ def _percent(x, y):
+ if x < y:
+ return 0.0
+ else:
+ return "%.2f" % (100.0 * (x - y) / samples)
+
+ load = map(_percent, cur_stats, prev_stats)
+
+ mpstat[cpu] = dict(zip(fields, load))
+
+ return {'mpstat': mpstat}
+
+ def run(self, result):
+ """Read processor statistics."""
+ if not self.setup_done:
+ self.setup()
+
+ result.update(self._get_loadavg())
+
+ if self.has_mpstat:
+ result.update(self._get_cpu_usage_mpstat())
+ else:
+ result.update(self._get_cpu_usage())
+
+ # Note: No SLA as this scenario is only collecting statistics
+
+# def _test():
+# """internal test function."""
+# import pkg_resources
+# key_filename = pkg_resources.resource_filename('yardstick.resources',
+# 'files/yardstick_key')
+# ctx = {
+# 'host': {
+# 'ip': '172.16.0.175',
+# 'user': 'ec2-user',
+# 'key_filename': key_filename
+# }
+# }
+
+# logger = logging.getLogger('yardstick')
+# logger.setLevel(logging.DEBUG)
+
+# args = {}
+# result = {}
+
+# p = CPULoad(args, ctx)
+# p.run(result)
+# import json
+# print json.dumps(result)
+
+# if __name__ == '__main__':
+# _test()