]> git.ipfire.org Git - nitsi.git/blob - src/nitsi/test.py
Add the ability to set all settings from the command line
[nitsi.git] / src / nitsi / test.py
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 from . import settings
12
13 logger = logging.getLogger("nitsi.test")
14
15
16 class TestException(Exception):
17 def __init__(self, message):
18 self.message = message
19
20 class Test():
21 def __init__(self, log_path, dir=None, recipe_file=None, settings_file=None, cmd_settings=None):
22 # init settings var
23 self.settings = {}
24
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
32 self.cmd_settings = cmd_settings
33 self.log_path = log_path
34
35 # Init all vars with None
36 self.settings_file = None
37 self.recipe_file = None
38 self.path = None
39
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
72
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
86
87
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))
94
95 def read_settings(self):
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)
110
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"])
115
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))
120
121 # Update all settings from the cmd
122 self.settings.update(self.cmd_settings)
123
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"])
126
127
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.")
132
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]))
137
138
139 def virtual_environ_setup(self):
140 self.virtual_environ = virtual_environ.Virtual_environ(self.settings["virtual_environ_path"])
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):
147 for name in self.virtual_environ.network_names:
148 self.virtual_networks[name].define()
149 self.virtual_networks[name].start()
150
151 for name in self.virtual_environ.machine_names:
152 self.virtual_machines[name].define()
153 self.virtual_machines[name].create_snapshot()
154 # We can only copy files when we know which and to which dir
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"])
157 self.virtual_machines[name].start()
158
159 # Time to which all serial output log entries are relativ
160 log_start_time = time.time()
161
162 # Number of chars of the longest machine name
163 longest_machine_name = self.virtual_environ.longest_machine_name
164
165 self.log.info("Try to login on all machines")
166 for name in self.virtual_environ.machine_names:
167 self.log.info("Try to login on {}".format(name))
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)
171
172 def load_recipe(self):
173 self.log.info("Going to load the recipe")
174 try:
175 self.recipe = recipe.Recipe(self.recipe_file, machines=self.virtual_environ.machine_names)
176 for line in self.recipe.recipe:
177 self.log.debug(line)
178
179 self.log.debug("This was the recipe")
180 except BaseException as e:
181 self.log.error("Failed to load recipe")
182 raise e
183
184 def run_recipe(self):
185 for line in self.recipe.recipe:
186 return_value = self.virtual_machines[line[0]].cmd(line[2])
187 self.log.debug("Return value is: {}".format(return_value))
188 if return_value != "0" and line[1] == "":
189 raise TestException("Failed to execute command '{}' on {}, return code: {}".format(line[2],line[0], return_value))
190 elif return_value == "0" and line[1] == "!":
191 raise TestException("Succeded to execute command '{}' on {}, return code: {}".format(line[2],line[0],return_value))
192 else:
193 self.log.debug("Command '{}' on {} returned with: {}".format(line[2],line[0],return_value))
194
195 def virtual_environ_stop(self):
196 for name in self.virtual_environ.machine_names:
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)
205
206 for name in self.virtual_environ.network_names:
207 try:
208 self.virtual_networks[name].undefine()
209 except BaseException as e:
210 self.log.exception(e)
211