• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# Copyright 2016 Google Inc.
2#
3# Licensed under the Apache License, Version 2.0 (the "License");
4# you may not use this file except in compliance with the License.
5# You may obtain a copy of the License at
6#
7#     http://www.apache.org/licenses/LICENSE-2.0
8#
9# Unless required by applicable law or agreed to in writing, software
10# distributed under the License is distributed on an "AS IS" BASIS,
11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12# See the License for the specific language governing permissions and
13# limitations under the License.
14
15import io
16import json
17import logging
18import os
19
20from mobly import utils
21
22MOBLY_CONTROLLER_CONFIG_NAME = 'IPerfServer'
23
24
25def create(configs):
26  results = []
27  for c in configs:
28    try:
29      results.append(IPerfServer(c, logging.log_path))
30    except:
31      pass
32  return results
33
34
35def destroy(objs):
36  for ipf in objs:
37    try:
38      ipf.stop()
39    except:
40      pass
41
42
43class IPerfResult:
44
45  def __init__(self, result_path):
46    try:
47      with io.open(result_path, 'r', encoding='utf-8') as f:
48        self.result = json.load(f)
49    except ValueError:
50      with io.open(result_path, 'r', encoding='utf-8') as f:
51        # Possibly a result from interrupted iperf run, skip first line
52        # and try again.
53        lines = f.readlines()[1:]
54        self.result = json.loads(''.join(lines))
55
56  def _has_data(self):
57    """Checks if the iperf result has valid throughput data.
58
59    Returns:
60        True if the result contains throughput data. False otherwise.
61    """
62    return ('end' in self.result) and ('sum' in self.result['end'])
63
64  def get_json(self):
65    """
66    Returns:
67        The raw json output from iPerf.
68    """
69    return self.result
70
71  @property
72  def error(self):
73    if 'error' not in self.result:
74      return None
75    return self.result['error']
76
77  @property
78  def avg_rate(self):
79    """Average receiving rate in MB/s over the entire run.
80
81    If the result is not from a success run, this property is None.
82    """
83    if not self._has_data or 'sum' not in self.result['end']:
84      return None
85    bps = self.result['end']['sum']['bits_per_second']
86    return bps / 8 / 1024 / 1024
87
88  @property
89  def avg_receive_rate(self):
90    """Average receiving rate in MB/s over the entire run. This data may
91    not exist if iperf was interrupted.
92
93    If the result is not from a success run, this property is None.
94    """
95    if not self._has_data or 'sum_received' not in self.result['end']:
96      return None
97    bps = self.result['end']['sum_received']['bits_per_second']
98    return bps / 8 / 1024 / 1024
99
100  @property
101  def avg_send_rate(self):
102    """Average sending rate in MB/s over the entire run. This data may
103    not exist if iperf was interrupted.
104
105    If the result is not from a success run, this property is None.
106    """
107    if not self._has_data or 'sum_sent' not in self.result['end']:
108      return None
109    bps = self.result['end']['sum_sent']['bits_per_second']
110    return bps / 8 / 1024 / 1024
111
112
113class IPerfServer:
114  """Class that handles iperf3 operations."""
115
116  def __init__(self, port, log_path):
117    self.port = port
118    self.log_path = os.path.join(log_path, 'iPerf{}'.format(self.port))
119    self.iperf_str = 'iperf3 -s -J -p {}'.format(port)
120    self.iperf_process = None
121    self.log_files = []
122    self.started = False
123
124  def start(self, extra_args='', tag=''):
125    """Starts iperf server on specified port.
126
127    Args:
128        extra_args: A string representing extra arguments to start iperf
129            server with.
130        tag: Appended to log file name to identify logs from different
131            iperf runs.
132    """
133    if self.started:
134      return
135    utils.create_dir(self.log_path)
136    if tag:
137      tag = tag + ','
138    out_file_name = 'IPerfServer,{},{}{}.log'.format(
139        self.port, tag, len(self.log_files)
140    )
141    full_out_path = os.path.join(self.log_path, out_file_name)
142    cmd = '%s %s > %s' % (self.iperf_str, extra_args, full_out_path)
143    self.iperf_process = utils.start_standing_subprocess(cmd, shell=True)
144    self.log_files.append(full_out_path)
145    self.started = True
146
147  def stop(self):
148    if self.started:
149      utils.stop_standing_subprocess(self.iperf_process)
150      self.started = False
151