156 self.run_arg(appid) |
156 self.run_arg(appid) |
157 |
157 |
158 |
158 |
159 # base commands ############################################################### |
159 # base commands ############################################################### |
160 |
160 |
|
161 def version_strictly_lower(a,b): |
|
162 if a: |
|
163 a = a.split('.') |
|
164 if b: |
|
165 b = b.split('.') |
|
166 return a < b |
|
167 |
|
168 def max_version(a, b): |
|
169 return '.'.join(max(a.split('.'), b.split('.'))) |
|
170 |
|
171 class ConfigurationProblem(object): |
|
172 """Each cube has its own list of dependencies on other cubes/versions. |
|
173 |
|
174 The ConfigurationProblem is used to record the loaded cubes, then to detect |
|
175 inconsistencies in their dependencies. |
|
176 |
|
177 See configuration management on wikipedia for litterature. |
|
178 """ |
|
179 |
|
180 def __init__(self): |
|
181 self.cubes = {} |
|
182 |
|
183 def add_cube(self, name, info): |
|
184 self.cubes[name] = info |
|
185 |
|
186 def solve(self): |
|
187 self.warnings = [] |
|
188 self.errors = [] |
|
189 self.read_constraints() |
|
190 for cube, versions in sorted(self.constraints.items()): |
|
191 oper, version = None, None |
|
192 # simplify constraints |
|
193 if versions: |
|
194 for constraint in versions: |
|
195 op, ver = constraint.split() |
|
196 if oper is None: |
|
197 oper = op |
|
198 version = ver |
|
199 elif op == '>=' and oper == '>=': |
|
200 version = max_version(ver, version) |
|
201 else: |
|
202 print 'unable to handle this case', oper, version, op, ver |
|
203 # "solve" constraint satisfaction problem |
|
204 if cube not in self.cubes: |
|
205 self.errors.append( ('add', cube, version) ) |
|
206 elif versions: |
|
207 lower_strict = version_strictly_lower(self.cubes[cube].version, version) |
|
208 if oper in ('>=','='): |
|
209 if lower_strict: |
|
210 self.errors.append( ('update', cube, version) ) |
|
211 else: |
|
212 print 'unknown operator', oper |
|
213 |
|
214 def read_constraints(self): |
|
215 self.constraints = {} |
|
216 self.reverse_constraints = {} |
|
217 for cube, info in self.cubes.items(): |
|
218 if hasattr(info,'__depends_cubes__'): |
|
219 use = info.__depends_cubes__ |
|
220 if not isinstance(use, dict): |
|
221 use = dict((key,None) for key in use) |
|
222 self.warnings.append('cube %s should define __depends_cubes__ as a dict not a list') |
|
223 else: |
|
224 self.warnings.append('cube %s should define __depends_cubes__' % cube) |
|
225 use = dict((key,None) for key in info.__use__) |
|
226 for name, constraint in use.items(): |
|
227 self.constraints.setdefault(name,set()) |
|
228 if constraint: |
|
229 self.constraints[name].add(constraint) |
|
230 self.reverse_constraints.setdefault(name, set()).add(cube) |
|
231 |
161 class ListCommand(Command): |
232 class ListCommand(Command): |
162 """List configurations, cubes and instances. |
233 """List configurations, cubes and instances. |
163 |
234 |
164 list available configurations, installed cubes, and registered instances |
235 list available configurations, installed cubes, and registered instances |
165 """ |
236 """ |
198 if cube in ('CVS', '.svn', 'shared', '.hg'): |
270 if cube in ('CVS', '.svn', 'shared', '.hg'): |
199 continue |
271 continue |
200 try: |
272 try: |
201 tinfo = cwcfg.cube_pkginfo(cube) |
273 tinfo = cwcfg.cube_pkginfo(cube) |
202 tversion = tinfo.version |
274 tversion = tinfo.version |
|
275 cfgpb.add_cube(cube, tinfo) |
203 except ConfigurationError: |
276 except ConfigurationError: |
204 tinfo = None |
277 tinfo = None |
205 tversion = '[missing cube information]' |
278 tversion = '[missing cube information]' |
206 print '* %s %s' % (cube.ljust(namesize), tversion) |
279 print '* %s %s' % (cube.ljust(namesize), tversion) |
207 if self.config.verbose: |
280 if self.config.verbose: |
233 print ' (BROKEN instance, %s)' % exc |
306 print ' (BROKEN instance, %s)' % exc |
234 continue |
307 continue |
235 else: |
308 else: |
236 print 'No instance available in %s' % regdir |
309 print 'No instance available in %s' % regdir |
237 print |
310 print |
238 |
311 # configuration management problem solving |
|
312 cfgpb.solve() |
|
313 if cfgpb.warnings: |
|
314 print 'Warnings:\n', '\n'.join('* '+txt for txt in cfgpb.warnings) |
|
315 if cfgpb.errors: |
|
316 print 'Errors:' |
|
317 for op, cube, version in cfgpb.errors: |
|
318 if op == 'add': |
|
319 print '* cube', cube, |
|
320 if version: |
|
321 print ' version', version, |
|
322 print 'is not installed, but required by %s' % ' '.join(cfgpb.reverse_constraints[cube]) |
|
323 else: |
|
324 print '* cube %s version %s is installed, but version %s is required by (%s)' % ( |
|
325 cube, cfgpb.cubes[cube].version, version, ', '.join(cfgpb.reverse_constraints[cube])) |
239 |
326 |
240 class CreateInstanceCommand(Command): |
327 class CreateInstanceCommand(Command): |
241 """Create an instance from a cube. This is an unified |
328 """Create an instance from a cube. This is an unified |
242 command which can handle web / server / all-in-one installation |
329 command which can handle web / server / all-in-one installation |
243 according to available parts of the software library and of the |
330 according to available parts of the software library and of the |