0c0bac93452890ce7f58ae1f4d9b1abacac09ca3
[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 execute_command(cmd):
128     exec_msg = "Executing command: '%s'" % cmd
129     logger.debug(exec_msg)
130
131     output = subprocess.check_output(cmd.split()).split(os.linesep)
132
133     return output
134
135
136 def source_env(env_file):
137     p = subprocess.Popen(". %s; env" % env_file, stdout=subprocess.PIPE,
138                          shell=True)
139     output = p.communicate()[0]
140     env = dict((line.split('=', 1) for line in output.splitlines()))
141     os.environ.update(env)
142     return env
143
144
145 def read_json_from_file(path):
146     with open(path, 'r') as f:
147         j = f.read()
148     # don't use jsonutils.load() it conflicts with already decoded input
149     return jsonutils.loads(j)
150
151
152 def write_json_to_file(path, data, mode='w'):
153     with open(path, mode) as f:
154         jsonutils.dump(data, f)
155
156
157 def write_file(path, data, mode='w'):
158     with open(path, mode) as f:
159         f.write(data)
160
161
162 def parse_ini_file(path):
163     parser = configparser.ConfigParser()
164     parser.read(path)
165
166     try:
167         default = {k: v for k, v in parser.items('DEFAULT')}
168     except configparser.NoSectionError:
169         default = {}
170
171     config = dict(DEFAULT=default,
172                   **{s: {k: v for k, v in parser.items(
173                       s)} for s in parser.sections()})
174
175     return config
176
177
178 def get_port_mac(sshclient, port):
179     cmd = "ifconfig |grep HWaddr |grep %s |awk '{print $5}' " % port
180     status, stdout, stderr = sshclient.execute(cmd)
181
182     if status:
183         raise RuntimeError(stderr)
184     return stdout.rstrip()
185
186
187 def get_port_ip(sshclient, port):
188     cmd = "ifconfig %s |grep 'inet addr' |awk '{print $2}' " \
189         "|cut -d ':' -f2 " % port
190     status, stdout, stderr = sshclient.execute(cmd)
191
192     if status:
193         raise RuntimeError(stderr)
194     return stdout.rstrip()
195
196
197 def flatten_dict_key(data):
198     next_data = {}
199
200     # use list, because iterable is too generic
201     if not any(isinstance(v, (collections.Mapping, list))
202                for v in data.values()):
203         return data
204
205     for k, v in six.iteritems(data):
206         if isinstance(v, collections.Mapping):
207             for n_k, n_v in six.iteritems(v):
208                 next_data["%s.%s" % (k, n_k)] = n_v
209         # use list because iterable is too generic
210         elif isinstance(v, list):
211             for index, item in enumerate(v):
212                 next_data["%s%d" % (k, index)] = item
213         else:
214             next_data[k] = v
215
216     return flatten_dict_key(next_data)
217
218
219 def translate_to_str(obj):
220     if isinstance(obj, collections.Mapping):
221         return {str(k): translate_to_str(v) for k, v in obj.items()}
222     elif isinstance(obj, list):
223         return [translate_to_str(ele) for ele in obj]
224     elif isinstance(obj, six.text_type):
225         return str(obj)
226     return obj
227
228
229 def result_handler(status, data):
230     result = {
231         'status': status,
232         'result': data
233     }
234     return jsonify(result)
235
236
237 def change_obj_to_dict(obj):
238     dic = {}
239     for k, v in vars(obj).items():
240         try:
241             vars(v)
242         except TypeError:
243             dic.update({k: v})
244     return dic