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