Merge "tg: speedup unittests, mock time.sleep"
[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 from functools import reduce
28
29 import yaml
30 import six
31 from flask import jsonify
32 from six.moves import configparser
33 from oslo_utils import importutils
34 from oslo_serialization import jsonutils
35
36 import yardstick
37
38 logger = logging.getLogger(__name__)
39 logger.setLevel(logging.DEBUG)
40
41
42 # Decorator for cli-args
43 def cliargs(*args, **kwargs):
44     def _decorator(func):
45         func.__dict__.setdefault('arguments', []).insert(0, (args, kwargs))
46         return func
47     return _decorator
48
49
50 def itersubclasses(cls, _seen=None):
51     """Generator over all subclasses of a given class in depth first order."""
52
53     if not isinstance(cls, type):
54         raise TypeError("itersubclasses must be called with "
55                         "new-style classes, not %.100r" % cls)
56     _seen = _seen or set()
57     try:
58         subs = cls.__subclasses__()
59     except TypeError:   # fails only when cls is type
60         subs = cls.__subclasses__(cls)
61     for sub in subs:
62         if sub not in _seen:
63             _seen.add(sub)
64             yield sub
65             for sub in itersubclasses(sub, _seen):
66                 yield sub
67
68
69 def try_append_module(name, modules):
70     if name not in modules:
71         modules[name] = importutils.import_module(name)
72
73
74 def import_modules_from_package(package):
75     """Import modules from package and append into sys.modules
76
77     :param: package - Full package name. For example: rally.deploy.engines
78     """
79     path = [os.path.dirname(yardstick.__file__), ".."] + package.split(".")
80     path = os.path.join(*path)
81     for root, dirs, files in os.walk(path):
82         for filename in files:
83             if filename.startswith("__") or not filename.endswith(".py"):
84                 continue
85             new_package = ".".join(root.split(os.sep)).split("....")[1]
86             module_name = "%s.%s" % (new_package, filename[:-3])
87             try:
88                 try_append_module(module_name, sys.modules)
89             except ImportError:
90                 logger.exception("unable to import %s", module_name)
91
92
93 def parse_yaml(file_path):
94     try:
95         with open(file_path) as f:
96             value = yaml.safe_load(f)
97     except IOError:
98         return {}
99     except OSError as e:
100         if e.errno != errno.EEXIST:
101             raise
102     else:
103         return value
104
105
106 def get_param(key, default=''):
107
108     conf_file = os.environ.get('CONF_FILE', '/etc/yardstick/yardstick.yaml')
109
110     conf = parse_yaml(conf_file)
111     try:
112         return reduce(lambda a, b: a[b], key.split('.'), conf)
113     except KeyError:
114         if not default:
115             raise
116         return default
117
118
119 def makedirs(d):
120     try:
121         os.makedirs(d)
122     except OSError as e:
123         if e.errno != errno.EEXIST:
124             raise
125
126
127 def remove_file(path):
128     try:
129         os.remove(path)
130     except OSError as e:
131         if e.errno != errno.ENOENT:
132             raise
133
134
135 def execute_command(cmd):
136     exec_msg = "Executing command: '%s'" % cmd
137     logger.debug(exec_msg)
138
139     output = subprocess.check_output(cmd.split()).split(os.linesep)
140
141     return output
142
143
144 def source_env(env_file):
145     p = subprocess.Popen(". %s; env" % env_file, stdout=subprocess.PIPE,
146                          shell=True)
147     output = p.communicate()[0]
148     env = dict((line.split('=', 1) for line in output.splitlines()))
149     os.environ.update(env)
150     return env
151
152
153 def read_json_from_file(path):
154     with open(path, 'r') as f:
155         j = f.read()
156     # don't use jsonutils.load() it conflicts with already decoded input
157     return jsonutils.loads(j)
158
159
160 def write_json_to_file(path, data, mode='w'):
161     with open(path, mode) as f:
162         jsonutils.dump(data, f)
163
164
165 def write_file(path, data, mode='w'):
166     with open(path, mode) as f:
167         f.write(data)
168
169
170 def parse_ini_file(path):
171     parser = configparser.ConfigParser()
172     parser.read(path)
173
174     try:
175         default = {k: v for k, v in parser.items('DEFAULT')}
176     except configparser.NoSectionError:
177         default = {}
178
179     config = dict(DEFAULT=default,
180                   **{s: {k: v for k, v in parser.items(
181                       s)} for s in parser.sections()})
182
183     return config
184
185
186 def get_port_mac(sshclient, port):
187     cmd = "ifconfig |grep HWaddr |grep %s |awk '{print $5}' " % port
188     status, stdout, stderr = sshclient.execute(cmd)
189
190     if status:
191         raise RuntimeError(stderr)
192     return stdout.rstrip()
193
194
195 def get_port_ip(sshclient, port):
196     cmd = "ifconfig %s |grep 'inet addr' |awk '{print $2}' " \
197         "|cut -d ':' -f2 " % port
198     status, stdout, stderr = sshclient.execute(cmd)
199
200     if status:
201         raise RuntimeError(stderr)
202     return stdout.rstrip()
203
204
205 def flatten_dict_key(data):
206     next_data = {}
207
208     # use list, because iterable is too generic
209     if not any(isinstance(v, (collections.Mapping, list))
210                for v in data.values()):
211         return data
212
213     for k, v in six.iteritems(data):
214         if isinstance(v, collections.Mapping):
215             for n_k, n_v in six.iteritems(v):
216                 next_data["%s.%s" % (k, n_k)] = n_v
217         # use list because iterable is too generic
218         elif isinstance(v, list):
219             for index, item in enumerate(v):
220                 next_data["%s%d" % (k, index)] = item
221         else:
222             next_data[k] = v
223
224     return flatten_dict_key(next_data)
225
226
227 def translate_to_str(obj):
228     if isinstance(obj, collections.Mapping):
229         return {str(k): translate_to_str(v) for k, v in obj.items()}
230     elif isinstance(obj, list):
231         return [translate_to_str(ele) for ele in obj]
232     elif isinstance(obj, six.text_type):
233         return str(obj)
234     return obj
235
236
237 def result_handler(status, data):
238     result = {
239         'status': status,
240         'result': data
241     }
242     return jsonify(result)
243
244
245 def change_obj_to_dict(obj):
246     dic = {}
247     for k, v in vars(obj).items():
248         try:
249             vars(v)
250         except TypeError:
251             dic.update({k: v})
252     return dic
253
254
255 def set_dict_value(dic, keys, value):
256     return_dic = dic
257
258     for key in keys.split('.'):
259
260         return_dic.setdefault(key, {})
261         if key == keys.split('.')[-1]:
262             return_dic[key] = value
263         else:
264             return_dic = return_dic[key]
265     return dic