]>
Commit | Line | Data |
---|---|---|
5e7f6db7 JS |
1 | #!/usr/bin/python3 |
2 | ||
6632e137 | 3 | import configparser |
5e7f6db7 | 4 | import libvirt |
6632e137 | 5 | import logging |
5e7f6db7 | 6 | import os |
6c352a80 JS |
7 | import time |
8 | ||
b560f31a JS |
9 | from . import recipe |
10 | from . import virtual_environ | |
1795f5e8 | 11 | from . import settings |
5e7f6db7 | 12 | |
1ed8ca9f | 13 | logger = logging.getLogger("nitsi.test") |
5e7f6db7 | 14 | |
61b44c10 JS |
15 | |
16 | class TestException(Exception): | |
17 | def __init__(self, message): | |
18 | self.message = message | |
19 | ||
ee227ea1 | 20 | class 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 | ||
79 | # os.path.isfile fails if self.recipe_file is None so we need to catch exceptions here | |
80 | try: | |
81 | if not (self.recipe_file or os.path.isfile(self.recipe_file)): | |
82 | logger.error("No such file: {}".format(self.recipe_file)) | |
83 | raise TestException("No recipe file found") | |
84 | except BaseException: | |
85 | pass | |
5e7f6db7 | 86 | |
5e7f6db7 | 87 | |
ab5b8849 JS |
88 | # Init logging |
89 | if dir: | |
90 | self.log = logger.getChild(os.path.basename(self.path)) | |
91 | ||
92 | if recipe: | |
93 | self.log = logger.getChild(os.path.basename(self.recipe_file)) | |
978854bf | 94 | |
5e7f6db7 | 95 | def read_settings(self): |
ab5b8849 JS |
96 | if self.settings_file: |
97 | self.log.debug("Going to read all settings from the ini file") | |
98 | try: | |
99 | self.config = configparser.ConfigParser() | |
100 | self.config.read(self.settings_file) | |
101 | except BaseException as e: | |
102 | self.log.error("Failed to parse the config") | |
103 | raise e | |
104 | ||
105 | self.settings["name"] = self.config.get("GENERAL","name", fallback="") | |
106 | self.settings["description"] = self.config.get("GENERAL", "description", fallback="") | |
107 | self.settings["copy_to"] = self.config.get("GENERAL", "copy_to", fallback=None) | |
108 | self.settings["copy_from"] = self.config.get("GENERAL", "copy_from", fallback=None) | |
109 | self.settings["virtual_environ_path"] = self.config.get("VIRTUAL_ENVIRONMENT", "path", fallback=None) | |
8428a452 | 110 | |
1795f5e8 JS |
111 | # We need to parse some settings here because they are loaded from a settings file |
112 | if not os.path.isabs(self.settings["virtual_environ_path"]): | |
113 | self.settings["virtual_environ_path"] = os.path.normpath(os.path.dirname( | |
114 | self.settings_file) + "/" + self.settings["virtual_environ_path"]) | |
129c0b0f | 115 | |
1795f5e8 JS |
116 | # Parse copy_from setting |
117 | if self.settings["copy_from"]: | |
118 | self.settings["copy_from"] = settings.settings_parse_copy_from(self.settings["copy_from"], | |
119 | path=os.path.dirname(self.settings_file)) | |
14cd493f | 120 | |
1795f5e8 JS |
121 | # Update all settings from the cmd |
122 | self.settings.update(self.cmd_settings) | |
f010720b | 123 | |
1795f5e8 JS |
124 | if not os.path.isabs(self.settings["virtual_environ_path"]): |
125 | self.settings["virtual_environ_path"] = os.path.abspath(self.settings["virtual_environ_path"]) | |
f010720b | 126 | |
14cd493f | 127 | |
1795f5e8 JS |
128 | # Check if we get at least a valid a valid path to virtual environ |
129 | if not self.settings["virtual_environ_path"]: | |
130 | self.log.error("No path for virtual environment found.") | |
131 | raise TestException("No path for virtual environment found.") | |
5e7f6db7 | 132 | |
1795f5e8 JS |
133 | # Print all settings for debugging purpose |
134 | self.log.debug("Settings are:") | |
135 | for key in self.settings: | |
136 | self.log.debug("{}: {}".format(key, self.settings[key])) | |
129c0b0f | 137 | |
5e7f6db7 JS |
138 | |
139 | def virtual_environ_setup(self): | |
978854bf | 140 | self.virtual_environ = virtual_environ.Virtual_environ(self.settings["virtual_environ_path"]) |
5e7f6db7 JS |
141 | |
142 | self.virtual_networks = self.virtual_environ.get_networks() | |
143 | ||
144 | self.virtual_machines = self.virtual_environ.get_machines() | |
145 | ||
146 | def virtual_environ_start(self): | |
3fa89b7c JS |
147 | for name in self.virtual_environ.network_names: |
148 | self.virtual_networks[name].define() | |
149 | self.virtual_networks[name].start() | |
5e7f6db7 | 150 | |
3fa89b7c JS |
151 | for name in self.virtual_environ.machine_names: |
152 | self.virtual_machines[name].define() | |
153 | self.virtual_machines[name].create_snapshot() | |
8428a452 | 154 | # We can only copy files when we know which and to which dir |
978854bf JS |
155 | if self.settings["copy_from"] and self.settings["copy_to"]: |
156 | self.virtual_machines[name].copy_in(self.settings["copy_from"], self.settings["copy_to"]) | |
3fa89b7c | 157 | self.virtual_machines[name].start() |
5e7f6db7 | 158 | |
6c352a80 JS |
159 | # Time to which all serial output log entries are relativ |
160 | log_start_time = time.time() | |
161 | ||
fc35cba1 JS |
162 | # Number of chars of the longest machine name |
163 | longest_machine_name = self.virtual_environ.longest_machine_name | |
164 | ||
5ff14f2d | 165 | self.log.info("Try to login on all machines") |
3fa89b7c | 166 | for name in self.virtual_environ.machine_names: |
5ff14f2d | 167 | self.log.info("Try to login on {}".format(name)) |
fc35cba1 JS |
168 | self.virtual_machines[name].login("{}/test.log".format(self.log_path), |
169 | log_start_time=log_start_time, | |
170 | longest_machine_name=longest_machine_name) | |
5e7f6db7 | 171 | |
3fa89b7c | 172 | def load_recipe(self): |
2fa4467d | 173 | self.log.info("Going to load the recipe") |
3fa89b7c | 174 | try: |
ee227ea1 | 175 | self.recipe = recipe.Recipe(self.recipe_file, machines=self.virtual_environ.machine_names) |
4bc54b45 JS |
176 | for line in self.recipe.recipe: |
177 | self.log.debug(line) | |
2fa4467d JS |
178 | |
179 | self.log.debug("This was the recipe") | |
4bc54b45 | 180 | except BaseException as e: |
3fa89b7c | 181 | self.log.error("Failed to load recipe") |
4bc54b45 | 182 | raise e |
3fa89b7c JS |
183 | |
184 | def run_recipe(self): | |
185 | for line in self.recipe.recipe: | |
186 | return_value = self.virtual_machines[line[0]].cmd(line[2]) | |
bce7d520 JS |
187 | self.log.debug("Return value is: {}".format(return_value)) |
188 | if return_value != "0" and line[1] == "": | |
61b44c10 | 189 | raise TestException("Failed to execute command '{}' on {}, return code: {}".format(line[2],line[0], return_value)) |
bce7d520 | 190 | elif return_value == "0" and line[1] == "!": |
61b44c10 | 191 | raise TestException("Succeded to execute command '{}' on {}, return code: {}".format(line[2],line[0],return_value)) |
bce7d520 JS |
192 | else: |
193 | self.log.debug("Command '{}' on {} returned with: {}".format(line[2],line[0],return_value)) | |
3fa89b7c JS |
194 | |
195 | def virtual_environ_stop(self): | |
196 | for name in self.virtual_environ.machine_names: | |
f9178ad3 JS |
197 | # We just catch exception here to avoid |
198 | # that we stop the cleanup process if only one command fails | |
199 | try: | |
200 | self.virtual_machines[name].shutdown() | |
201 | self.virtual_machines[name].revert_snapshot() | |
202 | self.virtual_machines[name].undefine() | |
203 | except BaseException as e: | |
204 | self.log.exception(e) | |
3fa89b7c JS |
205 | |
206 | for name in self.virtual_environ.network_names: | |
f9178ad3 JS |
207 | try: |
208 | self.virtual_networks[name].undefine() | |
209 | except BaseException as e: | |
210 | self.log.exception(e) | |
211 |