aboutsummaryrefslogtreecommitdiffstats
path: root/yardstick/benchmark/scenarios/networking/netutilization.py
blob: cecb64fa0118981bbd23fef8ec3397a55386417a (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
##############################################################################
# Copyright (c) 2016 Huawei Technologies Co.,Ltd and other.
#
# All rights reserved. This program and the accompanying materials
# are made available under the terms of the Apache License, Version 2.0
# which accompanies this distribution, and is available at
# http://www.apache.org/licenses/LICENSE-2.0
##############################################################################
from __future__ import absolute_import
import logging
import re

import yardstick.ssh as ssh
from yardstick.benchmark.scenarios import base
from six.moves import zip

LOG = logging.getLogger(__name__)


class NetUtilization(base.Scenario):
    """Collect network utilization statistics.

    This scenario reads statistics from the network devices on a Linux host.
    Network utilization statistics are read using the utility 'sar'.

    The following values are displayed:

    IFACE: Name of the network interface for which statistics are reported.

    rxpck/s: Total number of packets received per second.

    txpck/s: Total number of packets transmitted per second.

    rxkB/s: Total number of kilobytes received per second.

    txkB/s: Total number of kilobytes transmitted per second.

    rxcmp/s: Number of compressed packets received per second (for cslip etc.).

    txcmp/s: Number of compressed packets transmitted per second.

    rxmcst/s: Number of multicast packets received per second.

    %ifutil: Utilization percentage of the network interface. For half-duplex
    interfaces, utilization is calculated using the sum of rxkB/s and txkB/s
    as a percentage of the interface speed. For full-duplex, this is the
    greater  of rxkB/S or txkB/s.

    Parameters
        interval - Time interval to measure network utilization.
            type:       [int]
            unit:       seconds
            default:    1

        count - Number of times to measure network utilization.
            type:       [int]
            unit:       N/A
            default:    1
    """

    __scenario_type__ = "NetUtilization"

    NET_UTILIZATION_FIELD_SIZE = 8

    def __init__(self, scenario_cfg, context_cfg):
        """Scenario construction."""
        self.scenario_cfg = scenario_cfg
        self.context_cfg = context_cfg
        self.setup_done = False

    def setup(self):
        """Scenario setup."""
        host = self.context_cfg['host']

        self.client = ssh.SSH.from_node(host, defaults={"user": "ubuntu"})
        self.client.wait(timeout=600)

        self.setup_done = True

    def _execute_command(self, cmd):
        """Execute a command on target."""
        LOG.info("Executing: %s", cmd)
        status, stdout, stderr = self.client.execute(cmd)
        if status:
            raise RuntimeError("Failed executing command: ",
                               cmd, stderr)
        return stdout

    def _filtrate_result(self, raw_result):
        """Filtrate network utilization statistics."""
        fields = []
        maximum = {}
        minimum = {}
        average = {}

        time_marker = re.compile("^([0-9]+):([0-9]+):([0-9]+)$")

        # Parse network utilization stats
        for row in raw_result.splitlines():
            line = row.split()

            if line and re.match(time_marker, line[0]):

                try:
                    index = line.index('IFACE')
                except ValueError:
                    del line[:index]
                    net_interface = line[0]
                    values = line[1:]

                    if values and len(values) == len(fields):
                        temp_dict = dict(zip(fields, values))
                        if net_interface not in maximum:
                            maximum[net_interface] = temp_dict
                        else:
                            for item in temp_dict:
                                if float(maximum[net_interface][item]) <\
                                   float(temp_dict[item]):
                                    maximum[net_interface][item] = \
                                        temp_dict[item]

                        if net_interface not in minimum:
                            minimum[net_interface] = temp_dict
                        else:
                            for item in temp_dict:
                                if float(minimum[net_interface][item]) >\
                                   float(temp_dict[item]):
                                    minimum[net_interface][item] = \
                                        temp_dict[item]
                    else:
                        raise RuntimeError("network_utilization: parse error",
                                           fields, line)
                else:
                    del line[:index]
                    fields = line[1:]
                    if len(fields) != NetUtilization.\
                            NET_UTILIZATION_FIELD_SIZE:
                        raise RuntimeError("network_utilization: unexpected\
                                           field size", fields)

            elif line and line[0] == 'Average:':
                del line[:1]

                if line[0] == 'IFACE':
                    # header fields
                    fields = line[1:]
                    if len(fields) != NetUtilization.\
                            NET_UTILIZATION_FIELD_SIZE:
                        raise RuntimeError("network_utilization average: \
                                           unexpected field size", fields)
                else:
                    # value fields
                    net_interface = line[0]
                    values = line[1:]
                    if values and len(values) == len(fields):
                        average[net_interface] = dict(
                            zip(fields, values))
                    else:
                        raise RuntimeError("network_utilization average: \
                                           parse error", fields, line)

        return {'network_utilization_maximun': maximum,
                'network_utilization_minimum': minimum,
                'network_utilization_average': average}

    def _get_network_utilization(self):
        """Get network utilization statistics using sar."""
        options = self.scenario_cfg["options"]
        interval = options.get('interval', 1)
        count = options.get('count', 1)

        cmd = "sudo sar -n DEV %d %d" % (interval, count)

        raw_result = self._execute_command(cmd)
        result = self._filtrate_result(raw_result)

        return result

    def run(self, result):
        """Read statistics."""
        if not self.setup_done:
            self.setup()

        result.update(self._get_network_utilization())