run ha test case in compass pod
[yardstick.git] / yardstick / benchmark / scenarios / compute / cachestat.py
1 ##############################################################################
2 # Copyright (c) 2016 Huawei Technologies Co.,Ltd and others.
3 #
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 ##############################################################################
9
10 """cache hit/miss ratio and usage statistics"""
11
12 from __future__ import absolute_import
13 import pkg_resources
14 import logging
15 import re
16 import yardstick.ssh as ssh
17
18 from yardstick.benchmark.scenarios import base
19 from six.moves import zip
20
21 LOG = logging.getLogger(__name__)
22
23
24 class CACHEstat(base.Scenario):
25     """Collect cache statistics.
26
27     This scenario reads system cache hit/miss ratio and other statistics on
28     a Linux host.
29
30     cache statistics are read using 'cachestat'.
31     cachestat - show Linux page cache hit/miss statistics.
32                 Uses Linux ftrace.
33
34     This is a proof of concept using Linux ftrace capabilities on older
35     kernels, and works by using function profiling for in-kernel counters.
36     Specifically, four kernel functions are traced:
37
38     mark_page_accessed() for measuring cache accesses
39     mark_buffer_dirty() for measuring cache writes
40     add_to_page_cache_lru() for measuring page additions
41     account_page_dirtied() for measuring page dirties
42
43     It is possible that these functions have been renamed (or are different
44     logically) for your kernel version, and this script will not work as-is.
45     This script was written on Linux 3.13. This script is a sandcastle: the
46     kernel may wash some away, and you'll need to rebuild.
47
48     USAGE: cachestat [-Dht] [interval]
49        eg,
50          cachestat 5    # show stats every 5 seconds
51
52     Run "cachestat -h" for full usage.
53
54     WARNING: This uses dynamic tracing of kernel functions, and could cause
55     kernel panics or freezes. Test, and know what you are doing, before use.
56     It also traces cache activity, which can be frequent, and cost some
57     overhead. The statistics should be treated as best-effort: there may be
58     some error margin depending on unusual workload types.
59
60     REQUIREMENTS: CONFIG_FUNCTION_PROFILER, awk.
61     """
62     __scenario_type__ = "CACHEstat"
63
64     TARGET_SCRIPT = "cache_stat.bash"
65
66     def __init__(self, scenario_cfg, context_cfg):
67         """Scenario construction."""
68         self.scenario_cfg = scenario_cfg
69         self.context_cfg = context_cfg
70         self.setup_done = False
71
72     def setup(self):
73         """Scenario setup."""
74         self.target_script = pkg_resources.resource_filename(
75             "yardstick.benchmark.scenarios.compute",
76             CACHEstat.TARGET_SCRIPT)
77
78         host = self.context_cfg['host']
79
80         self.client = ssh.SSH.from_node(host, defaults={"user": "ubuntu"})
81         self.client.wait(timeout=600)
82
83         # copy scripts to host
84         self.client._put_file_shell(self.target_script, '~/cache_stat.sh')
85
86         self.setup_done = True
87
88     def _execute_command(self, cmd):
89         """Execute a command on server."""
90         LOG.info("Executing: %s", cmd)
91         status, stdout, stderr = self.client.execute(cmd)
92         if status:
93             raise RuntimeError("Failed executing command: ",
94                                cmd, stderr)
95         return stdout
96
97     def _filtrate_result(self, result):
98         fields = []
99         cachestat = {}
100         data_marker = re.compile(r"\d+")
101         ite = 0
102         average = {'HITS': 0, 'MISSES': 0, 'DIRTIES': 0, 'RATIO': 0,
103                    'BUFFERS_MB': 0, 'CACHE_MB': 0}
104         maximum = {'HITS': 0, 'MISSES': 0, 'DIRTIES': 0, 'RATIO': 0,
105                    'BUFFERS_MB': 0, 'CACHE_MB': 0}
106
107         # Parse cache stats
108         for row in result.split('\n'):
109             line = row.split()
110
111             if line and line[0] == 'HITS':
112                 # header fields
113                 fields = line[:]
114             elif line and re.match(data_marker, line[0]):
115                 cache = 'cache' + str(ite)
116                 ite += 1
117                 values = line[:]
118                 if values and len(values) == len(fields):
119                     cachestat[cache] = dict(list(zip(fields, values)))
120
121         for entry in cachestat:
122             for item in average:
123                 if item != 'RATIO':
124                     average[item] += int(cachestat[entry][item])
125                 else:
126                     average[item] += float(cachestat[entry][item][:-1])
127
128             for item in maximum:
129                 if item != 'RATIO':
130                     if int(cachestat[entry][item]) > maximum[item]:
131                         maximum[item] = int(cachestat[entry][item])
132                 else:
133                     if float(cachestat[entry][item][:-1]) > maximum[item]:
134                         maximum[item] = float(cachestat[entry][item][:-1])
135
136         for item in average:
137             if item != 'RATIO':
138                 average[item] = average[item] / len(cachestat)
139             else:
140                 average[item] = str(average[item] / len(cachestat)) + '%'
141
142         return {'cachestat': cachestat, 'average': average, 'max': maximum}
143
144     def _get_cache_usage(self):
145         """Get cache statistics."""
146         options = self.scenario_cfg['options']
147         interval = options.get("interval", 1)
148
149         cmd = "sudo bash cache_stat.sh %s" % (interval)
150
151         result = self._execute_command(cmd)
152         filtrated_result = self._filtrate_result(result)
153
154         return filtrated_result
155
156     def run(self, result):
157         if not self.setup_done:
158             self.setup()
159
160         result.update(self._get_cache_usage())