blob: 55ba4afb69fc6e03eb8ef3d0d5f0b871c96d78a4 [file] [log] [blame]
Monty Taylor36ddea32017-10-02 10:05:17 -05001# Copyright (C) 2017 Red Hat, Inc.
2#
3# Licensed under the Apache License, Version 2.0 (the "License");
4# you may not use this file except in compliance with the License.
5# You may obtain a copy of the License at
6#
7# http://www.apache.org/licenses/LICENSE-2.0
8#
9# Unless required by applicable law or agreed to in writing, software
10# distributed under the License is distributed on an "AS IS" BASIS,
11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
12# implied.
13#
14# See the License for the specific language governing permissions and
15# limitations under the License.
16
17import re
18
19
20class VarGraph(object):
21 # This is based on the JobGraph from Zuul.
22
23 def __init__(self, vars):
24 self.vars = {}
25 self._varnames = set()
26 self._dependencies = {} # dependent_var_name -> set(parent_var_names)
27 for k, v in vars.items():
28 self._varnames.add(k)
29 for k, v in vars.items():
30 self._addVar(k, str(v))
31
32 bash_var_re = re.compile(r'\$\{?(\w+)')
33 def getDependencies(self, value):
34 return self.bash_var_re.findall(value)
35
36 def _addVar(self, key, value):
37 if key in self.vars:
38 raise Exception("Variable {} already added".format(key))
39 self.vars[key] = value
40 # Append the dependency information
41 self._dependencies.setdefault(key, set())
42 try:
43 for dependency in self.getDependencies(value):
44 if dependency == key:
45 # A variable is allowed to reference itself; no
46 # dependency link needed in that case.
47 continue
48 if dependency not in self._varnames:
49 # It's not necessary to create a link for an
50 # external variable.
51 continue
52 # Make sure a circular dependency is never created
53 ancestor_vars = self._getParentVarNamesRecursively(
54 dependency, soft=True)
55 ancestor_vars.add(dependency)
56 if any((key == anc_var) for anc_var in ancestor_vars):
57 raise Exception("Dependency cycle detected in var {}".
58 format(key))
59 self._dependencies[key].add(dependency)
60 except Exception:
61 del self.vars[key]
62 del self._dependencies[key]
63 raise
64
65 def getVars(self):
66 ret = []
67 keys = sorted(self.vars.keys())
68 seen = set()
69 for key in keys:
70 dependencies = self.getDependentVarsRecursively(key)
71 for var in dependencies + [key]:
72 if var not in seen:
73 ret.append((var, self.vars[var]))
74 seen.add(var)
75 return ret
76
77 def getDependentVarsRecursively(self, parent_var):
78 dependent_vars = []
79
80 current_dependent_vars = self._dependencies[parent_var]
81 for current_var in current_dependent_vars:
82 if current_var not in dependent_vars:
83 dependent_vars.append(current_var)
84 for dep in self.getDependentVarsRecursively(current_var):
85 if dep not in dependent_vars:
86 dependent_vars.append(dep)
87 return dependent_vars
88
89 def _getParentVarNamesRecursively(self, dependent_var, soft=False):
90 all_parent_vars = set()
91 vars_to_iterate = set([dependent_var])
92 while len(vars_to_iterate) > 0:
93 current_var = vars_to_iterate.pop()
94 current_parent_vars = self._dependencies.get(current_var)
95 if current_parent_vars is None:
96 if soft:
97 current_parent_vars = set()
98 else:
99 raise Exception("Dependent var {} not found: ".format(
100 dependent_var))
101 new_parent_vars = current_parent_vars - all_parent_vars
102 vars_to_iterate |= new_parent_vars
103 all_parent_vars |= new_parent_vars
104 return all_parent_vars
105
106
107class LocalConf(object):
108
Andrea Frittoli (andreaf)7d444652017-12-01 17:36:38 +0000109 def __init__(self, localrc, localconf, base_services, services, plugins):
Monty Taylor36ddea32017-10-02 10:05:17 -0500110 self.localrc = []
111 self.meta_sections = {}
112 if plugins:
113 self.handle_plugins(plugins)
Andrea Frittoli (andreaf)7d444652017-12-01 17:36:38 +0000114 if services or base_services:
115 self.handle_services(base_services, services or {})
Monty Taylor36ddea32017-10-02 10:05:17 -0500116 if localrc:
117 self.handle_localrc(localrc)
118 if localconf:
119 self.handle_localconf(localconf)
120
121 def handle_plugins(self, plugins):
122 for k, v in plugins.items():
123 if v:
124 self.localrc.append('enable_plugin {} {}'.format(k, v))
125
Andrea Frittoli (andreaf)7d444652017-12-01 17:36:38 +0000126 def handle_services(self, base_services, services):
127 enable_base_services = services.pop('base', True)
128 if enable_base_services and base_services:
129 self.localrc.append('ENABLED_SERVICES={}'.format(
130 ",".join(base_services)))
131 else:
Andrea Frittoli (andreaf)55511702017-11-30 15:49:39 +0000132 self.localrc.append('disable_all_services')
Monty Taylor36ddea32017-10-02 10:05:17 -0500133 for k, v in services.items():
134 if v is False:
135 self.localrc.append('disable_service {}'.format(k))
136 elif v is True:
137 self.localrc.append('enable_service {}'.format(k))
138
139 def handle_localrc(self, localrc):
140 vg = VarGraph(localrc)
141 for k, v in vg.getVars():
142 self.localrc.append('{}={}'.format(k, v))
143
144 def handle_localconf(self, localconf):
145 for phase, phase_data in localconf.items():
146 for fn, fn_data in phase_data.items():
147 ms_name = '[[{}|{}]]'.format(phase, fn)
148 ms_data = []
149 for section, section_data in fn_data.items():
150 ms_data.append('[{}]'.format(section))
151 for k, v in section_data.items():
152 ms_data.append('{} = {}'.format(k, v))
153 ms_data.append('')
154 self.meta_sections[ms_name] = ms_data
155
156 def write(self, path):
157 with open(path, 'w') as f:
158 f.write('[[local|localrc]]\n')
159 f.write('\n'.join(self.localrc))
160 f.write('\n\n')
161 for section, lines in self.meta_sections.items():
162 f.write('{}\n'.format(section))
163 f.write('\n'.join(lines))
164
165
166def main():
167 module = AnsibleModule(
168 argument_spec=dict(
169 plugins=dict(type='dict'),
Andrea Frittoli (andreaf)7d444652017-12-01 17:36:38 +0000170 base_services=dict(type='list'),
Monty Taylor36ddea32017-10-02 10:05:17 -0500171 services=dict(type='dict'),
172 localrc=dict(type='dict'),
173 local_conf=dict(type='dict'),
174 path=dict(type='str'),
175 )
176 )
177
178 p = module.params
179 lc = LocalConf(p.get('localrc'),
180 p.get('local_conf'),
Andrea Frittoli (andreaf)7d444652017-12-01 17:36:38 +0000181 p.get('base_services'),
Monty Taylor36ddea32017-10-02 10:05:17 -0500182 p.get('services'),
183 p.get('plugins'))
184 lc.write(p['path'])
185
186 module.exit_json()
187
188
189from ansible.module_utils.basic import * # noqa
190from ansible.module_utils.basic import AnsibleModule
191
192if __name__ == '__main__':
193 main()