]>
Commit | Line | Data |
---|---|---|
3a4eb2c7 JS |
1 | #!/usr/bin/python3 |
2 | ||
1ed8ca9f | 3 | import logging |
6632e137 | 4 | import os |
1ed8ca9f JS |
5 | |
6 | logger = logging.getLogger("nitsi.recipe") | |
7 | ||
3a4eb2c7 JS |
8 | |
9 | ||
10 | class 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 | 18 | class Recipe(): |
b860b441 | 19 | def __init__(self, path, circle=[], machines=[]): |
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 JS |
30 | self.log.debug("Path of recipe is: {}".format(self.recipe_file)) |
31 | self._recipe = None | |
b860b441 | 32 | self._machines = machines |
821394c3 | 33 | self._fallback_machines = fallback_machines |
b860b441 JS |
34 | |
35 | self.log.debug("Machine names we use when we substitute the all statement: {}".format(self._machines)) | |
3a4eb2c7 | 36 | |
821394c3 | 37 | self.log.debug("Length of the cirle list {}".format(len(circle))) |
3a4eb2c7 JS |
38 | self.in_recursion = True |
39 | if len(circle) == 0: | |
40 | self.in_recursion = False | |
41 | ||
821394c3 JS |
42 | self.log.debug("We are in a recursion: {}".format(self.in_recursion)) |
43 | ||
3a4eb2c7 | 44 | self.circle = circle |
2fa4467d | 45 | self.log.debug("Recipes we have already included: {}".format(self.circle)) |
3a4eb2c7 JS |
46 | |
47 | if not os.path.isfile(self.recipe_file): | |
faff2d5c | 48 | self.log.error("{} is not a file".format(self.recipe_file)) |
624e9083 | 49 | raise RecipeExeption("{} is not a file".format(self.recipe_file)) |
3a4eb2c7 JS |
50 | |
51 | try: | |
52 | with open(self.recipe_file) as fobj: | |
53 | self.raw_recipe = fobj.readlines() | |
54 | except FileNotFoundError as error: | |
624e9083 | 55 | self.log.error("No such file: {}".format(self.recipe_file)) |
61b44c10 | 56 | raise error |
3a4eb2c7 JS |
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): | |
3a4eb2c7 JS |
67 | return self._machines |
68 | ||
69 | def parse(self): | |
70 | self._recipe = [] | |
71 | i = 1 | |
72 | for line in self.raw_recipe: | |
6cb7e939 JS |
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 | ||
2e8d0473 | 85 | raw_line = line.split(":", 1) |
3a4eb2c7 JS |
86 | if len(raw_line) < 2: |
87 | self.log.error("Error parsing the recipe in line {}".format(i)) | |
61b44c10 | 88 | raise RecipeExeption("Error parsing the recipe in line {}".format(i)) |
3a4eb2c7 | 89 | cmd = raw_line[1].strip() |
821394c3 | 90 | |
3a4eb2c7 JS |
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)) | |
61b44c10 | 94 | raise RecipeExeption("Failed to parse the recipe in line {}".format(i)) |
3a4eb2c7 JS |
95 | |
96 | if raw_line[0].strip() == "": | |
97 | self.log.error("Failed to parse the recipe in line {}".format(i)) | |
61b44c10 | 98 | raise RecipeExeption("Failed to parse the recipe in line {}".format(i)) |
3a4eb2c7 JS |
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) | |
c9c4a606 JS |
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 | ||
3a4eb2c7 JS |
116 | if path in self.circle: |
117 | self.log.error("Detect import loop!") | |
61b44c10 | 118 | raise RecipeExeption("Detect import loop!") |
3a4eb2c7 | 119 | self.circle.append(path) |
ee227ea1 | 120 | recipe_to_include = Recipe(path, circle=self.circle) |
3a4eb2c7 JS |
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 |