13 # FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more |
13 # FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more |
14 # details. |
14 # details. |
15 # |
15 # |
16 # You should have received a copy of the GNU Lesser General Public License along |
16 # You should have received a copy of the GNU Lesser General Public License along |
17 # with CubicWeb. If not, see <http://www.gnu.org/licenses/>. |
17 # with CubicWeb. If not, see <http://www.gnu.org/licenses/>. |
18 """Check integrity of a CubicWeb repository. Hum actually only the system database |
18 """Integrity checking tool for instances: |
19 is checked. |
19 |
|
20 * integrity of a CubicWeb repository. Hum actually only the system database is |
|
21 checked. |
|
22 |
|
23 * consistency of multi-sources instance mapping file |
20 """ |
24 """ |
21 |
25 |
22 from __future__ import with_statement |
26 from __future__ import with_statement |
23 |
27 |
24 __docformat__ = "restructuredtext en" |
28 __docformat__ = "restructuredtext en" |
26 import sys |
30 import sys |
27 from datetime import datetime |
31 from datetime import datetime |
28 |
32 |
29 from logilab.common.shellutils import ProgressBar |
33 from logilab.common.shellutils import ProgressBar |
30 |
34 |
31 from cubicweb.schema import PURE_VIRTUAL_RTYPES |
35 from cubicweb.schema import META_RTYPES, VIRTUAL_RTYPES, PURE_VIRTUAL_RTYPES |
32 from cubicweb.server.sqlutils import SQL_PREFIX |
36 from cubicweb.server.sqlutils import SQL_PREFIX |
33 from cubicweb.server.session import security_enabled |
37 from cubicweb.server.session import security_enabled |
34 |
38 |
35 def has_eid(session, sqlcursor, eid, eids): |
39 def has_eid(session, sqlcursor, eid, eids): |
36 """return true if the eid is a valid eid""" |
40 """return true if the eid is a valid eid""" |
323 if reindex: |
327 if reindex: |
324 cnx.rollback() |
328 cnx.rollback() |
325 session.set_pool() |
329 session.set_pool() |
326 reindex_entities(repo.schema, session, withpb=withpb) |
330 reindex_entities(repo.schema, session, withpb=withpb) |
327 cnx.commit() |
331 cnx.commit() |
|
332 |
|
333 |
|
334 def warning(msg, *args): |
|
335 if args: |
|
336 msg = msg % args |
|
337 print 'WARNING: %s' % msg |
|
338 |
|
339 def error(msg, *args): |
|
340 if args: |
|
341 msg = msg % args |
|
342 print 'ERROR: %s' % msg |
|
343 |
|
344 def check_mapping(schema, mapping, warning=warning, error=error): |
|
345 # first check stuff found in mapping file exists in the schema |
|
346 for attr in ('support_entities', 'support_relations'): |
|
347 for ertype in mapping[attr].keys(): |
|
348 try: |
|
349 mapping[attr][ertype] = erschema = schema[ertype] |
|
350 except KeyError: |
|
351 error('reference to unknown type %s in %s', ertype, attr) |
|
352 del mapping[attr][ertype] |
|
353 else: |
|
354 if erschema.final or erschema in META_RTYPES: |
|
355 error('type %s should not be mapped in %s', ertype, attr) |
|
356 del mapping[attr][ertype] |
|
357 for attr in ('dont_cross_relations', 'cross_relations'): |
|
358 for rtype in list(mapping[attr]): |
|
359 try: |
|
360 rschema = schema.rschema(rtype) |
|
361 except KeyError: |
|
362 error('reference to unknown relation type %s in %s', rtype, attr) |
|
363 mapping[attr].remove(rtype) |
|
364 else: |
|
365 if rschema.final or rschema in VIRTUAL_RTYPES: |
|
366 error('relation type %s should not be mapped in %s', |
|
367 rtype, attr) |
|
368 mapping[attr].remove(rtype) |
|
369 # check relation in dont_cross_relations aren't in support_relations |
|
370 for rschema in mapping['dont_cross_relations']: |
|
371 if rschema in mapping['support_relations']: |
|
372 warning('relation %s is in dont_cross_relations and in support_relations', |
|
373 rschema) |
|
374 # check relation in cross_relations are in support_relations |
|
375 for rschema in mapping['cross_relations']: |
|
376 if rschema not in mapping['support_relations']: |
|
377 warning('relation %s is in cross_relations but not in support_relations', |
|
378 rschema) |
|
379 # check for relation in both cross_relations and dont_cross_relations |
|
380 for rschema in mapping['cross_relations'] & mapping['dont_cross_relations']: |
|
381 error('relation %s is in both cross_relations and dont_cross_relations', |
|
382 rschema) |
|
383 # now check for more handy things |
|
384 seen = set() |
|
385 for eschema in mapping['support_entities'].values(): |
|
386 for rschema, ttypes, role in eschema.relation_definitions(): |
|
387 if rschema in META_RTYPES: |
|
388 continue |
|
389 ttypes = [ttype for ttype in ttypes if ttype in mapping['support_entities']] |
|
390 if not rschema in mapping['support_relations']: |
|
391 somethingprinted = False |
|
392 for ttype in ttypes: |
|
393 rdef = rschema.role_rdef(eschema, ttype, role) |
|
394 seen.add(rdef) |
|
395 if rdef.role_cardinality(role) in '1+': |
|
396 error('relation %s with %s as %s and target type %s is ' |
|
397 'mandatory but not supported', |
|
398 rschema, eschema, role, ttype) |
|
399 somethingprinted = True |
|
400 elif ttype in mapping['support_entities']: |
|
401 if rdef not in seen: |
|
402 warning('%s could be supported', rdef) |
|
403 somethingprinted = True |
|
404 if rschema not in mapping['dont_cross_relations']: |
|
405 if role == 'subject' and rschema.inlined: |
|
406 error('inlined relation %s of %s should be supported', |
|
407 rschema, eschema) |
|
408 elif not somethingprinted and rschema not in seen: |
|
409 print 'you may want to specify something for %s' % rschema |
|
410 seen.add(rschema) |
|
411 elif not ttypes: |
|
412 warning('relation %s with %s as %s is supported but no target ' |
|
413 'type supported', rschema, role, eschema) |
|
414 for rschema in mapping['support_relations'].values(): |
|
415 if rschema in META_RTYPES: |
|
416 continue |
|
417 for subj, obj in rschema.rdefs: |
|
418 if subj in mapping['support_entities'] and obj in mapping['support_entities']: |
|
419 break |
|
420 else: |
|
421 error('relation %s is supported but none if its definitions ' |
|
422 'matches supported entities', rschema) |