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 ssh_port = host.get("ssh_port", ssh.DEFAULT_PORT)
71 key_filename = host.get('key_filename', '~/.ssh/id_rsa')
73 LOG.info("user:%s, host:%s", user, ip)
74 self.client = ssh.SSH(user, ip, key_filename=key_filename,
76 self.client.wait(timeout=600)
78 # Check if mpstat prog is installed
79 status, _, _ = self.client.execute("mpstat -V >/dev/null 2>&1")
81 LOG.info("MPSTAT is NOT installed")
82 self.has_mpstat = False
84 LOG.info("MPSTAT is installed")
85 self.has_mpstat = True
87 options = self.scenario_cfg['options']
88 self.interval = options.get("interval", 1)
89 if 'count' in options:
90 self.count = options.get("count", 1)
93 self.has_count = False
95 self.setup_done = True
97 def _execute_command(self, cmd):
98 """Execute a command on server."""
99 LOG.info("Executing: %s" % cmd)
100 status, stdout, stderr = self.client.execute(cmd)
102 raise RuntimeError("Failed executing command: ",
106 def _get_loadavg(self):
107 """Get system load."""
108 return {'loadavg': self._execute_command("cat /proc/loadavg").split()}
110 def _get_cpu_usage_mpstat(self):
111 """Get processor usage using mpstat."""
112 if self.interval > 0 and self.has_count:
113 cmd = "mpstat -P ON %s %s" % (self.interval, self.count)
115 cmd = "mpstat -P ON %s 1" % self.interval
117 result = self._execute_command(cmd)
124 time_marker = re.compile("^([0-9]+):([0-9]+):([0-9]+)$")
125 ampm_marker = re.compile("(AM|PM)$")
128 for row in result.split('\n'):
131 if line and re.match(time_marker, line[0]):
132 if re.match(ampm_marker, line[1]):
140 if len(fields) != CPULoad.MPSTAT_FIELD_SIZE:
141 raise RuntimeError("mpstat: unexpected field size",
145 cpu = 'cpu' if line[0] == 'all' else 'cpu' + line[0]
147 if values and len(values) == len(fields):
148 temp_dict = dict(zip(fields, values))
149 if cpu not in maximum:
150 maximum[cpu] = temp_dict
152 for item in temp_dict:
153 if float(maximum[cpu][item]) <\
154 float(temp_dict[item]):
155 maximum[cpu][item] = temp_dict[item]
157 if cpu not in minimum:
158 minimum[cpu] = temp_dict
160 for item in temp_dict:
161 if float(minimum[cpu][item]) >\
162 float(temp_dict[item]):
163 minimum[cpu][item] = temp_dict[item]
165 raise RuntimeError("mpstat: parse error", fields, line)
167 elif line and line[0] == 'Average:':
172 if len(fields) != CPULoad.MPSTAT_FIELD_SIZE:
173 raise RuntimeError("mpstat average: unexpected field\
177 cpu = 'cpu' if line[0] == 'all' else 'cpu' + line[0]
179 if values and len(values) == len(fields):
180 average[cpu] = dict(zip(fields, values))
182 raise RuntimeError("mpstat average: parse error",
185 return {'mpstat_maximun': maximum, 'mpstat_minimum': minimum,
186 'mpstat_average': average}
188 def _get_cpu_usage(self):
189 """Get processor usage from /proc/stat."""
190 fields = ['%usr', '%nice', '%sys', '%idle', '%iowait',
191 '%irq', '%soft', '%steal', '%guest', '%gnice']
193 cmd = "grep '^cpu[0-9 ].' /proc/stat"
195 if self.interval > 0:
196 previous = self._execute_command(cmd).splitlines()
197 time.sleep(self.interval)
198 current = self._execute_command(cmd).splitlines()
200 current = self._execute_command(cmd).splitlines()
205 for (prev, cur) in zip(previous, current):
207 # Split string to list tokens
208 cur_list = cur.split()
209 prev_list = prev.split()
213 cur_stats = map(int, cur_list[1:])
214 if self.interval > 0:
215 prev_stats = map(int, prev_list[1:])
217 prev_stats = [0] * len(cur_stats)
219 # NB: Don't add 'guest' and 'gnice' as they
220 # are already included in 'usr' and 'nice'.
221 uptime_prev = sum(prev_stats[0:8])
222 uptime_cur = sum(cur_stats[0:8])
224 # Remove 'guest' and 'gnice' from 'usr' and 'nice'
225 prev_stats[0] -= prev_stats[8]
226 prev_stats[1] -= prev_stats[9]
227 cur_stats[0] -= cur_stats[8]
228 cur_stats[1] -= cur_stats[9]
230 # number of samples (jiffies) in the interval
231 samples = (uptime_cur - uptime_prev) or 1
237 return "%.2f" % (100.0 * (x - y) / samples)
239 load = map(_percent, cur_stats, prev_stats)
241 mpstat[cpu] = dict(zip(fields, load))
243 return {'mpstat': mpstat}
245 def run(self, result):
246 """Read processor statistics."""
247 if not self.setup_done:
250 result.update(self._get_loadavg())
253 result.update(self._get_cpu_usage_mpstat())
255 result.update(self._get_cpu_usage())
257 # Note: No SLA as this scenario is only collecting statistics
260 # """internal test function."""
261 # import pkg_resources
262 # key_filename = pkg_resources.resource_filename('yardstick.resources',
263 # 'files/yardstick_key')
266 # 'ip': '172.16.0.175',
268 # 'key_filename': key_filename
272 # logger = logging.getLogger('yardstick')
273 # logger.setLevel(logging.DEBUG)
278 # p = CPULoad(args, ctx)
281 # print json.dumps(result)
283 # if __name__ == '__main__':