]>
Commit | Line | Data |
---|---|---|
1 | #!/usr/bin/python3 | |
2 | ||
3 | import configparser | |
4 | import libvirt | |
5 | import logging | |
6 | import os | |
7 | import time | |
8 | ||
9 | from . import recipe | |
10 | from . import virtual_environ | |
11 | ||
12 | logger = logging.getLogger("nitsi.test") | |
13 | ||
14 | ||
15 | class TestException(Exception): | |
16 | def __init__(self, message): | |
17 | self.message = message | |
18 | ||
19 | class Test(): | |
20 | def __init__(self, log_path, dir=None, recipe_file=None, settings_file=None, cmd_settings=None): | |
21 | # init settings var | |
22 | self.settings = {} | |
23 | ||
24 | self.cmd_settings = cmd_settings | |
25 | self.log_path = log_path | |
26 | ||
27 | # Init all vars with None | |
28 | self.settings_file = None | |
29 | self.recipe_file = None | |
30 | self.path = None | |
31 | ||
32 | # We need at least a path to a recipe file or a dir to a test | |
33 | if not dir and not recipe: | |
34 | raise TestException("Did not get a path to a test or to a recipe file") | |
35 | ||
36 | # We cannot decide which to use when we get both | |
37 | if (dir and recipe_file) or (dir and settings_file): | |
38 | raise TestException("Get dir and path to recipe or settings file") | |
39 | ||
40 | if dir: | |
41 | try: | |
42 | if not os.path.isabs(dir): | |
43 | self.path = os.path.abspath(dir) | |
44 | except BaseException as e: | |
45 | logger.error("Could not get absolute path") | |
46 | raise e | |
47 | ||
48 | logger.debug("Path of this test is: {}".format(self.path)) | |
49 | ||
50 | self.recipe_file = "{}/recipe".format(self.path) | |
51 | self.settings_file = "{}/settings".format(self.path) | |
52 | ||
53 | if recipe_file: | |
54 | if not os.path.isabs(recipe_file): | |
55 | self.recipe_file = os.path.abspath(recipe_file) | |
56 | else: | |
57 | self.recipe_file = recipe_file | |
58 | ||
59 | if settings_file: | |
60 | if not os.path.isabs(settings_file): | |
61 | self.settings_file = os.path.abspath(settings_file) | |
62 | else: | |
63 | self.settings_file = settings_file | |
64 | ||
65 | if not os.path.isfile(self.settings_file): | |
66 | logger.error("No such file: {}".format(self.settings_file)) | |
67 | raise TestException("No settings file found") | |
68 | ||
69 | if not os.path.isfile(self.recipe_file): | |
70 | logger.error("No such file: {}".format(self.recipe_file)) | |
71 | raise TestException("No recipe file found") | |
72 | ||
73 | # Init logging | |
74 | if dir: | |
75 | self.log = logger.getChild(os.path.basename(self.path)) | |
76 | ||
77 | if recipe: | |
78 | self.log = logger.getChild(os.path.basename(self.recipe_file)) | |
79 | ||
80 | def read_settings(self): | |
81 | if self.settings_file: | |
82 | self.log.debug("Going to read all settings from the ini file") | |
83 | try: | |
84 | self.config = configparser.ConfigParser() | |
85 | self.config.read(self.settings_file) | |
86 | except BaseException as e: | |
87 | self.log.error("Failed to parse the config") | |
88 | raise e | |
89 | ||
90 | self.settings["name"] = self.config.get("GENERAL","name", fallback="") | |
91 | self.settings["description"] = self.config.get("GENERAL", "description", fallback="") | |
92 | self.settings["copy_to"] = self.config.get("GENERAL", "copy_to", fallback=None) | |
93 | self.settings["copy_from"] = self.config.get("GENERAL", "copy_from", fallback=None) | |
94 | self.settings["virtual_environ_path"] = self.config.get("VIRTUAL_ENVIRONMENT", "path", fallback=None) | |
95 | ||
96 | if not self.settings["virtual_environ_path"]: | |
97 | self.log.error("No path for virtual environment found.") | |
98 | raise TestException("No path for virtual environment found.") | |
99 | ||
100 | self.settings["virtual_environ_path"] = os.path.normpath(self.path + "/" + self.settings["virtual_environ_path"]) | |
101 | ||
102 | # Parse copy_from setting | |
103 | if self.settings["copy_from"]: | |
104 | self.log.debug("Going to parse the copy_from setting.") | |
105 | self.settings["copy_from"] = self.settings["copy_from"].split(",") | |
106 | ||
107 | tmp = [] | |
108 | for file in self.settings["copy_from"]: | |
109 | file = file.strip() | |
110 | # If file is empty we do not want to add it to the list | |
111 | if not file == "": | |
112 | # If we get an absolut path we do nothing | |
113 | # If not we add self.path to get an absolut path | |
114 | if not os.path.isabs(file): | |
115 | file = os.path.normpath(self.path + "/" + file) | |
116 | ||
117 | # We need to check if file is a valid file or dir | |
118 | if not (os.path.isdir(file) or os.path.isfile(file)): | |
119 | raise TestException("'{}' is not a valid file nor a valid directory".format(file)) | |
120 | ||
121 | self.log.debug("'{}' will be copied into all images".format(file)) | |
122 | tmp.append(file) | |
123 | ||
124 | self.settings["copy_from"] = tmp | |
125 | ||
126 | ||
127 | ||
128 | def virtual_environ_setup(self): | |
129 | self.virtual_environ = virtual_environ.Virtual_environ(self.settings["virtual_environ_path"]) | |
130 | ||
131 | self.virtual_networks = self.virtual_environ.get_networks() | |
132 | ||
133 | self.virtual_machines = self.virtual_environ.get_machines() | |
134 | ||
135 | def virtual_environ_start(self): | |
136 | for name in self.virtual_environ.network_names: | |
137 | self.virtual_networks[name].define() | |
138 | self.virtual_networks[name].start() | |
139 | ||
140 | for name in self.virtual_environ.machine_names: | |
141 | self.virtual_machines[name].define() | |
142 | self.virtual_machines[name].create_snapshot() | |
143 | # We can only copy files when we know which and to which dir | |
144 | if self.settings["copy_from"] and self.settings["copy_to"]: | |
145 | self.virtual_machines[name].copy_in(self.settings["copy_from"], self.settings["copy_to"]) | |
146 | self.virtual_machines[name].start() | |
147 | ||
148 | # Time to which all serial output log entries are relativ | |
149 | log_start_time = time.time() | |
150 | ||
151 | # Number of chars of the longest machine name | |
152 | longest_machine_name = self.virtual_environ.longest_machine_name | |
153 | ||
154 | self.log.info("Try to login on all machines") | |
155 | for name in self.virtual_environ.machine_names: | |
156 | self.log.info("Try to login on {}".format(name)) | |
157 | self.virtual_machines[name].login("{}/test.log".format(self.log_path), | |
158 | log_start_time=log_start_time, | |
159 | longest_machine_name=longest_machine_name) | |
160 | ||
161 | def load_recipe(self): | |
162 | self.log.info("Going to load the recipe") | |
163 | try: | |
164 | self.recipe = recipe.Recipe(self.recipe_file, machines=self.virtual_environ.machine_names) | |
165 | for line in self.recipe.recipe: | |
166 | self.log.debug(line) | |
167 | ||
168 | self.log.debug("This was the recipe") | |
169 | except BaseException as e: | |
170 | self.log.error("Failed to load recipe") | |
171 | raise e | |
172 | ||
173 | def run_recipe(self): | |
174 | for line in self.recipe.recipe: | |
175 | return_value = self.virtual_machines[line[0]].cmd(line[2]) | |
176 | self.log.debug("Return value is: {}".format(return_value)) | |
177 | if return_value != "0" and line[1] == "": | |
178 | raise TestException("Failed to execute command '{}' on {}, return code: {}".format(line[2],line[0], return_value)) | |
179 | elif return_value == "0" and line[1] == "!": | |
180 | raise TestException("Succeded to execute command '{}' on {}, return code: {}".format(line[2],line[0],return_value)) | |
181 | else: | |
182 | self.log.debug("Command '{}' on {} returned with: {}".format(line[2],line[0],return_value)) | |
183 | ||
184 | def virtual_environ_stop(self): | |
185 | for name in self.virtual_environ.machine_names: | |
186 | # We just catch exception here to avoid | |
187 | # that we stop the cleanup process if only one command fails | |
188 | try: | |
189 | self.virtual_machines[name].shutdown() | |
190 | self.virtual_machines[name].revert_snapshot() | |
191 | self.virtual_machines[name].undefine() | |
192 | except BaseException as e: | |
193 | self.log.exception(e) | |
194 | ||
195 | for name in self.virtual_environ.network_names: | |
196 | try: | |
197 | self.virtual_networks[name].undefine() | |
198 | except BaseException as e: | |
199 | self.log.exception(e) | |
200 |