Update to Alpine 3.14
[functest.git] / functest / utils / functest_utils.py
1 #!/usr/bin/env python
2 #
3 # jose.lausuch@ericsson.com
4 # valentin.boucher@orange.com
5 # All rights reserved. This program and the accompanying materials
6 # are made available under the terms of the Apache License, Version 2.0
7 # which accompanies this distribution, and is available at
8 # http://www.apache.org/licenses/LICENSE-2.0
9
10 # pylint: disable=missing-docstring
11
12 from __future__ import print_function
13 import logging
14 import os
15 import subprocess
16 import sys
17 import yaml
18
19 from shade import _utils
20 import six
21
22 LOGGER = logging.getLogger(__name__)
23
24
25 def execute_command_raise(cmd, info=False, error_msg="",
26                           verbose=True, output_file=None):
27     ret = execute_command(cmd, info, error_msg, verbose, output_file)
28     if ret != 0:
29         raise Exception(error_msg)
30
31
32 def execute_command(cmd, info=False, error_msg="",
33                     verbose=True, output_file=None):
34     if not error_msg:
35         error_msg = ("The command '%s' failed." % cmd)
36     msg_exec = ("Executing command: '%s'" % cmd)
37     if verbose:
38         if info:
39             LOGGER.info(msg_exec)
40         else:
41             LOGGER.debug(msg_exec)
42     with subprocess.Popen(
43             cmd, shell=True, stdout=subprocess.PIPE,
44             stderr=subprocess.STDOUT) as popen:
45         if output_file:
46             with open(output_file, "w") as ofd:
47                 for line in iter(popen.stdout.readline, b''):
48                     if output_file:
49                         ofd.write(line.decode("utf-8"))
50                     else:
51                         line = line.decode("utf-8").replace('\n', '')
52                         print(line)
53                         sys.stdout.flush()
54         returncode = popen.wait()
55     if returncode != 0:
56         if verbose:
57             LOGGER.error(error_msg)
58
59     return returncode
60
61
62 def get_parameter_from_yaml(parameter, yfile):
63     """
64     Returns the value of a given parameter in file.yaml
65     parameter must be given in string format with dots
66     Example: general.openstack.image_name
67     """
68     with open(yfile) as yfd:
69         file_yaml = yaml.safe_load(yfd)
70     value = file_yaml
71     for element in parameter.split("."):
72         value = value.get(element)
73         if value is None:
74             raise ValueError("The parameter %s is not defined in"
75                              " %s" % (parameter, yfile))
76     return value
77
78
79 def get_nova_version(cloud):
80     """ Get Nova API microversion
81
82     Returns:
83
84     - Nova API microversion
85     - None on operation error
86     """
87     # pylint: disable=protected-access
88     try:
89         request = cloud._compute_client.request("/", "GET")
90         LOGGER.debug('cloud._compute_client.request: %s', request)
91         version = request["version"]["version"]
92         major, minor = version.split('.')
93         LOGGER.debug('nova version: %s', (int(major), int(minor)))
94         return (int(major), int(minor))
95     except Exception:  # pylint: disable=broad-except
96         LOGGER.exception("Cannot detect Nova version")
97         return None
98
99
100 def get_openstack_version(cloud):
101     """ Detect OpenStack version via Nova API microversion
102
103     It follows `MicroversionHistory
104     <https://docs.openstack.org/nova/latest/reference/api-microversion-history.html>`_.
105
106     Returns:
107
108     - OpenStack release
109     - Unknown on operation error
110     """
111     # pylint: disable=too-many-branches
112     version = get_nova_version(cloud)
113     try:
114         assert version
115         if version > (2, 88):
116             osversion = "Master"
117         elif version > (2, 87):
118             osversion = "Wallaby"
119         elif version > (2, 79):
120             osversion = "Ussuri"
121         elif version > (2, 72):
122             osversion = "Train"
123         elif version > (2, 65):
124             osversion = "Stein"
125         elif version > (2, 60):
126             osversion = "Rocky"
127         elif version > (2, 53):
128             osversion = "Queens"
129         elif version > (2, 42):
130             osversion = "Pike"
131         elif version > (2, 38):
132             osversion = "Ocata"
133         elif version > (2, 25):
134             osversion = "Newton"
135         elif version > (2, 12):
136             osversion = "Mitaka"
137         elif version > (2, 3):
138             osversion = "Liberty"
139         elif version >= (2, 1):
140             osversion = "Kilo"
141         else:
142             osversion = "Unknown"
143         LOGGER.info('Detect OpenStack version: %s', osversion)
144         return osversion
145     except AssertionError:
146         LOGGER.exception("Cannot detect OpenStack version")
147         return "Unknown"
148
149
150 def list_services(cloud):
151     # pylint: disable=protected-access
152     """Search Keystone services via $OS_INTERFACE.
153
154     It mainly conforms with `Shade
155     <https://docs.openstack.org/shade/latest>`_ but allows testing vs
156     public endpoints. It's worth mentioning that it doesn't support keystone
157     v2.
158
159     :returns: a list of ``munch.Munch`` containing the services description
160
161     :raises: ``OpenStackCloudException`` if something goes wrong during the
162         openstack API call.
163     """
164     url, key = '/services', 'services'
165     data = cloud._identity_client.get(
166         url, endpoint_filter={
167             'interface': os.environ.get('OS_INTERFACE', 'public')},
168         error_message="Failed to list services")
169     services = cloud._get_and_munchify(key, data)
170     return _utils.normalize_keystone_services(services)
171
172
173 def search_services(cloud, name_or_id=None, filters=None):
174     # pylint: disable=protected-access
175     """Search Keystone services ia $OS_INTERFACE.
176
177     It mainly conforms with `Shade
178     <https://docs.openstack.org/shade/latest>`_ but allows testing vs
179     public endpoints. It's worth mentioning that it doesn't support keystone
180     v2.
181
182     :param name_or_id: Name or id of the desired service.
183     :param filters: a dict containing additional filters to use. e.g.
184                     {'type': 'network'}.
185
186     :returns: a list of ``munch.Munch`` containing the services description
187
188     :raises: ``OpenStackCloudException`` if something goes wrong during the
189         openstack API call.
190     """
191     services = list_services(cloud)
192     return _utils._filter_list(services, name_or_id, filters)
193
194
195 def convert_dict_to_ini(value):
196     "Convert dict to oslo.conf input"
197     assert isinstance(value, dict)
198     return ",".join("{}:{}".format(
199         key, val) for (key, val) in six.iteritems(value))
200
201
202 def convert_list_to_ini(value):
203     "Convert list to oslo.conf input"
204     assert isinstance(value, list)
205     return ",".join("{}".format(val) for val in value)
206
207
208 def convert_ini_to_dict(value):
209     "Convert oslo.conf input to dict"
210     assert isinstance(value, str)
211     try:
212         return dict((x.rsplit(':', 1) for x in value.split(',')))
213     except ValueError:
214         return {}
215
216
217 def convert_ini_to_list(value):
218     "Convert list to oslo.conf input"
219     assert isinstance(value, str)
220     if not value:
221         return []
222     return list(value.split(','))