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."""
15 import yardstick.ssh as ssh
17 from yardstick.benchmark.scenarios import base
20 LOG = logging.getLogger(__name__)
23 class CPULoad(base.Scenario):
25 """Collect processor statistics and system load.
27 This scenario reads system load averages and
28 CPU usage statistics on a Linux host.
30 CPU usage statistics are read using the utility 'mpstat'.
32 If 'mpstat' is not installed on the host usage statistics
33 are instead read directly from '/proc/stat'.
35 Load averages are read from the file '/proc/loadavg'
39 interval - Time interval to measure CPU usage.
45 count (for mpstat only) - Number of CPU usage measurment.
53 __scenario_type__ = "CPUload"
55 MPSTAT_FIELD_SIZE = 10
57 def __init__(self, scenario_cfg, context_cfg):
58 """Scenario construction."""
59 self.scenario_cfg = scenario_cfg
60 self.context_cfg = context_cfg
61 self.setup_done = False
62 self.has_mpstat = False
63 self.has_count = False
67 host = self.context_cfg['host']
68 user = host.get('user', 'ubuntu')
69 ip = host.get('ip', None)
70 key_filename = host.get('key_filename', '~/.ssh/id_rsa')
72 LOG.info("user:%s, host:%s", user, ip)
73 self.client = ssh.SSH(user, ip, key_filename=key_filename)
74 self.client.wait(timeout=600)
76 # Check if mpstat prog is installed
77 status, _, _ = self.client.execute("mpstat -V >/dev/null 2>&1")
79 LOG.info("MPSTAT is NOT installed")
80 self.has_mpstat = False
82 LOG.info("MPSTAT is installed")
83 self.has_mpstat = True
85 options = self.scenario_cfg['options']
86 self.interval = options.get("interval", 1)
87 if 'count' in options:
88 self.count = options.get("count", 1)
91 self.has_count = False
93 self.setup_done = True
95 def _execute_command(self, cmd):
96 """Execute a command on server."""
97 LOG.info("Executing: %s" % cmd)
98 status, stdout, stderr = self.client.execute(cmd)
100 raise RuntimeError("Failed executing command: ",
104 def _get_loadavg(self):
105 """Get system load."""
106 return {'loadavg': self._execute_command("cat /proc/loadavg").split()}
108 def _get_cpu_usage_mpstat(self):
109 """Get processor usage using mpstat."""
110 if self.interval > 0 and self.has_count:
111 cmd = "mpstat -P ON %s %s" % (self.interval, self.count)
113 cmd = "mpstat -P ON %s 1" % self.interval
115 result = self._execute_command(cmd)
122 time_marker = re.compile("^([0-9]+):([0-9]+):([0-9]+)$")
123 ampm_marker = re.compile("(AM|PM)$")
126 for row in result.split('\n'):
129 if line and re.match(time_marker, line[0]):
130 if re.match(ampm_marker, line[1]):
138 if len(fields) != CPULoad.MPSTAT_FIELD_SIZE:
139 raise RuntimeError("mpstat: unexpected field size",
143 cpu = 'cpu' if line[0] == 'all' else 'cpu' + line[0]
145 if values and len(values) == len(fields):
146 temp_dict = dict(zip(fields, values))
147 if cpu not in maximum:
148 maximum[cpu] = temp_dict
150 for item in temp_dict:
151 if float(maximum[cpu][item]) <\
152 float(temp_dict[item]):
153 maximum[cpu][item] = temp_dict[item]
155 if cpu not in minimum:
156 minimum[cpu] = temp_dict
158 for item in temp_dict:
159 if float(minimum[cpu][item]) >\
160 float(temp_dict[item]):
161 minimum[cpu][item] = temp_dict[item]
163 raise RuntimeError("mpstat: parse error", fields, line)
165 elif line and line[0] == 'Average:':
170 if len(fields) != CPULoad.MPSTAT_FIELD_SIZE:
171 raise RuntimeError("mpstat average: unexpected field\
175 cpu = 'cpu' if line[0] == 'all' else 'cpu' + line[0]
177 if values and len(values) == len(fields):
178 average[cpu] = dict(zip(fields, values))
180 raise RuntimeError("mpstat average: parse error",
183 return {'mpstat_maximun': maximum, 'mpstat_minimum': minimum,
184 'mpstat_average': average}
186 def _get_cpu_usage(self):
187 """Get processor usage from /proc/stat."""
188 fields = ['%usr', '%nice', '%sys', '%idle', '%iowait',
189 '%irq', '%soft', '%steal', '%guest', '%gnice']
191 cmd = "grep '^cpu[0-9 ].' /proc/stat"
193 if self.interval > 0:
194 previous = self._execute_command(cmd).splitlines()
195 time.sleep(self.interval)
196 current = self._execute_command(cmd).splitlines()
198 current = self._execute_command(cmd).splitlines()
203 for (prev, cur) in zip(previous, current):
205 # Split string to list tokens
206 cur_list = cur.split()
207 prev_list = prev.split()
211 cur_stats = map(int, cur_list[1:])
212 if self.interval > 0:
213 prev_stats = map(int, prev_list[1:])
215 prev_stats = [0] * len(cur_stats)
217 # NB: Don't add 'guest' and 'gnice' as they
218 # are already included in 'usr' and 'nice'.
219 uptime_prev = sum(prev_stats[0:8])
220 uptime_cur = sum(cur_stats[0:8])
222 # Remove 'guest' and 'gnice' from 'usr' and 'nice'
223 prev_stats[0] -= prev_stats[8]
224 prev_stats[1] -= prev_stats[9]
225 cur_stats[0] -= cur_stats[8]
226 cur_stats[1] -= cur_stats[9]
228 # number of samples (jiffies) in the interval
229 samples = (uptime_cur - uptime_prev) or 1
235 return "%.2f" % (100.0 * (x - y) / samples)
237 load = map(_percent, cur_stats, prev_stats)
239 mpstat[cpu] = dict(zip(fields, load))
241 return {'mpstat': mpstat}
243 def run(self, result):
244 """Read processor statistics."""
245 if not self.setup_done:
248 result.update(self._get_loadavg())
251 result.update(self._get_cpu_usage_mpstat())
253 result.update(self._get_cpu_usage())
255 # Note: No SLA as this scenario is only collecting statistics
258 # """internal test function."""
259 # import pkg_resources
260 # key_filename = pkg_resources.resource_filename('yardstick.resources',
261 # 'files/yardstick_key')
264 # 'ip': '172.16.0.175',
266 # 'key_filename': key_filename
270 # logger = logging.getLogger('yardstick')
271 # logger.setLevel(logging.DEBUG)
276 # p = CPULoad(args, ctx)
279 # print json.dumps(result)
281 # if __name__ == '__main__':