[ms, c-c] new command checking for consistency / potentian flaws and enhancements of mapping file of a multi-sources instance
--- 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)
--- a/server/serverctl.py Fri Aug 20 08:21:15 2010 +0200
+++ b/server/serverctl.py Fri Aug 20 08:29:48 2010 +0200
@@ -865,6 +865,34 @@
mih.cmd_synchronize_schema()
+class CheckMappingCommand(Command):
+ """Check content of the mapping file of an external source.
+
+ The mapping is checked against the instance's schema, searching for
+ inconsistencies or stuff you may have forgotten. It's higly recommanded to
+ run it when you setup a multi-sources instance.
+
+ <instance>
+ the identifier of the instance.
+
+ <mapping file>
+ the mapping file to check.
+ """
+ name = 'check-mapping'
+ arguments = '<instance> <mapping file>'
+ min_args = max_args = 2
+
+ def run(self, args):
+ from cubicweb.server.checkintegrity import check_mapping
+ from cubicweb.server.sources.pyrorql import load_mapping_file
+ appid = pop_arg(args, 1, msg='No instance specified !')
+ mappingfile = pop_arg(args, msg='No mapping file specified !')
+ config = ServerConfiguration.config_for(appid)
+ config.quick_start = True
+ mih = config.migration_handler(connect=False, verbosity=1)
+ repo = mih.repo_connect() # necessary to get cubes
+ checkintegrity(config.load_schema(), load_mapping_file(mappingfile))
+
register_commands( (CreateInstanceDBCommand,
InitInstanceCommand,
GrantUserOnInstanceCommand,
@@ -876,4 +904,5 @@
CheckRepositoryCommand,
RebuildFTICommand,
SynchronizeInstanceSchemaCommand,
+ CheckMappingCommand,
) )