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. A value of 0
40 indicates that processors statistics are to be
41 reported for the time since system startup (boot)
49 __scenario_type__ = "CPUload"
51 MPSTAT_FIELD_SIZE = 10
53 def __init__(self, scenario_cfg, context_cfg):
54 """Scenario construction."""
55 self.scenario_cfg = scenario_cfg
56 self.context_cfg = context_cfg
57 self.setup_done = False
58 self.has_mpstat = False
62 host = self.context_cfg['host']
63 user = host.get('user', 'ubuntu')
64 ip = host.get('ip', None)
65 key_filename = host.get('key_filename', '~/.ssh/id_rsa')
67 LOG.info("user:%s, host:%s", user, ip)
68 self.client = ssh.SSH(user, ip, key_filename=key_filename)
69 self.client.wait(timeout=600)
71 # Check if mpstat prog is installed
72 status, _, _ = self.client.execute("mpstat -V >/dev/null 2>&1")
74 LOG.info("MPSTAT is NOT installed")
75 self.has_mpstat = False
77 LOG.info("MPSTAT is installed")
78 self.has_mpstat = True
80 if 'options' in self.scenario_cfg:
81 self.interval = self.scenario_cfg['options'].get("interval", 0)
85 self.setup_done = True
87 def _execute_command(self, cmd):
88 """Execute a command on server."""
89 LOG.info("Executing: %s" % cmd)
90 status, stdout, stderr = self.client.execute(cmd)
92 raise RuntimeError("Failed executing command: ",
96 def _get_loadavg(self):
97 """Get system load."""
98 return {'loadavg': self._execute_command("cat /proc/loadavg").split()}
100 def _get_cpu_usage_mpstat(self):
101 """Get processor usage using mpstat."""
102 if self.interval > 0:
103 cmd = "mpstat -P ON %s 1" % self.interval
107 result = self._execute_command(cmd)
112 time_marker = re.compile("^([0-9]+):([0-9]+):([0-9]+)$")
113 ampm_marker = re.compile("(AM|PM)$")
116 for row in result.split('\n'):
119 if line and re.match(time_marker, line[0]):
121 if re.match(ampm_marker, line[1]):
129 if len(fields) != CPULoad.MPSTAT_FIELD_SIZE:
130 raise RuntimeError("mpstat: unexpected field size",
134 cpu = 'cpu' if line[0] == 'all' else 'cpu' + line[0]
136 if values and len(values) == len(fields):
137 mpstat[cpu] = dict(zip(fields, values))
139 raise RuntimeError("mpstat: parse error", fields, line)
141 return {'mpstat': mpstat}
143 def _get_cpu_usage(self):
144 """Get processor usage from /proc/stat."""
145 fields = ['%usr', '%nice', '%sys', '%idle', '%iowait',
146 '%irq', '%soft', '%steal', '%guest', '%gnice']
148 cmd = "grep '^cpu[0-9 ].' /proc/stat"
150 if self.interval > 0:
151 previous = self._execute_command(cmd).splitlines()
152 time.sleep(self.interval)
153 current = self._execute_command(cmd).splitlines()
155 current = self._execute_command(cmd).splitlines()
160 for (prev, cur) in zip(previous, current):
162 # Split string to list tokens
163 cur_list = cur.split()
164 prev_list = prev.split()
168 cur_stats = map(int, cur_list[1:])
169 if self.interval > 0:
170 prev_stats = map(int, prev_list[1:])
172 prev_stats = [0] * len(cur_stats)
174 # NB: Don't add 'guest' and 'gnice' as they
175 # are already included in 'usr' and 'nice'.
176 uptime_prev = sum(prev_stats[0:8])
177 uptime_cur = sum(cur_stats[0:8])
179 # Remove 'guest' and 'gnice' from 'usr' and 'nice'
180 prev_stats[0] -= prev_stats[8]
181 prev_stats[1] -= prev_stats[9]
182 cur_stats[0] -= cur_stats[8]
183 cur_stats[1] -= cur_stats[9]
185 # number of samples (jiffies) in the interval
186 samples = (uptime_cur - uptime_prev) or 1
192 return "%.2f" % (100.0 * (x - y) / samples)
194 load = map(_percent, cur_stats, prev_stats)
196 mpstat[cpu] = dict(zip(fields, load))
198 return {'mpstat': mpstat}
200 def run(self, result):
201 """Read processor statistics."""
202 if not self.setup_done:
205 result.update(self._get_loadavg())
208 result.update(self._get_cpu_usage_mpstat())
210 result.update(self._get_cpu_usage())
212 # Note: No SLA as this scenario is only collecting statistics
215 # """internal test function."""
216 # import pkg_resources
217 # key_filename = pkg_resources.resource_filename('yardstick.resources',
218 # 'files/yardstick_key')
221 # 'ip': '172.16.0.175',
222 # 'user': 'ec2-user',
223 # 'key_filename': key_filename
227 # logger = logging.getLogger('yardstick')
228 # logger.setLevel(logging.DEBUG)
233 # p = CPULoad(args, ctx)
236 # print json.dumps(result)
238 # if __name__ == '__main__':