Merge "Add documentation of testapi internship"
[functest.git] / functest / ci / prepare_env.py
1 #!/usr/bin/env python
2 #
3 # All rights reserved. This program and the accompanying materials
4 # are made available under the terms of the Apache License, Version 2.0
5 # which accompanies this distribution, and is available at
6 # http://www.apache.org/licenses/LICENSE-2.0
7 #
8
9 import argparse
10 import json
11 import os
12 import re
13 import subprocess
14 import sys
15
16 import yaml
17
18 import functest.utils.functest_logger as ft_logger
19 import functest.utils.functest_utils as ft_utils
20 import functest.utils.openstack_utils as os_utils
21 from functest.utils.constants import CONST
22
23 from opnfv.utils import constants as opnfv_constants
24 from opnfv.deployment import factory
25
26 actions = ['start', 'check']
27
28 """ logging configuration """
29 logger = ft_logger.Logger("prepare_env").getLogger()
30
31
32 CONFIG_FUNCTEST_PATH = CONST.CONFIG_FUNCTEST_YAML
33 CONFIG_PATCH_PATH = os.path.join(os.path.dirname(
34     CONFIG_FUNCTEST_PATH), "config_patch.yaml")
35
36 with open(CONFIG_PATCH_PATH) as f:
37     functest_patch_yaml = yaml.safe_load(f)
38
39
40 class PrepareEnvParser(object):
41
42     def __init__(self):
43         self.parser = argparse.ArgumentParser()
44         self.parser.add_argument("action", help="Possible actions are: "
45                                  "'{d[0]}|{d[1]}' ".format(d=actions),
46                                  choices=actions)
47         self.parser.add_argument("-d", "--debug", help="Debug mode",
48                                  action="store_true")
49
50     def parse_args(self, argv=[]):
51         return vars(self.parser.parse_args(argv))
52
53
54 def print_separator():
55     logger.info("==============================================")
56
57
58 def check_env_variables():
59     print_separator()
60     logger.info("Checking environment variables...")
61
62     if CONST.INSTALLER_TYPE is None:
63         logger.warning("The env variable 'INSTALLER_TYPE' is not defined.")
64         CONST.INSTALLER_TYPE = "undefined"
65     else:
66         if CONST.INSTALLER_TYPE not in opnfv_constants.INSTALLERS:
67             logger.warning("INSTALLER_TYPE=%s is not a valid OPNFV installer. "
68                            "Available OPNFV Installers are : %s. "
69                            "Setting INSTALLER_TYPE=undefined."
70                            % (CONST.INSTALLER_TYPE,
71                               opnfv_constants.INSTALLERS))
72             CONST.INSTALLER_TYPE = "undefined"
73         else:
74             logger.info("    INSTALLER_TYPE=%s"
75                         % CONST.INSTALLER_TYPE)
76
77     if CONST.INSTALLER_IP is None:
78         logger.warning("The env variable 'INSTALLER_IP' is not defined. "
79                        "It is needed to fetch the OpenStack credentials. "
80                        "If the credentials are not provided to the "
81                        "container as a volume, please add this env variable "
82                        "to the 'docker run' command.")
83     else:
84         logger.info("    INSTALLER_IP=%s" % CONST.INSTALLER_IP)
85
86     if CONST.DEPLOY_SCENARIO is None:
87         logger.warning("The env variable 'DEPLOY_SCENARIO' is not defined. "
88                        "Setting CI_SCENARIO=undefined.")
89         CONST.DEPLOY_SCENARIO = "undefined"
90     else:
91         logger.info("    DEPLOY_SCENARIO=%s" % CONST.DEPLOY_SCENARIO)
92     if CONST.CI_DEBUG:
93         logger.info("    CI_DEBUG=%s" % CONST.CI_DEBUG)
94
95     if CONST.NODE_NAME:
96         logger.info("    NODE_NAME=%s" % CONST.NODE_NAME)
97
98     if CONST.BUILD_TAG:
99         logger.info("    BUILD_TAG=%s" % CONST.BUILD_TAG)
100
101     if CONST.IS_CI_RUN:
102         logger.info("    IS_CI_RUN=%s" % CONST.IS_CI_RUN)
103
104
105 def create_directories():
106     print_separator()
107     logger.info("Creating needed directories...")
108     if not os.path.exists(CONST.dir_functest_conf):
109         os.makedirs(CONST.dir_functest_conf)
110         logger.info("    %s created." % CONST.dir_functest_conf)
111     else:
112         logger.debug("   %s already exists."
113                      % CONST.dir_functest_conf)
114
115     if not os.path.exists(CONST.dir_functest_data):
116         os.makedirs(CONST.dir_functest_data)
117         logger.info("    %s created." % CONST.dir_functest_data)
118     else:
119         logger.debug("   %s already exists."
120                      % CONST.dir_functest_data)
121
122
123 def source_rc_file():
124     print_separator()
125     logger.info("Fetching RC file...")
126
127     if CONST.openstack_creds is None:
128         logger.warning("The environment variable 'creds' must be set and"
129                        "pointing to the local RC file. Using default: "
130                        "/home/opnfv/functest/conf/openstack.creds ...")
131         os.path.join(CONST.dir_functest_conf, 'openstack.creds')
132
133     if not os.path.isfile(CONST.openstack_creds):
134         logger.info("RC file not provided. "
135                     "Fetching it from the installer...")
136         if CONST.INSTALLER_IP is None:
137             logger.error("The env variable CI_INSTALLER_IP must be provided in"
138                          " order to fetch the credentials from the installer.")
139             raise Exception("Missing CI_INSTALLER_IP.")
140         if CONST.INSTALLER_TYPE not in opnfv_constants.INSTALLERS:
141             logger.error("Cannot fetch credentials. INSTALLER_TYPE=%s is "
142                          "not a valid OPNFV installer. Available "
143                          "installers are : %s." %
144                          (CONST.INSTALLER_TYPE,
145                           opnfv_constants.INSTALLERS))
146             raise Exception("Wrong INSTALLER_TYPE.")
147
148         cmd = ("/home/opnfv/repos/releng/utils/fetch_os_creds.sh "
149                "-d %s -i %s -a %s"
150                % (CONST.openstack_creds,
151                   CONST.INSTALLER_TYPE,
152                   CONST.INSTALLER_IP))
153         logger.debug("Executing command: %s" % cmd)
154         p = subprocess.Popen(cmd.split(), stdout=subprocess.PIPE)
155         output = p.communicate()[0]
156         logger.debug("\n%s" % output)
157         if p.returncode != 0:
158             raise Exception("Failed to fetch credentials from installer.")
159     else:
160         logger.info("RC file provided in %s."
161                     % CONST.openstack_creds)
162         if os.path.getsize(CONST.openstack_creds) == 0:
163             raise Exception("The file %s is empty." % CONST.openstack_creds)
164
165     logger.info("Sourcing the OpenStack RC file...")
166     os_utils.source_credentials(
167         CONST.openstack_creds)
168     for key, value in os.environ.iteritems():
169         if re.search("OS_", key):
170             if key == 'OS_AUTH_URL':
171                 CONST.OS_AUTH_URL = value
172             elif key == 'OS_USERNAME':
173                 CONST.OS_USERNAME = value
174             elif key == 'OS_TENANT_NAME':
175                 CONST.OS_TENANT_NAME = value
176             elif key == 'OS_PASSWORD':
177                 CONST.OS_PASSWORD = value
178
179
180 def patch_config_file():
181     updated = False
182     for key in functest_patch_yaml:
183         if key in CONST.DEPLOY_SCENARIO:
184             new_functest_yaml = dict(ft_utils.merge_dicts(
185                 ft_utils.get_functest_yaml(), functest_patch_yaml[key]))
186             updated = True
187
188     if updated:
189         os.remove(CONFIG_FUNCTEST_PATH)
190         with open(CONFIG_FUNCTEST_PATH, "w") as f:
191             f.write(yaml.dump(new_functest_yaml, default_style='"'))
192         f.close()
193
194
195 def verify_deployment():
196     print_separator()
197     logger.info("Verifying OpenStack services...")
198     cmd = ("%s/functest/ci/check_os.sh" % CONST.dir_repo_functest)
199
200     logger.debug("Executing command: %s" % cmd)
201     p = subprocess.Popen(cmd, stdout=subprocess.PIPE, shell=True)
202
203     while p.poll() is None:
204         line = p.stdout.readline().rstrip()
205         if "ERROR" in line:
206             logger.error(line)
207             raise Exception("Problem while running 'check_os.sh'.")
208         logger.info(line)
209
210
211 def install_rally():
212     print_separator()
213     logger.info("Creating Rally environment...")
214
215     cmd = "rally deployment destroy opnfv-rally"
216     ft_utils.execute_command(cmd, error_msg=(
217         "Deployment %s does not exist."
218         % CONST.rally_deployment_name),
219         verbose=False)
220
221     rally_conf = os_utils.get_credentials_for_rally()
222     with open('rally_conf.json', 'w') as fp:
223         json.dump(rally_conf, fp)
224     cmd = ("rally deployment create "
225            "--file=rally_conf.json --name={0}"
226            .format(CONST.rally_deployment_name))
227     error_msg = "Problem while creating Rally deployment"
228     ft_utils.execute_command_raise(cmd, error_msg=error_msg)
229
230     cmd = "rally deployment check"
231     error_msg = "OpenStack not responding or faulty Rally deployment."
232     ft_utils.execute_command_raise(cmd, error_msg=error_msg)
233
234     cmd = "rally deployment list"
235     ft_utils.execute_command(cmd,
236                              error_msg=("Problem while listing "
237                                         "Rally deployment."))
238
239     cmd = "rally plugin list | head -5"
240     ft_utils.execute_command(cmd,
241                              error_msg=("Problem while showing "
242                                         "Rally plugins."))
243
244
245 def install_tempest():
246     logger.info("Installing tempest from existing repo...")
247     cmd = ("rally verify list-verifiers | "
248            "grep '{0}' | wc -l".format(CONST.tempest_deployment_name))
249     p = subprocess.Popen(cmd, stdout=subprocess.PIPE, shell=True)
250     while p.poll() is None:
251         line = p.stdout.readline().rstrip()
252         if str(line) == '0':
253             logger.debug("Tempest %s does not exist" %
254                          CONST.tempest_deployment_name)
255             cmd = ("rally verify create-verifier --source {0} "
256                    "--name {1} --type tempest"
257                    .format(CONST.dir_repo_tempest,
258                            CONST.tempest_deployment_name))
259             error_msg = "Problem while installing Tempest."
260             ft_utils.execute_command_raise(cmd, error_msg=error_msg)
261
262
263 def create_flavor():
264     _, flavor_id = os_utils.get_or_create_flavor('m1.tiny',
265                                                  '512',
266                                                  '1',
267                                                  '1',
268                                                  public=True)
269     if flavor_id is None:
270         raise Exception('Failed to create flavor')
271
272
273 def check_environment():
274     msg_not_active = "The Functest environment is not installed."
275     if not os.path.isfile(CONST.env_active):
276         raise Exception(msg_not_active)
277
278     with open(CONST.env_active, "r") as env_file:
279         s = env_file.read()
280         if not re.search("1", s):
281             raise Exception(msg_not_active)
282
283     logger.info("Functest environment is installed.")
284
285
286 def print_deployment_info():
287     installer_params_yaml = os.path.join(CONST.dir_repo_functest,
288                                          'functest/ci/installer_params.yaml')
289     if (CONST.INSTALLER_IP and CONST.INSTALLER_TYPE and
290             CONST.INSTALLER_TYPE in opnfv_constants.INSTALLERS):
291         try:
292             installer_params = ft_utils.get_parameter_from_yaml(
293                 CONST.INSTALLER_TYPE, installer_params_yaml)
294         except ValueError as e:
295             logger.debug('Printing deployment info is not supported for %s' %
296                          CONST.INSTALLER_TYPE)
297             logger.debug(e)
298         else:
299             user = installer_params.get('user', None)
300             password = installer_params.get('password', None)
301             pkey = installer_params.get('pkey', None)
302
303             try:
304                 handler = factory.Factory.get_handler(
305                     installer=CONST.INSTALLER_TYPE,
306                     installer_ip=CONST.INSTALLER_IP,
307                     installer_user=user,
308                     installer_pwd=password,
309                     pkey_file=pkey)
310                 if handler:
311                     logger.info('\n\nDeployment information:\n%s' %
312                                 handler.get_deployment_info())
313             except Exception as e:
314                 logger.debug("Cannot get deployment information. %s" % e)
315
316
317 def main(**kwargs):
318     try:
319         if not (kwargs['action'] in actions):
320             logger.error('Argument not valid.')
321             return -1
322         elif kwargs['action'] == "start":
323             logger.info("######### Preparing Functest environment #########\n")
324             check_env_variables()
325             create_directories()
326             source_rc_file()
327             patch_config_file()
328             verify_deployment()
329             install_rally()
330             install_tempest()
331             create_flavor()
332             with open(CONST.env_active, "w") as env_file:
333                 env_file.write("1")
334             check_environment()
335             print_deployment_info()
336         elif kwargs['action'] == "check":
337             check_environment()
338     except Exception as e:
339         logger.error(e)
340         return -1
341     return 0
342
343
344 if __name__ == '__main__':
345     parser = PrepareEnvParser()
346     args = parser.parse_args(sys.argv[1:])
347     sys.exit(main(**args))