]> git.ipfire.org Git - nitsi.git/blob - src/nitsi/recipe.py
Improve code readability and log messages
[nitsi.git] / src / nitsi / recipe.py
1 #!/usr/bin/python3
2
3 import logging
4 import os
5
6 logger = logging.getLogger("nitsi.recipe")
7
8
9
10 class RecipeExeption(Exception):
11 def __init__(self, message):
12 self.message = message
13
14
15
16 # Should read the test, check if the syntax are valid
17 # and return tuples with the ( host, command ) structure
18 class Recipe():
19 def __init__(self, path, circle=[], machines=[]):
20 self.recipe_file = path
21 try:
22 self.path = os.path.dirname(self.recipe_file)
23 self.path = os.path.abspath(self.path)
24 self.name = os.path.basename(self.path)
25 except BaseException as e:
26 logger.error("Failed to get the path to this recipe")
27 raise e
28
29 self.log = logger.getChild(self.name)
30 self.log.debug("Path of recipe is: {}".format(self.recipe_file))
31 self._recipe = None
32 self._machines = machines
33 self._fallback_machines = fallback_machines
34
35 self.log.debug("Machine names we use when we substitute the all statement: {}".format(self._machines))
36
37 self.log.debug("Length of the cirle list {}".format(len(circle)))
38 self.in_recursion = True
39 if len(circle) == 0:
40 self.in_recursion = False
41
42 self.log.debug("We are in a recursion: {}".format(self.in_recursion))
43
44 self.circle = circle
45 self.log.debug("Recipes we have already included: {}".format(self.circle))
46
47 if not os.path.isfile(self.recipe_file):
48 self.log.error("{} is not a file".format(self.recipe_file))
49 raise RecipeExeption("{} is not a file".format(self.recipe_file))
50
51 try:
52 with open(self.recipe_file) as fobj:
53 self.raw_recipe = fobj.readlines()
54 except FileNotFoundError as error:
55 self.log.error("No such file: {}".format(self.recipe_file))
56 raise error
57
58 @property
59 def recipe(self):
60 if not self._recipe:
61 self.parse()
62
63 return self._recipe
64
65 @property
66 def machines(self):
67 return self._machines
68
69 def parse(self):
70 self._recipe = []
71 i = 1
72 for line in self.raw_recipe:
73 # Check if the line is empty
74 if line.strip() == "":
75 self.log.debug("Skipping empty line {}".format(i))
76 i = i + 1
77 continue
78
79 # Check if the line is a comment
80 if line.strip().startswith("#"):
81 self.log.debug("Skipping comment in line {}".format(i))
82 i = i + 1
83 continue
84
85 raw_line = line.split(":", 1)
86 if len(raw_line) < 2:
87 self.log.error("Error parsing the recipe in line {}".format(i))
88 raise RecipeExeption("Error parsing the recipe in line {}".format(i))
89 cmd = raw_line[1].strip()
90
91 raw_line = raw_line[0].strip().split(" ")
92 if len(raw_line) == 0:
93 self.log.error("Failed to parse the recipe in line {}".format(i))
94 raise RecipeExeption("Failed to parse the recipe in line {}".format(i))
95
96 if raw_line[0].strip() == "":
97 self.log.error("Failed to parse the recipe in line {}".format(i))
98 raise RecipeExeption("Failed to parse the recipe in line {}".format(i))
99
100 machine = raw_line[0].strip()
101
102 if len(raw_line) == 2:
103 extra = raw_line[1].strip()
104 else:
105 extra = ""
106
107 # We could get a machine here or a include statement
108 if machine == "include":
109 path = cmd.strip()
110 path = os.path.normpath(self.path + "/" + path)
111
112 # If we did not get a valid file we asume that we get a valid path to a test.
113 if os.path.isdir(path):
114 path = path + "/recipe"
115
116 if path in self.circle:
117 self.log.error("Detect import loop!")
118 raise RecipeExeption("Detect import loop!")
119 self.circle.append(path)
120 recipe_to_include = Recipe(path, circle=self.circle)
121
122 if machine == "include":
123 self._recipe.extend(recipe_to_include.recipe)
124 else:
125 # Support also something like 'alice,bob: echo'
126 machines = machine.split(",")
127 for machine in machines:
128 self._recipe.append((machine.strip(), extra.strip(), cmd.strip()))
129 i = i + 1
130
131 if not self.in_recursion:
132 tmp_recipe = []
133 for line in self._recipe:
134 if line[0] != "all":
135 tmp_recipe.append(line)
136 else:
137 for machine in self.machines:
138 tmp_recipe.append((machine.strip(), line[1], line[2]))
139
140 self._recipe = tmp_recipe