]> git.ipfire.org Git - nitsi.git/blame - src/nitsi/test.py
Split setup virtual environment in two functions
[nitsi.git] / src / nitsi / test.py
CommitLineData
5e7f6db7
JS
1#!/usr/bin/python3
2
6632e137 3import configparser
5e7f6db7 4import libvirt
6632e137 5import logging
5e7f6db7 6import os
6c352a80
JS
7import time
8
b560f31a
JS
9from . import recipe
10from . import virtual_environ
1795f5e8 11from . import settings
5e7f6db7 12
1ed8ca9f 13logger = logging.getLogger("nitsi.test")
5e7f6db7 14
61b44c10
JS
15
16class TestException(Exception):
17 def __init__(self, message):
18 self.message = message
19
ee227ea1 20class Test():
ab5b8849 21 def __init__(self, log_path, dir=None, recipe_file=None, settings_file=None, cmd_settings=None):
978854bf
JS
22 # init settings var
23 self.settings = {}
24
1ec6548b
JS
25 # Set default values for the settings dict
26 self.settings["name"] = ""
27 self.settings["description"] = ""
28 self.settings["copy_from"] = None
29 self.settings["copy_to"] = None
30 self.settings["virtual_environ_path"] = None
31
ab5b8849
JS
32 self.cmd_settings = cmd_settings
33 self.log_path = log_path
5e7f6db7 34
ab5b8849
JS
35 # Init all vars with None
36 self.settings_file = None
37 self.recipe_file = None
38 self.path = None
5e7f6db7 39
ab5b8849
JS
40 # We need at least a path to a recipe file or a dir to a test
41 if not dir and not recipe:
42 raise TestException("Did not get a path to a test or to a recipe file")
43
44 # We cannot decide which to use when we get both
45 if (dir and recipe_file) or (dir and settings_file):
46 raise TestException("Get dir and path to recipe or settings file")
47
48 if dir:
49 try:
50 if not os.path.isabs(dir):
51 self.path = os.path.abspath(dir)
52 except BaseException as e:
53 logger.error("Could not get absolute path")
54 raise e
55
56 logger.debug("Path of this test is: {}".format(self.path))
57
58 self.recipe_file = "{}/recipe".format(self.path)
59 self.settings_file = "{}/settings".format(self.path)
60
61 if recipe_file:
62 if not os.path.isabs(recipe_file):
63 self.recipe_file = os.path.abspath(recipe_file)
64 else:
65 self.recipe_file = recipe_file
66
67 if settings_file:
68 if not os.path.isabs(settings_file):
69 self.settings_file = os.path.abspath(settings_file)
70 else:
71 self.settings_file = settings_file
b4936764 72
aadbafc3
JS
73 # We can also go on without a settings file
74 if self.settings_file:
75 if not os.path.isfile(self.settings_file):
76 logger.error("No such file: {}".format(self.settings_file))
77 raise TestException("No settings file found")
78
e8da2bf3
JS
79 # os.path.isfile fails if self.recipe_file is None so we cannot use an and statement
80 if self.recipe_file:
81 if not os.path.isfile(self.recipe_file):
aadbafc3
JS
82 logger.error("No such file: {}".format(self.recipe_file))
83 raise TestException("No recipe file found")
e8da2bf3
JS
84 else:
85 logger.error("No such file: {}".format(self.recipe_file))
86 raise TestException("No recipe file found")
5e7f6db7 87
5e7f6db7 88
ab5b8849
JS
89 # Init logging
90 if dir:
91 self.log = logger.getChild(os.path.basename(self.path))
92
93 if recipe:
94 self.log = logger.getChild(os.path.basename(self.recipe_file))
978854bf 95
5e7f6db7 96 def read_settings(self):
ab5b8849
JS
97 if self.settings_file:
98 self.log.debug("Going to read all settings from the ini file")
99 try:
100 self.config = configparser.ConfigParser()
101 self.config.read(self.settings_file)
102 except BaseException as e:
103 self.log.error("Failed to parse the config")
104 raise e
105
106 self.settings["name"] = self.config.get("GENERAL","name", fallback="")
107 self.settings["description"] = self.config.get("GENERAL", "description", fallback="")
108 self.settings["copy_to"] = self.config.get("GENERAL", "copy_to", fallback=None)
109 self.settings["copy_from"] = self.config.get("GENERAL", "copy_from", fallback=None)
110 self.settings["virtual_environ_path"] = self.config.get("VIRTUAL_ENVIRONMENT", "path", fallback=None)
8428a452 111
1795f5e8
JS
112 # We need to parse some settings here because they are loaded from a settings file
113 if not os.path.isabs(self.settings["virtual_environ_path"]):
114 self.settings["virtual_environ_path"] = os.path.normpath(os.path.dirname(
115 self.settings_file) + "/" + self.settings["virtual_environ_path"])
129c0b0f 116
1795f5e8
JS
117 # Parse copy_from setting
118 if self.settings["copy_from"]:
119 self.settings["copy_from"] = settings.settings_parse_copy_from(self.settings["copy_from"],
120 path=os.path.dirname(self.settings_file))
14cd493f 121
1795f5e8
JS
122 # Update all settings from the cmd
123 self.settings.update(self.cmd_settings)
f010720b 124
1795f5e8
JS
125 if not os.path.isabs(self.settings["virtual_environ_path"]):
126 self.settings["virtual_environ_path"] = os.path.abspath(self.settings["virtual_environ_path"])
f010720b 127
14cd493f 128
1795f5e8
JS
129 # Check if we get at least a valid a valid path to virtual environ
130 if not self.settings["virtual_environ_path"]:
131 self.log.error("No path for virtual environment found.")
132 raise TestException("No path for virtual environment found.")
5e7f6db7 133
1795f5e8
JS
134 # Print all settings for debugging purpose
135 self.log.debug("Settings are:")
136 for key in self.settings:
137 self.log.debug("{}: {}".format(key, self.settings[key]))
129c0b0f 138
5e7f6db7 139
3dac9881 140 def virtual_environ_setup_stage_1(self):
d9f6c37f 141 self.virtual_environ = virtual_environ.VirtualEnviron(self.settings["virtual_environ_path"])
5e7f6db7
JS
142
143 self.virtual_networks = self.virtual_environ.get_networks()
144
145 self.virtual_machines = self.virtual_environ.get_machines()
146
3dac9881 147 def virtual_environ_setup_stage_2(self):
fbaed8c3
JS
148 # built up which machines which are used in our recipe
149 used_machines = []
150
151 for line in self.recipe.recipe:
152 if not line[0] in used_machines:
153 used_machines.append(line[0])
154
155 self.log.debug("Machines used in this recipe {}".format(used_machines))
156
157 self.used_machine_names = used_machines
158
159 for machine in self.used_machine_names:
160 if not machine in self.virtual_environ.machine_names:
161 raise TestException("{} is listed as machine in the recipe, but the virtual environmet does not have such a machine".format(machine))
162
163
5e7f6db7 164 def virtual_environ_start(self):
3fa89b7c
JS
165 for name in self.virtual_environ.network_names:
166 self.virtual_networks[name].define()
167 self.virtual_networks[name].start()
5e7f6db7 168
fbaed8c3 169 for name in self.used_machine_names:
3fa89b7c
JS
170 self.virtual_machines[name].define()
171 self.virtual_machines[name].create_snapshot()
8428a452 172 # We can only copy files when we know which and to which dir
978854bf
JS
173 if self.settings["copy_from"] and self.settings["copy_to"]:
174 self.virtual_machines[name].copy_in(self.settings["copy_from"], self.settings["copy_to"])
3fa89b7c 175 self.virtual_machines[name].start()
5e7f6db7 176
6c352a80
JS
177 # Time to which all serial output log entries are relativ
178 log_start_time = time.time()
179
fc35cba1
JS
180 # Number of chars of the longest machine name
181 longest_machine_name = self.virtual_environ.longest_machine_name
182
5ff14f2d 183 self.log.info("Try to login on all machines")
fbaed8c3 184 for name in self.used_machine_names:
5ff14f2d 185 self.log.info("Try to login on {}".format(name))
fc35cba1
JS
186 self.virtual_machines[name].login("{}/test.log".format(self.log_path),
187 log_start_time=log_start_time,
188 longest_machine_name=longest_machine_name)
5e7f6db7 189
3fa89b7c 190 def load_recipe(self):
2fa4467d 191 self.log.info("Going to load the recipe")
3fa89b7c 192 try:
ee227ea1 193 self.recipe = recipe.Recipe(self.recipe_file, machines=self.virtual_environ.machine_names)
4bc54b45
JS
194 for line in self.recipe.recipe:
195 self.log.debug(line)
2fa4467d
JS
196
197 self.log.debug("This was the recipe")
4bc54b45 198 except BaseException as e:
3fa89b7c 199 self.log.error("Failed to load recipe")
4bc54b45 200 raise e
3fa89b7c
JS
201
202 def run_recipe(self):
203 for line in self.recipe.recipe:
204 return_value = self.virtual_machines[line[0]].cmd(line[2])
bce7d520
JS
205 self.log.debug("Return value is: {}".format(return_value))
206 if return_value != "0" and line[1] == "":
61b44c10 207 raise TestException("Failed to execute command '{}' on {}, return code: {}".format(line[2],line[0], return_value))
bce7d520 208 elif return_value == "0" and line[1] == "!":
61b44c10 209 raise TestException("Succeded to execute command '{}' on {}, return code: {}".format(line[2],line[0],return_value))
bce7d520
JS
210 else:
211 self.log.debug("Command '{}' on {} returned with: {}".format(line[2],line[0],return_value))
3fa89b7c
JS
212
213 def virtual_environ_stop(self):
fbaed8c3 214 for name in self.used_machine_names:
f9178ad3
JS
215 # We just catch exception here to avoid
216 # that we stop the cleanup process if only one command fails
217 try:
218 self.virtual_machines[name].shutdown()
219 self.virtual_machines[name].revert_snapshot()
220 self.virtual_machines[name].undefine()
221 except BaseException as e:
222 self.log.exception(e)
3fa89b7c
JS
223
224 for name in self.virtual_environ.network_names:
f9178ad3
JS
225 try:
226 self.virtual_networks[name].undefine()
227 except BaseException as e:
228 self.log.exception(e)
229