Merge "Add API(v2) to add case to task"
[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     parser.read(path)
176
177     try:
178         default = {k: v for k, v in parser.items('DEFAULT')}
179     except configparser.NoSectionError:
180         default = {}
181
182     config = dict(DEFAULT=default,
183                   **{s: {k: v for k, v in parser.items(
184                       s)} for s in parser.sections()})
185
186     return config
187
188
189 def get_port_mac(sshclient, port):
190     cmd = "ifconfig |grep HWaddr |grep %s |awk '{print $5}' " % port
191     status, stdout, stderr = sshclient.execute(cmd)
192
193     if status:
194         raise RuntimeError(stderr)
195     return stdout.rstrip()
196
197
198 def get_port_ip(sshclient, port):
199     cmd = "ifconfig %s |grep 'inet addr' |awk '{print $2}' " \
200         "|cut -d ':' -f2 " % port
201     status, stdout, stderr = sshclient.execute(cmd)
202
203     if status:
204         raise RuntimeError(stderr)
205     return stdout.rstrip()
206
207
208 def flatten_dict_key(data):
209     next_data = {}
210
211     # use list, because iterable is too generic
212     if not any(isinstance(v, (collections.Mapping, list))
213                for v in data.values()):
214         return data
215
216     for k, v in six.iteritems(data):
217         if isinstance(v, collections.Mapping):
218             for n_k, n_v in six.iteritems(v):
219                 next_data["%s.%s" % (k, n_k)] = n_v
220         # use list because iterable is too generic
221         elif isinstance(v, list):
222             for index, item in enumerate(v):
223                 next_data["%s%d" % (k, index)] = item
224         else:
225             next_data[k] = v
226
227     return flatten_dict_key(next_data)
228
229
230 def translate_to_str(obj):
231     if isinstance(obj, collections.Mapping):
232         return {str(k): translate_to_str(v) for k, v in obj.items()}
233     elif isinstance(obj, list):
234         return [translate_to_str(ele) for ele in obj]
235     elif isinstance(obj, six.text_type):
236         return str(obj)
237     return obj
238
239
240 def result_handler(status, data):
241     result = {
242         'status': status,
243         'result': data
244     }
245     return jsonify(result)
246
247
248 def change_obj_to_dict(obj):
249     dic = {}
250     for k, v in vars(obj).items():
251         try:
252             vars(v)
253         except TypeError:
254             dic.update({k: v})
255     return dic
256
257
258 def set_dict_value(dic, keys, value):
259     return_dic = dic
260
261     for key in keys.split('.'):
262
263         return_dic.setdefault(key, {})
264         if key == keys.split('.')[-1]:
265             return_dic[key] = value
266         else:
267             return_dic = return_dic[key]
268     return dic
269
270
271 def get_free_port(ip):
272     with closing(socket.socket(socket.AF_INET, socket.SOCK_STREAM)) as s:
273         while True:
274             port = random.randint(5000, 10000)
275             if s.connect_ex((ip, port)) != 0:
276                 return port