server/checkintegrity.py
branchstable
changeset 6127 747e423093fc
parent 6112 913979c79244
child 6132 440df442d705
--- a/server/checkintegrity.py	Fri Aug 20 08:21:15 2010 +0200
+++ b/server/checkintegrity.py	Fri Aug 20 08:29:48 2010 +0200
@@ -15,8 +15,12 @@
 #
 # You should have received a copy of the GNU Lesser General Public License along
 # with CubicWeb.  If not, see <http://www.gnu.org/licenses/>.
-"""Check integrity of a CubicWeb repository. Hum actually only the system database
-is checked.
+"""Integrity checking tool for instances:
+
+* integrity of a CubicWeb repository. Hum actually only the system database is
+  checked.
+
+* consistency of multi-sources instance mapping file
 """
 
 from __future__ import with_statement
@@ -28,7 +32,7 @@
 
 from logilab.common.shellutils import ProgressBar
 
-from cubicweb.schema import PURE_VIRTUAL_RTYPES
+from cubicweb.schema import META_RTYPES, VIRTUAL_RTYPES, PURE_VIRTUAL_RTYPES
 from cubicweb.server.sqlutils import SQL_PREFIX
 from cubicweb.server.session import security_enabled
 
@@ -325,3 +329,94 @@
         session.set_pool()
         reindex_entities(repo.schema, session, withpb=withpb)
         cnx.commit()
+
+
+def warning(msg, *args):
+    if args:
+        msg = msg % args
+    print 'WARNING: %s' % msg
+
+def error(msg, *args):
+    if args:
+        msg = msg % args
+    print 'ERROR: %s' % msg
+
+def check_mapping(schema, mapping, warning=warning, error=error):
+    # first check stuff found in mapping file exists in the schema
+    for attr in ('support_entities', 'support_relations'):
+        for ertype in mapping[attr].keys():
+            try:
+                mapping[attr][ertype] = erschema = schema[ertype]
+            except KeyError:
+                error('reference to unknown type %s in %s', ertype, attr)
+                del mapping[attr][ertype]
+            else:
+                if erschema.final or erschema in META_RTYPES:
+                    error('type %s should not be mapped in %s', ertype, attr)
+                    del mapping[attr][ertype]
+    for attr in ('dont_cross_relations', 'cross_relations'):
+        for rtype in list(mapping[attr]):
+            try:
+                rschema = schema.rschema(rtype)
+            except KeyError:
+                error('reference to unknown relation type %s in %s', rtype, attr)
+                mapping[attr].remove(rtype)
+            else:
+                if rschema.final or rschema in VIRTUAL_RTYPES:
+                    error('relation type %s should not be mapped in %s',
+                          rtype, attr)
+                    mapping[attr].remove(rtype)
+    # check relation in dont_cross_relations aren't in support_relations
+    for rschema in mapping['dont_cross_relations']:
+        if rschema in mapping['support_relations']:
+            warning('relation %s is in dont_cross_relations and in support_relations',
+                    rschema)
+    # check relation in cross_relations are in support_relations
+    for rschema in mapping['cross_relations']:
+        if rschema not in mapping['support_relations']:
+            warning('relation %s is in cross_relations but not in support_relations',
+                    rschema)
+    # check for relation in both cross_relations and dont_cross_relations
+    for rschema in mapping['cross_relations'] & mapping['dont_cross_relations']:
+        error('relation %s is in both cross_relations and dont_cross_relations',
+              rschema)
+    # now check for more handy things
+    seen = set()
+    for eschema in mapping['support_entities'].values():
+        for rschema, ttypes, role in eschema.relation_definitions():
+            if rschema in META_RTYPES:
+                continue
+            ttypes = [ttype for ttype in ttypes if ttype in mapping['support_entities']]
+            if not rschema in mapping['support_relations']:
+                somethingprinted = False
+                for ttype in ttypes:
+                    rdef = rschema.role_rdef(eschema, ttype, role)
+                    seen.add(rdef)
+                    if rdef.role_cardinality(role) in '1+':
+                        error('relation %s with %s as %s and target type %s is '
+                              'mandatory but not supported',
+                              rschema, eschema, role, ttype)
+                        somethingprinted = True
+                    elif ttype in mapping['support_entities']:
+                        if rdef not in seen:
+                            warning('%s could be supported', rdef)
+                        somethingprinted = True
+                if rschema not in mapping['dont_cross_relations']:
+                    if role == 'subject' and rschema.inlined:
+                        error('inlined relation %s of %s should be supported',
+                              rschema, eschema)
+                    elif not somethingprinted and rschema not in seen:
+                        print 'you may want to specify something for %s' % rschema
+                        seen.add(rschema)
+            elif not ttypes:
+                warning('relation %s with %s as %s is supported but no target '
+                        'type supported', rschema, role, eschema)
+    for rschema in mapping['support_relations'].values():
+        if rschema in META_RTYPES:
+            continue
+        for subj, obj in rschema.rdefs:
+            if subj in mapping['support_entities'] and obj in mapping['support_entities']:
+                break
+        else:
+            error('relation %s is supported but none if its definitions '
+                  'matches supported entities', rschema)