Merge "contexts/node: default to pod.yaml"
[yardstick.git] / yardstick / benchmark / scenarios / networking / netutilization.py
1 ##############################################################################
2 # Copyright (c) 2016 Huawei Technologies Co.,Ltd and other.
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 import logging
10 import re
11
12 import yardstick.ssh as ssh
13 from yardstick.benchmark.scenarios import base
14
15 LOG = logging.getLogger(__name__)
16
17
18 class NetUtilization(base.Scenario):
19     """Collect network utilization statistics.
20
21     This scenario reads statistics from the network devices on a Linux host.
22     Network utilization statistics are read using the utility 'sar'.
23
24     The following values are displayed:
25
26     IFACE: Name of the network interface for which statistics are reported.
27
28     rxpck/s: Total number of packets received per second.
29
30     txpck/s: Total number of packets transmitted per second.
31
32     rxkB/s: Total number of kilobytes received per second.
33
34     txkB/s: Total number of kilobytes transmitted per second.
35
36     rxcmp/s: Number of compressed packets received per second (for cslip etc.).
37
38     txcmp/s: Number of compressed packets transmitted per second.
39
40     rxmcst/s: Number of multicast packets received per second.
41
42     %ifutil: Utilization percentage of the network interface. For half-duplex
43     interfaces, utilization is calculated using the sum of rxkB/s and txkB/s
44     as a percentage of the interface speed. For full-duplex, this is the
45     greater  of rxkB/S or txkB/s.
46
47     Parameters
48         interval - Time interval to measure network utilization.
49             type:       [int]
50             unit:       seconds
51             default:    1
52
53         count - Number of times to measure network utilization.
54             type:       [int]
55             unit:       N/A
56             default:    1
57     """
58
59     __scenario_type__ = "NetUtilization"
60
61     NET_UTILIZATION_FIELD_SIZE = 8
62
63     def __init__(self, scenario_cfg, context_cfg):
64         """Scenario construction."""
65         self.scenario_cfg = scenario_cfg
66         self.context_cfg = context_cfg
67         self.setup_done = False
68
69     def setup(self):
70         """Scenario setup."""
71         host = self.context_cfg['host']
72         user = host.get('user', 'ubuntu')
73         ssh_port = host.get("ssh_port", ssh.DEFAULT_PORT)
74         ip = host.get('ip', None)
75         key_filename = host.get('key_filename', '~/.ssh/id_rsa')
76
77         LOG.info("user:%s, host:%s", user, ip)
78         self.client = ssh.SSH(user, ip, key_filename=key_filename,
79                               port=ssh_port)
80         self.client.wait(timeout=600)
81
82         self.setup_done = True
83
84     def _execute_command(self, cmd):
85         """Execute a command on target."""
86         LOG.info("Executing: %s" % cmd)
87         status, stdout, stderr = self.client.execute(cmd)
88         if status:
89             raise RuntimeError("Failed executing command: ",
90                                cmd, stderr)
91         return stdout
92
93     def _filtrate_result(self, raw_result):
94         """Filtrate network utilization statistics."""
95         fields = []
96         maximum = {}
97         minimum = {}
98         average = {}
99
100         time_marker = re.compile("^([0-9]+):([0-9]+):([0-9]+)$")
101         ampm_marker = re.compile("(AM|PM)$")
102
103         # Parse network utilization stats
104         for row in raw_result.split('\n'):
105             line = row.split()
106
107             if line and re.match(time_marker, line[0]):
108                 if re.match(ampm_marker, line[1]):
109                     del line[:2]
110
111                 if line[0] == 'IFACE':
112                     # header fields
113                     fields = line[1:]
114                     if len(fields) != NetUtilization.\
115                             NET_UTILIZATION_FIELD_SIZE:
116                         raise RuntimeError("network_utilization: unexpected\
117                                            field size", fields)
118                 else:
119                     # value fields
120                     net_interface = line[0]
121                     values = line[1:]
122
123                     if values and len(values) == len(fields):
124                         temp_dict = dict(zip(fields, values))
125                         if net_interface not in maximum:
126                             maximum[net_interface] = temp_dict
127                         else:
128                             for item in temp_dict:
129                                 if float(maximum[net_interface][item]) <\
130                                    float(temp_dict[item]):
131                                     maximum[net_interface][item] = \
132                                         temp_dict[item]
133
134                         if net_interface not in minimum:
135                             minimum[net_interface] = temp_dict
136                         else:
137                             for item in temp_dict:
138                                 if float(minimum[net_interface][item]) >\
139                                    float(temp_dict[item]):
140                                     minimum[net_interface][item] = \
141                                         temp_dict[item]
142                     else:
143                         raise RuntimeError("network_utilization: parse error",
144                                            fields, line)
145
146             elif line and line[0] == 'Average:':
147                 del line[:1]
148
149                 if line[0] == 'IFACE':
150                     # header fields
151                     fields = line[1:]
152                     if len(fields) != NetUtilization.\
153                             NET_UTILIZATION_FIELD_SIZE:
154                         raise RuntimeError("network_utilization average: \
155                                            unexpected field size", fields)
156                 else:
157                     # value fields
158                     net_interface = line[0]
159                     values = line[1:]
160                     if values and len(values) == len(fields):
161                         average[net_interface] = dict(zip(fields, values))
162                     else:
163                         raise RuntimeError("network_utilization average: \
164                                            parse error", fields, line)
165
166         return {'network_utilization_maximun': maximum,
167                 'network_utilization_minimum': minimum,
168                 'network_utilization_average': average}
169
170     def _get_network_utilization(self):
171         """Get network utilization statistics using sar."""
172         options = self.scenario_cfg["options"]
173         interval = options.get('interval', 1)
174         count = options.get('count', 1)
175
176         cmd = "sudo sar -n DEV %d %d" % (interval, count)
177
178         raw_result = self._execute_command(cmd)
179         result = self._filtrate_result(raw_result)
180
181         return result
182
183     def run(self, result):
184         """Read statistics."""
185         if not self.setup_done:
186             self.setup()
187
188         result.update(self._get_network_utilization())