1 ##############################################################################
2 # Copyright (c) 2015 Ericsson AB and others.
4 # All rights reserved. This program and the accompanying materials
5 # are made available under the terms of the Apache License, Version 2.0
6 # which accompanies this distribution, and is available at
7 # http://www.apache.org/licenses/LICENSE-2.0
8 ##############################################################################
10 """Processor statistics and system load."""
12 from __future__ import absolute_import
18 from six.moves import map, zip
20 import yardstick.ssh as ssh
21 from yardstick.benchmark.scenarios import base
23 LOG = logging.getLogger(__name__)
26 class CPULoad(base.Scenario):
28 """Collect processor statistics and system load.
30 This scenario reads system load averages and
31 CPU usage statistics on a Linux host.
33 CPU usage statistics are read using the utility 'mpstat'.
35 If 'mpstat' is not installed on the host usage statistics
36 are instead read directly from '/proc/stat'.
38 Load averages are read from the file '/proc/loadavg'
42 interval - Time interval to measure CPU usage.
48 count (for mpstat only) - Number of CPU usage measurment.
56 __scenario_type__ = "CPUload"
58 MPSTAT_FIELD_SIZE = 10
60 def __init__(self, scenario_cfg, context_cfg):
61 """Scenario construction."""
62 self.scenario_cfg = scenario_cfg
63 self.context_cfg = context_cfg
64 self.setup_done = False
65 self.has_mpstat = False
66 self.has_count = False
70 host = self.context_cfg['host']
72 self.client = ssh.SSH.from_node(host, defaults={"user": "ubuntu"})
73 self.client.wait(timeout=600)
75 # Check if mpstat prog is installed
76 status, _, _ = self.client.execute("mpstat -V >/dev/null 2>&1")
78 LOG.info("MPSTAT is NOT installed")
79 self.has_mpstat = False
81 LOG.info("MPSTAT is installed")
82 self.has_mpstat = True
84 options = self.scenario_cfg['options']
85 self.interval = options.get("interval", 1)
86 if 'count' in options:
87 self.count = options.get("count", 1)
90 self.has_count = False
92 self.setup_done = True
94 def _execute_command(self, cmd):
95 """Execute a command on server."""
96 LOG.info("Executing: %s", cmd)
97 status, stdout, stderr = self.client.execute(cmd)
99 raise RuntimeError("Failed executing command: ",
103 def _get_loadavg(self):
104 """Get system load."""
105 return {'loadavg': self._execute_command("cat /proc/loadavg").split()}
107 def _get_cpu_usage_mpstat(self):
108 """Get processor usage using mpstat."""
109 if self.interval > 0 and self.has_count:
110 cmd = "mpstat -P ON %s %s" % (self.interval, self.count)
112 cmd = "mpstat -P ON %s 1" % self.interval
114 result = self._execute_command(cmd)
121 time_marker = re.compile("^([0-9]+):([0-9]+):([0-9]+)$")
122 ampm_marker = re.compile("(AM|PM)$")
125 for row in result.split('\n'):
128 if line and re.match(time_marker, line[0]):
129 if re.match(ampm_marker, line[1]):
137 if len(fields) != CPULoad.MPSTAT_FIELD_SIZE:
138 raise RuntimeError("mpstat: unexpected field size",
142 cpu = 'cpu' if line[0] == 'all' else 'cpu' + line[0]
144 if values and len(values) == len(fields):
145 temp_dict = dict(list(zip(fields, values)))
146 if cpu not in maximum:
147 maximum[cpu] = temp_dict
149 for item in temp_dict:
150 if float(maximum[cpu][item]) <\
151 float(temp_dict[item]):
152 maximum[cpu][item] = temp_dict[item]
154 if cpu not in minimum:
155 minimum[cpu] = temp_dict
157 for item in temp_dict:
158 if float(minimum[cpu][item]) >\
159 float(temp_dict[item]):
160 minimum[cpu][item] = temp_dict[item]
162 raise RuntimeError("mpstat: parse error", fields, line)
164 elif line and line[0] == 'Average:':
169 if len(fields) != CPULoad.MPSTAT_FIELD_SIZE:
170 raise RuntimeError("mpstat average: unexpected field\
174 cpu = 'cpu' if line[0] == 'all' else 'cpu' + line[0]
176 if values and len(values) == len(fields):
177 average[cpu] = dict(list(zip(fields, values)))
179 raise RuntimeError("mpstat average: parse error",
182 return {'mpstat_maximun': maximum, 'mpstat_minimum': minimum,
183 'mpstat_average': average}
185 def _get_cpu_usage(self):
186 """Get processor usage from /proc/stat."""
187 fields = ['%usr', '%nice', '%sys', '%idle', '%iowait',
188 '%irq', '%soft', '%steal', '%guest', '%gnice']
190 cmd = "grep '^cpu[0-9 ].' /proc/stat"
192 if self.interval > 0:
193 previous = self._execute_command(cmd).splitlines()
194 time.sleep(self.interval)
195 current = self._execute_command(cmd).splitlines()
197 current = self._execute_command(cmd).splitlines()
202 for (prev, cur) in zip(previous, current):
204 # Split string to list tokens
205 cur_list = cur.split()
206 prev_list = prev.split()
210 cur_stats = list(map(int, cur_list[1:]))
211 if self.interval > 0:
212 prev_stats = list(map(int, prev_list[1:]))
214 prev_stats = [0] * len(cur_stats)
216 # NB: Don't add 'guest' and 'gnice' as they
217 # are already included in 'usr' and 'nice'.
218 uptime_prev = sum(prev_stats[0:8])
219 uptime_cur = sum(cur_stats[0:8])
221 # Remove 'guest' and 'gnice' from 'usr' and 'nice'
222 prev_stats[0] -= prev_stats[8]
223 prev_stats[1] -= prev_stats[9]
224 cur_stats[0] -= cur_stats[8]
225 cur_stats[1] -= cur_stats[9]
227 # number of samples (jiffies) in the interval
228 samples = (uptime_cur - uptime_prev) or 1
234 return "%.2f" % (100.0 * (x - y) / samples)
236 load = list(map(_percent, cur_stats, prev_stats))
238 mpstat[cpu] = dict(list(zip(fields, load)))
240 return {'mpstat': mpstat}
242 def run(self, result):
243 """Read processor statistics."""
244 if not self.setup_done:
247 result.update(self._get_loadavg())
250 result.update(self._get_cpu_usage_mpstat())
252 result.update(self._get_cpu_usage())
254 # Note: No SLA as this scenario is only collecting statistics
257 # """internal test function."""
258 # import pkg_resources
259 # key_filename = pkg_resources.resource_filename('yardstick.resources',
260 # 'files/yardstick_key')
263 # 'ip': '172.16.0.175',
265 # 'key_filename': key_filename
269 # logger = logging.getLogger('yardstick')
270 # logger.setLevel(logging.DEBUG)
275 # p = CPULoad(args, ctx)
278 # print(oslo_serialization.jsonutils.dump_as_bytes(result))
280 # if __name__ == '__main__':