Merge "support more parameters in iperf3 testcase"
[yardstick.git] / yardstick / common / utils.py
1 # Copyright 2013: Mirantis Inc.
2 # All Rights Reserved.
3 #
4 #    Licensed under the Apache License, Version 2.0 (the "License"); you may
5 #    not use this file except in compliance with the License. You may obtain
6 #    a copy of the License at
7 #
8 #         http://www.apache.org/licenses/LICENSE-2.0
9 #
10 #    Unless required by applicable law or agreed to in writing, software
11 #    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12 #    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13 #    License for the specific language governing permissions and limitations
14 #    under the License.
15
16 # yardstick comment: this is a modified copy of rally/rally/common/utils.py
17
18 from __future__ import absolute_import
19 from __future__ import print_function
20
21 import errno
22 import logging
23 import os
24 import subprocess
25 import sys
26 import collections
27 import socket
28 import random
29 from functools import reduce
30 from contextlib import closing
31
32 import yaml
33 import six
34 from flask import jsonify
35 from six.moves import configparser
36 from oslo_utils import importutils
37 from oslo_serialization import jsonutils
38
39 import yardstick
40
41 logger = logging.getLogger(__name__)
42 logger.setLevel(logging.DEBUG)
43
44
45 # Decorator for cli-args
46 def cliargs(*args, **kwargs):
47     def _decorator(func):
48         func.__dict__.setdefault('arguments', []).insert(0, (args, kwargs))
49         return func
50     return _decorator
51
52
53 def itersubclasses(cls, _seen=None):
54     """Generator over all subclasses of a given class in depth first order."""
55
56     if not isinstance(cls, type):
57         raise TypeError("itersubclasses must be called with "
58                         "new-style classes, not %.100r" % cls)
59     _seen = _seen or set()
60     try:
61         subs = cls.__subclasses__()
62     except TypeError:   # fails only when cls is type
63         subs = cls.__subclasses__(cls)
64     for sub in subs:
65         if sub not in _seen:
66             _seen.add(sub)
67             yield sub
68             for sub in itersubclasses(sub, _seen):
69                 yield sub
70
71
72 def try_append_module(name, modules):
73     if name not in modules:
74         modules[name] = importutils.import_module(name)
75
76
77 def import_modules_from_package(package):
78     """Import modules from package and append into sys.modules
79
80     :param: package - Full package name. For example: rally.deploy.engines
81     """
82     path = [os.path.dirname(yardstick.__file__), ".."] + package.split(".")
83     path = os.path.join(*path)
84     for root, dirs, files in os.walk(path):
85         for filename in files:
86             if filename.startswith("__") or not filename.endswith(".py"):
87                 continue
88             new_package = ".".join(root.split(os.sep)).split("....")[1]
89             module_name = "%s.%s" % (new_package, filename[:-3])
90             try:
91                 try_append_module(module_name, sys.modules)
92             except ImportError:
93                 logger.exception("unable to import %s", module_name)
94
95
96 def parse_yaml(file_path):
97     try:
98         with open(file_path) as f:
99             value = yaml.safe_load(f)
100     except IOError:
101         return {}
102     except OSError as e:
103         if e.errno != errno.EEXIST:
104             raise
105     else:
106         return value
107
108
109 def get_param(key, default=''):
110
111     conf_file = os.environ.get('CONF_FILE', '/etc/yardstick/yardstick.yaml')
112
113     conf = parse_yaml(conf_file)
114     try:
115         return reduce(lambda a, b: a[b], key.split('.'), conf)
116     except KeyError:
117         if not default:
118             raise
119         return default
120
121
122 def makedirs(d):
123     try:
124         os.makedirs(d)
125     except OSError as e:
126         if e.errno != errno.EEXIST:
127             raise
128
129
130 def remove_file(path):
131     try:
132         os.remove(path)
133     except OSError as e:
134         if e.errno != errno.ENOENT:
135             raise
136
137
138 def execute_command(cmd):
139     exec_msg = "Executing command: '%s'" % cmd
140     logger.debug(exec_msg)
141
142     output = subprocess.check_output(cmd.split()).split(os.linesep)
143
144     return output
145
146
147 def source_env(env_file):
148     p = subprocess.Popen(". %s; env" % env_file, stdout=subprocess.PIPE,
149                          shell=True)
150     output = p.communicate()[0]
151     env = dict((line.split('=', 1) for line in output.splitlines()))
152     os.environ.update(env)
153     return env
154
155
156 def read_json_from_file(path):
157     with open(path, 'r') as f:
158         j = f.read()
159     # don't use jsonutils.load() it conflicts with already decoded input
160     return jsonutils.loads(j)
161
162
163 def write_json_to_file(path, data, mode='w'):
164     with open(path, mode) as f:
165         jsonutils.dump(data, f)
166
167
168 def write_file(path, data, mode='w'):
169     with open(path, mode) as f:
170         f.write(data)
171
172
173 def parse_ini_file(path):
174     parser = configparser.ConfigParser()
175
176     try:
177         files = parser.read(path)
178     except configparser.MissingSectionHeaderError:
179         logger.exception('invalid file type')
180         raise
181     else:
182         if not files:
183             raise RuntimeError('file not exist')
184
185     try:
186         default = {k: v for k, v in parser.items('DEFAULT')}
187     except configparser.NoSectionError:
188         default = {}
189
190     config = dict(DEFAULT=default,
191                   **{s: {k: v for k, v in parser.items(
192                       s)} for s in parser.sections()})
193
194     return config
195
196
197 def get_port_mac(sshclient, port):
198     cmd = "ifconfig |grep HWaddr |grep %s |awk '{print $5}' " % port
199     status, stdout, stderr = sshclient.execute(cmd)
200
201     if status:
202         raise RuntimeError(stderr)
203     return stdout.rstrip()
204
205
206 def get_port_ip(sshclient, port):
207     cmd = "ifconfig %s |grep 'inet addr' |awk '{print $2}' " \
208         "|cut -d ':' -f2 " % port
209     status, stdout, stderr = sshclient.execute(cmd)
210
211     if status:
212         raise RuntimeError(stderr)
213     return stdout.rstrip()
214
215
216 def flatten_dict_key(data):
217     next_data = {}
218
219     # use list, because iterable is too generic
220     if not any(isinstance(v, (collections.Mapping, list))
221                for v in data.values()):
222         return data
223
224     for k, v in six.iteritems(data):
225         if isinstance(v, collections.Mapping):
226             for n_k, n_v in six.iteritems(v):
227                 next_data["%s.%s" % (k, n_k)] = n_v
228         # use list because iterable is too generic
229         elif isinstance(v, list):
230             for index, item in enumerate(v):
231                 next_data["%s%d" % (k, index)] = item
232         else:
233             next_data[k] = v
234
235     return flatten_dict_key(next_data)
236
237
238 def translate_to_str(obj):
239     if isinstance(obj, collections.Mapping):
240         return {str(k): translate_to_str(v) for k, v in obj.items()}
241     elif isinstance(obj, list):
242         return [translate_to_str(ele) for ele in obj]
243     elif isinstance(obj, six.text_type):
244         return str(obj)
245     return obj
246
247
248 def result_handler(status, data):
249     result = {
250         'status': status,
251         'result': data
252     }
253     return jsonify(result)
254
255
256 def change_obj_to_dict(obj):
257     dic = {}
258     for k, v in vars(obj).items():
259         try:
260             vars(v)
261         except TypeError:
262             dic.update({k: v})
263     return dic
264
265
266 def set_dict_value(dic, keys, value):
267     return_dic = dic
268
269     for key in keys.split('.'):
270
271         return_dic.setdefault(key, {})
272         if key == keys.split('.')[-1]:
273             return_dic[key] = value
274         else:
275             return_dic = return_dic[key]
276     return dic
277
278
279 def get_free_port(ip):
280     with closing(socket.socket(socket.AF_INET, socket.SOCK_STREAM)) as s:
281         while True:
282             port = random.randint(5000, 10000)
283             if s.connect_ex((ip, port)) != 0:
284                 return port