]> git.ipfire.org Git - nitsi.git/blame - src/nitsi/recipe.py
Refactor disk class
[nitsi.git] / src / nitsi / recipe.py
CommitLineData
3a4eb2c7
JS
1#!/usr/bin/python3
2
1ed8ca9f 3import logging
6632e137 4import os
1ed8ca9f
JS
5
6logger = logging.getLogger("nitsi.recipe")
7
3a4eb2c7
JS
8
9
10class RecipeExeption(Exception):
61b44c10
JS
11 def __init__(self, message):
12 self.message = message
3a4eb2c7
JS
13
14
15
16# Should read the test, check if the syntax are valid
17# and return tuples with the ( host, command ) structure
ee227ea1 18class Recipe():
ca4917b4 19 def __init__(self, path, circle=[], machines=[], fallback_machines=[], include_path=None):
3a4eb2c7 20 self.recipe_file = path
518441de
JS
21 try:
22 self.path = os.path.dirname(self.recipe_file)
c9c4a606 23 self.path = os.path.abspath(self.path)
518441de
JS
24 self.name = os.path.basename(self.path)
25 except BaseException as e:
c9c4a606 26 logger.error("Failed to get the path to this recipe")
518441de
JS
27 raise e
28
29 self.log = logger.getChild(self.name)
3a4eb2c7 30 self.log.debug("Path of recipe is: {}".format(self.recipe_file))
ca4917b4
JS
31
32 # This path must be absolut
33 self.include_path = include_path
34
35 if self.include_path and not os.path.isabs(self.include_path):
36 raise RecipeExeption("Include path must be absolut.")
37
38 self.log.debug("Include path is: {}".format(self.include_path))
39
3a4eb2c7 40 self._recipe = None
b860b441 41 self._machines = machines
821394c3 42 self._fallback_machines = fallback_machines
b860b441
JS
43
44 self.log.debug("Machine names we use when we substitute the all statement: {}".format(self._machines))
3a4eb2c7 45
821394c3 46 self.log.debug("Length of the cirle list {}".format(len(circle)))
3a4eb2c7
JS
47 self.in_recursion = True
48 if len(circle) == 0:
49 self.in_recursion = False
50
821394c3
JS
51 self.log.debug("We are in a recursion: {}".format(self.in_recursion))
52
3a4eb2c7 53 self.circle = circle
2fa4467d 54 self.log.debug("Recipes we have already included: {}".format(self.circle))
3a4eb2c7
JS
55
56 if not os.path.isfile(self.recipe_file):
faff2d5c 57 self.log.error("{} is not a file".format(self.recipe_file))
624e9083 58 raise RecipeExeption("{} is not a file".format(self.recipe_file))
3a4eb2c7
JS
59
60 try:
61 with open(self.recipe_file) as fobj:
62 self.raw_recipe = fobj.readlines()
63 except FileNotFoundError as error:
624e9083 64 self.log.error("No such file: {}".format(self.recipe_file))
61b44c10 65 raise error
3a4eb2c7
JS
66
67 @property
68 def recipe(self):
69 if not self._recipe:
70 self.parse()
71
72 return self._recipe
73
74 @property
75 def machines(self):
3a4eb2c7
JS
76 return self._machines
77
78 def parse(self):
79 self._recipe = []
80 i = 1
81 for line in self.raw_recipe:
6cb7e939
JS
82 # Check if the line is empty
83 if line.strip() == "":
84 self.log.debug("Skipping empty line {}".format(i))
85 i = i + 1
86 continue
87
88 # Check if the line is a comment
89 if line.strip().startswith("#"):
90 self.log.debug("Skipping comment in line {}".format(i))
91 i = i + 1
92 continue
93
2e8d0473 94 raw_line = line.split(":", 1)
3a4eb2c7
JS
95 if len(raw_line) < 2:
96 self.log.error("Error parsing the recipe in line {}".format(i))
61b44c10 97 raise RecipeExeption("Error parsing the recipe in line {}".format(i))
3a4eb2c7 98 cmd = raw_line[1].strip()
821394c3 99
3a4eb2c7
JS
100 raw_line = raw_line[0].strip().split(" ")
101 if len(raw_line) == 0:
102 self.log.error("Failed to parse the recipe in line {}".format(i))
61b44c10 103 raise RecipeExeption("Failed to parse the recipe in line {}".format(i))
3a4eb2c7
JS
104
105 if raw_line[0].strip() == "":
106 self.log.error("Failed to parse the recipe in line {}".format(i))
61b44c10 107 raise RecipeExeption("Failed to parse the recipe in line {}".format(i))
3a4eb2c7
JS
108
109 machine = raw_line[0].strip()
110
111 if len(raw_line) == 2:
112 extra = raw_line[1].strip()
113 else:
114 extra = ""
115
116 # We could get a machine here or a include statement
117 if machine == "include":
118 path = cmd.strip()
ca4917b4
JS
119 if self.include_path:
120 path = os.path.normpath(self.include_path + "/" + path)
121 else:
122 path = os.path.normpath(self.path + "/" + path)
c9c4a606
JS
123
124 # If we did not get a valid file we asume that we get a valid path to a test.
125 if os.path.isdir(path):
126 path = path + "/recipe"
127
ca4917b4
JS
128 self.log.debug("Path of recipe to include is: {}".format(path))
129
3a4eb2c7
JS
130 if path in self.circle:
131 self.log.error("Detect import loop!")
61b44c10 132 raise RecipeExeption("Detect import loop!")
3a4eb2c7 133 self.circle.append(path)
ca4917b4 134 recipe_to_include = Recipe(path, circle=self.circle, include_path=self.include_path)
3a4eb2c7
JS
135
136 if machine == "include":
137 self._recipe.extend(recipe_to_include.recipe)
138 else:
139 # Support also something like 'alice,bob: echo'
140 machines = machine.split(",")
141 for machine in machines:
142 self._recipe.append((machine.strip(), extra.strip(), cmd.strip()))
3a4eb2c7 143
5945ce2a
JS
144 # Increase the line number by one
145 i = i + 1
3a4eb2c7 146
5945ce2a
JS
147 # Substitue the all statement
148 if not self.in_recursion:
149 self.log.debug("We are not in a recursion")
150 # We will store the machine names we use to substitute the all statement
151 # in tmp_machines to keep the code which actually does the substitution clear
152 tmp_machines = None
153
154 # Check if we get a setting to substitute the all statement
155 if len(self.machines) != 0:
156 tmp_machines = self.machines
157
158 # Second try to fill tmp_machines
159 if not tmp_machines:
160 # dertermine machines we use in this recipe
161 tmp = []
162 for line in self.recipe:
163 self.log.debug(line)
164 if not line[0] in tmp and line[0] != "all":
165 tmp.append(line[0])
166
167 self.log.debug("Machines except all in the recipe: {}".format(tmp))
168
169 # Check if we got anything else then all: in th recipe
170 if len(tmp) != 0:
171 tmp_machines = tmp
172
173 # If we get here we are using all machines in the virtual environment as fallback value
174 if not tmp_machines:
175 tmp_machines = self._fallback_machines
176
177 tmp_recipe = []
178 for line in self._recipe:
179 if line[0] != "all":
180 tmp_recipe.append(line)
181 else:
182 for machine in tmp_machines:
183 tmp_recipe.append((machine.strip(), line[1], line[2]))
184
ca4917b4 185 self._recipe = tmp_recipe