14 from os.path import exists, join, basename, splitext |
14 from os.path import exists, join, basename, splitext |
15 |
15 |
16 from logilab.common.decorators import cached |
16 from logilab.common.decorators import cached |
17 from logilab.common.configuration import REQUIRED, read_old_config |
17 from logilab.common.configuration import REQUIRED, read_old_config |
18 from logilab.common.shellutils import ASK |
18 from logilab.common.shellutils import ASK |
|
19 from logilab.common.changelog import Version |
19 |
20 |
20 from cubicweb import ConfigurationError |
21 from cubicweb import ConfigurationError |
21 |
22 |
22 |
23 |
23 def filter_scripts(config, directory, fromversion, toversion, quiet=True): |
24 def filter_scripts(config, directory, fromversion, toversion, quiet=True): |
372 |
373 |
373 |
374 |
374 from logging import getLogger |
375 from logging import getLogger |
375 from cubicweb import set_log_methods |
376 from cubicweb import set_log_methods |
376 set_log_methods(MigrationHelper, getLogger('cubicweb.migration')) |
377 set_log_methods(MigrationHelper, getLogger('cubicweb.migration')) |
|
378 |
|
379 |
|
380 def version_strictly_lower(a, b): |
|
381 if a: |
|
382 a = Version(a) |
|
383 if b: |
|
384 b = Version(b) |
|
385 return a < b |
|
386 |
|
387 def max_version(a, b): |
|
388 return str(max(Version(a), Version(b))) |
|
389 |
|
390 class ConfigurationProblem(object): |
|
391 """Each cube has its own list of dependencies on other cubes/versions. |
|
392 |
|
393 The ConfigurationProblem is used to record the loaded cubes, then to detect |
|
394 inconsistencies in their dependencies. |
|
395 |
|
396 See configuration management on wikipedia for litterature. |
|
397 """ |
|
398 |
|
399 def __init__(self, config): |
|
400 self.cubes = {} |
|
401 self.config = config |
|
402 |
|
403 def add_cube(self, name, version): |
|
404 self.cubes[name] = version |
|
405 |
|
406 def solve(self): |
|
407 self.warnings = [] |
|
408 self.errors = [] |
|
409 self.read_constraints() |
|
410 for cube, versions in sorted(self.constraints.items()): |
|
411 oper, version = None, None |
|
412 # simplify constraints |
|
413 if versions: |
|
414 for constraint in versions: |
|
415 op, ver = constraint |
|
416 if oper is None: |
|
417 oper = op |
|
418 version = ver |
|
419 elif op == '>=' and oper == '>=': |
|
420 version = max_version(ver, version) |
|
421 else: |
|
422 print 'unable to handle this case', oper, version, op, ver |
|
423 # "solve" constraint satisfaction problem |
|
424 if cube not in self.cubes: |
|
425 self.errors.append( ('add', cube, version) ) |
|
426 elif versions: |
|
427 lower_strict = version_strictly_lower(self.cubes[cube], version) |
|
428 if oper in ('>=','='): |
|
429 if lower_strict: |
|
430 self.errors.append( ('update', cube, version) ) |
|
431 else: |
|
432 print 'unknown operator', oper |
|
433 |
|
434 def read_constraints(self): |
|
435 self.constraints = {} |
|
436 self.reverse_constraints = {} |
|
437 for cube in self.cubes: |
|
438 use = self.config.cube_dependencies(cube) |
|
439 for name, constraint in use.iteritems(): |
|
440 self.constraints.setdefault(name,set()) |
|
441 if constraint: |
|
442 try: |
|
443 oper, version = constraint.split() |
|
444 self.constraints[name].add( (oper, version) ) |
|
445 except: |
|
446 self.warnings.append( |
|
447 'cube %s depends on %s but constraint badly ' |
|
448 'formatted: %s' % (cube, name, constraint)) |
|
449 self.reverse_constraints.setdefault(name, set()).add(cube) |