aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--samples/cpuload.yaml31
-rw-r--r--tests/unit/benchmark/scenarios/compute/cpuload_sample_output1.txt5
-rw-r--r--tests/unit/benchmark/scenarios/compute/cpuload_sample_output2.txt2
-rw-r--r--tests/unit/benchmark/scenarios/compute/test_cpuload.py238
-rwxr-xr-xtools/ubuntu-server-cloudimg-modify.sh4
-rw-r--r--yardstick/benchmark/scenarios/compute/cpuload.py239
6 files changed, 517 insertions, 2 deletions
diff --git a/samples/cpuload.yaml b/samples/cpuload.yaml
new file mode 100644
index 000000000..e28d2d281
--- /dev/null
+++ b/samples/cpuload.yaml
@@ -0,0 +1,31 @@
+---
+# Sample benchmark task config file
+# Reading processor load/statistics
+
+schema: "yardstick:task:0.1"
+
+run_in_parallel: true
+
+scenarios:
+-
+ type: CPUload
+ options:
+ interval: 2
+ host: apollo.demo
+ runner:
+ type: Duration
+ duration: 60
+
+context:
+ name: demo
+ image: yardstick-trusty-server
+ flavor: yardstick-flavor
+ user: ec2-user
+
+ servers:
+ apollo:
+ floating_ip: true
+
+ networks:
+ test:
+ cidr: '10.0.1.0/24'
diff --git a/tests/unit/benchmark/scenarios/compute/cpuload_sample_output1.txt b/tests/unit/benchmark/scenarios/compute/cpuload_sample_output1.txt
new file mode 100644
index 000000000..b1723ae17
--- /dev/null
+++ b/tests/unit/benchmark/scenarios/compute/cpuload_sample_output1.txt
@@ -0,0 +1,5 @@
+Linux 3.13.0-68-generic (elxg482ls42) 11/30/2015 _x86_64_ (12 CPU)
+
+04:53:04 PM CPU %usr %nice %sys %iowait %irq %soft %steal %guest %gnice %idle
+04:53:04 PM all 11.31 0.03 1.19 0.18 0.00 0.01 0.00 5.51 0.00 81.77
+04:53:04 PM 0 20.03 0.03 1.36 0.33 0.00 0.06 0.00 6.62 0.00 71.56
diff --git a/tests/unit/benchmark/scenarios/compute/cpuload_sample_output2.txt b/tests/unit/benchmark/scenarios/compute/cpuload_sample_output2.txt
new file mode 100644
index 000000000..c66520a27
--- /dev/null
+++ b/tests/unit/benchmark/scenarios/compute/cpuload_sample_output2.txt
@@ -0,0 +1,2 @@
+cpu 245813227 366650 17338727 1195600354 2652765 178 177114 0 80439531 0
+cpu0 32334587 35782 1659040 87008833 401178 60 73571 0 8030817 0
diff --git a/tests/unit/benchmark/scenarios/compute/test_cpuload.py b/tests/unit/benchmark/scenarios/compute/test_cpuload.py
new file mode 100644
index 000000000..22c4419b2
--- /dev/null
+++ b/tests/unit/benchmark/scenarios/compute/test_cpuload.py
@@ -0,0 +1,238 @@
+#!/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
diff --git a/tools/ubuntu-server-cloudimg-modify.sh b/tools/ubuntu-server-cloudimg-modify.sh
index bdf96ba5a..9e1b40cac 100755
--- a/tools/ubuntu-server-cloudimg-modify.sh
+++ b/tools/ubuntu-server-cloudimg-modify.sh
@@ -46,8 +46,8 @@ apt-get install -y \
lmbench \
netperf \
rt-tests \
- stress
+ stress \
+ sysstat
# restore symlink
ln -sf /run/resolvconf/resolv.conf /etc/resolv.conf
-
diff --git a/yardstick/benchmark/scenarios/compute/cpuload.py b/yardstick/benchmark/scenarios/compute/cpuload.py
new file mode 100644
index 000000000..2b458ff64
--- /dev/null
+++ b/yardstick/benchmark/scenarios/compute/cpuload.py
@@ -0,0 +1,239 @@
+##############################################################################
+# 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()