18a764d1fb6cd99d08cda6400bd35162fa3ac5fe0Jakob Juelich# Copyright (c) 2014 The Chromium OS Authors. All rights reserved.
28a764d1fb6cd99d08cda6400bd35162fa3ac5fe0Jakob Juelich# Use of this source code is governed by a BSD-style license that can be
38a764d1fb6cd99d08cda6400bd35162fa3ac5fe0Jakob Juelich# found in the LICENSE file.
48a764d1fb6cd99d08cda6400bd35162fa3ac5fe0Jakob Juelich
58a764d1fb6cd99d08cda6400bd35162fa3ac5fe0Jakob Juelich"""Django database Router
68a764d1fb6cd99d08cda6400bd35162fa3ac5fe0Jakob Juelich
78a764d1fb6cd99d08cda6400bd35162fa3ac5fe0Jakob JuelichDjango gets configured with three database connections in frontend/settings.py.
88a764d1fb6cd99d08cda6400bd35162fa3ac5fe0Jakob Juelich- The default database
98a764d1fb6cd99d08cda6400bd35162fa3ac5fe0Jakob Juelich    - This database should be used for most things.
108a764d1fb6cd99d08cda6400bd35162fa3ac5fe0Jakob Juelich    - For the master, this is the global database.
118a764d1fb6cd99d08cda6400bd35162fa3ac5fe0Jakob Juelich    - For shards, this this is the shard-local database.
128a764d1fb6cd99d08cda6400bd35162fa3ac5fe0Jakob Juelich- The global database
138a764d1fb6cd99d08cda6400bd35162fa3ac5fe0Jakob Juelich    - For the master, this is the same database as default, which is the global
148a764d1fb6cd99d08cda6400bd35162fa3ac5fe0Jakob Juelich      database.
158a764d1fb6cd99d08cda6400bd35162fa3ac5fe0Jakob Juelich    - For the shards, this is the global database (the same as for the master).
168a764d1fb6cd99d08cda6400bd35162fa3ac5fe0Jakob Juelich- The readonly connection
178a764d1fb6cd99d08cda6400bd35162fa3ac5fe0Jakob Juelich    - This should be the same database as the global database, but it should
188a764d1fb6cd99d08cda6400bd35162fa3ac5fe0Jakob Juelich      use an account on the database that only has readonly permissions.
199a535c9f3144690cf85d88a51e07b98c21454159Dan Shi- The server database
209a535c9f3144690cf85d88a51e07b98c21454159Dan Shi    - This is the database stores information about all servers in the Autotest
219a535c9f3144690cf85d88a51e07b98c21454159Dan Shi      instance. Each instance, master or shard should have its own server
229a535c9f3144690cf85d88a51e07b98c21454159Dan Shi      database is use_server_db is enabled in global config.
238a764d1fb6cd99d08cda6400bd35162fa3ac5fe0Jakob Juelich
248a764d1fb6cd99d08cda6400bd35162fa3ac5fe0Jakob JuelichThe reason shards need two distinct databases for different objects is, that
258a764d1fb6cd99d08cda6400bd35162fa3ac5fe0Jakob Juelichthe tko parser should always write to the global database. Otherwise test
268a764d1fb6cd99d08cda6400bd35162fa3ac5fe0Jakob Juelichresults wouldn't be synced back to the master and would not be accessible in one
278a764d1fb6cd99d08cda6400bd35162fa3ac5fe0Jakob Juelichplace.
288a764d1fb6cd99d08cda6400bd35162fa3ac5fe0Jakob Juelich
299a535c9f3144690cf85d88a51e07b98c21454159Dan ShiTherefore this class will route all queries for tables starts with `server`
309a535c9f3144690cf85d88a51e07b98c21454159Dan Shiprefix to server database, route all queries for tables that involve
319a535c9f3144690cf85d88a51e07b98c21454159Dan Shi`tko_`-prefixed tables to the global database. For all others this router will
329a535c9f3144690cf85d88a51e07b98c21454159Dan Shinot give a hint, which means the default database will be used.
338a764d1fb6cd99d08cda6400bd35162fa3ac5fe0Jakob Juelich"""
348a764d1fb6cd99d08cda6400bd35162fa3ac5fe0Jakob Juelich
358a764d1fb6cd99d08cda6400bd35162fa3ac5fe0Jakob Juelichclass Router(object):
368a764d1fb6cd99d08cda6400bd35162fa3ac5fe0Jakob Juelich    """
378a764d1fb6cd99d08cda6400bd35162fa3ac5fe0Jakob Juelich    Decide if an object should be written to the default or to the global db.
388a764d1fb6cd99d08cda6400bd35162fa3ac5fe0Jakob Juelich
398a764d1fb6cd99d08cda6400bd35162fa3ac5fe0Jakob Juelich    This is an implementaton of Django's multi-database router interface:
408a764d1fb6cd99d08cda6400bd35162fa3ac5fe0Jakob Juelich    https://docs.djangoproject.com/en/1.5/topics/db/multi-db/
418a764d1fb6cd99d08cda6400bd35162fa3ac5fe0Jakob Juelich    """
428a764d1fb6cd99d08cda6400bd35162fa3ac5fe0Jakob Juelich
439a535c9f3144690cf85d88a51e07b98c21454159Dan Shi    def _should_be_in_server_db(self, model):
449a535c9f3144690cf85d88a51e07b98c21454159Dan Shi        """Return True if the model should be stored in the server db.
459a535c9f3144690cf85d88a51e07b98c21454159Dan Shi
469a535c9f3144690cf85d88a51e07b98c21454159Dan Shi        @param model: Model to decide for.
479a535c9f3144690cf85d88a51e07b98c21454159Dan Shi
489a535c9f3144690cf85d88a51e07b98c21454159Dan Shi        @return: True if querying the model requires server database.
499a535c9f3144690cf85d88a51e07b98c21454159Dan Shi        """
509a535c9f3144690cf85d88a51e07b98c21454159Dan Shi        return model._meta.db_table.startswith('server')
519a535c9f3144690cf85d88a51e07b98c21454159Dan Shi
529a535c9f3144690cf85d88a51e07b98c21454159Dan Shi
538a764d1fb6cd99d08cda6400bd35162fa3ac5fe0Jakob Juelich    def _should_be_in_global(self, model):
548a764d1fb6cd99d08cda6400bd35162fa3ac5fe0Jakob Juelich        """Returns True if the model should be stored in the global db.
558a764d1fb6cd99d08cda6400bd35162fa3ac5fe0Jakob Juelich
568a764d1fb6cd99d08cda6400bd35162fa3ac5fe0Jakob Juelich        @param model: Model to decide for.
578a764d1fb6cd99d08cda6400bd35162fa3ac5fe0Jakob Juelich
588a764d1fb6cd99d08cda6400bd35162fa3ac5fe0Jakob Juelich        @return: True if querying the model requires global database.
598a764d1fb6cd99d08cda6400bd35162fa3ac5fe0Jakob Juelich        """
608a764d1fb6cd99d08cda6400bd35162fa3ac5fe0Jakob Juelich        return model._meta.db_table.startswith('tko_')
618a764d1fb6cd99d08cda6400bd35162fa3ac5fe0Jakob Juelich
628a764d1fb6cd99d08cda6400bd35162fa3ac5fe0Jakob Juelich
638a764d1fb6cd99d08cda6400bd35162fa3ac5fe0Jakob Juelich    def db_for_read(self, model, **hints):
649a535c9f3144690cf85d88a51e07b98c21454159Dan Shi        """Return the database for a reading access.
658a764d1fb6cd99d08cda6400bd35162fa3ac5fe0Jakob Juelich
668a764d1fb6cd99d08cda6400bd35162fa3ac5fe0Jakob Juelich        @param model: Model to decide for.
678a764d1fb6cd99d08cda6400bd35162fa3ac5fe0Jakob Juelich        @param hints: Optional arguments to determine which database for read.
688a764d1fb6cd99d08cda6400bd35162fa3ac5fe0Jakob Juelich
699a535c9f3144690cf85d88a51e07b98c21454159Dan Shi        @returns: 'server' for all server models. 'global' for all tko models,
709a535c9f3144690cf85d88a51e07b98c21454159Dan Shi                  None otherwise. None means the router doesn't have an opinion.
718a764d1fb6cd99d08cda6400bd35162fa3ac5fe0Jakob Juelich        """
729a535c9f3144690cf85d88a51e07b98c21454159Dan Shi        if self._should_be_in_server_db(model):
739a535c9f3144690cf85d88a51e07b98c21454159Dan Shi            return 'server'
748a764d1fb6cd99d08cda6400bd35162fa3ac5fe0Jakob Juelich        if self._should_be_in_global(model):
758a764d1fb6cd99d08cda6400bd35162fa3ac5fe0Jakob Juelich            return 'global'
768a764d1fb6cd99d08cda6400bd35162fa3ac5fe0Jakob Juelich        return None
778a764d1fb6cd99d08cda6400bd35162fa3ac5fe0Jakob Juelich
788a764d1fb6cd99d08cda6400bd35162fa3ac5fe0Jakob Juelich
798a764d1fb6cd99d08cda6400bd35162fa3ac5fe0Jakob Juelich    def db_for_write(self, model, **hints):
809a535c9f3144690cf85d88a51e07b98c21454159Dan Shi        """Return the database for a writing access.
818a764d1fb6cd99d08cda6400bd35162fa3ac5fe0Jakob Juelich
828a764d1fb6cd99d08cda6400bd35162fa3ac5fe0Jakob Juelich        @param model: Model to decide for.
838a764d1fb6cd99d08cda6400bd35162fa3ac5fe0Jakob Juelich        @param hints: Optional arguments to determine which database for write.
848a764d1fb6cd99d08cda6400bd35162fa3ac5fe0Jakob Juelich
859a535c9f3144690cf85d88a51e07b98c21454159Dan Shi        @returns: 'server' for all server models. 'global' for all tko models,
869a535c9f3144690cf85d88a51e07b98c21454159Dan Shi                  None otherwise. None means the router doesn't have an opinion.
878a764d1fb6cd99d08cda6400bd35162fa3ac5fe0Jakob Juelich        """
889a535c9f3144690cf85d88a51e07b98c21454159Dan Shi        if self._should_be_in_server_db(model):
899a535c9f3144690cf85d88a51e07b98c21454159Dan Shi            return 'server'
908a764d1fb6cd99d08cda6400bd35162fa3ac5fe0Jakob Juelich        if self._should_be_in_global(model):
918a764d1fb6cd99d08cda6400bd35162fa3ac5fe0Jakob Juelich            return 'global'
928a764d1fb6cd99d08cda6400bd35162fa3ac5fe0Jakob Juelich        return None
938a764d1fb6cd99d08cda6400bd35162fa3ac5fe0Jakob Juelich
948a764d1fb6cd99d08cda6400bd35162fa3ac5fe0Jakob Juelich
958a764d1fb6cd99d08cda6400bd35162fa3ac5fe0Jakob Juelich    def allow_relation(self, obj1, obj2, **hints):
968a764d1fb6cd99d08cda6400bd35162fa3ac5fe0Jakob Juelich        """
978a764d1fb6cd99d08cda6400bd35162fa3ac5fe0Jakob Juelich        Allow relations only if either both are in tko_ tables or none is.
988a764d1fb6cd99d08cda6400bd35162fa3ac5fe0Jakob Juelich
998a764d1fb6cd99d08cda6400bd35162fa3ac5fe0Jakob Juelich        @param obj1: First object involved in the relation.
1008a764d1fb6cd99d08cda6400bd35162fa3ac5fe0Jakob Juelich        @param obj2: Second object involved in the relation.
1018a764d1fb6cd99d08cda6400bd35162fa3ac5fe0Jakob Juelich        @param hints: Optional arguments to determine if relation is allowed.
1028a764d1fb6cd99d08cda6400bd35162fa3ac5fe0Jakob Juelich
1038a764d1fb6cd99d08cda6400bd35162fa3ac5fe0Jakob Juelich        @returns False, if the relation should be prohibited,
1048a764d1fb6cd99d08cda6400bd35162fa3ac5fe0Jakob Juelich                 None, if the router doesn't have an opinion.
1058a764d1fb6cd99d08cda6400bd35162fa3ac5fe0Jakob Juelich        """
1069a535c9f3144690cf85d88a51e07b98c21454159Dan Shi        if (not self._should_be_in_server_db(type(obj1)) ==
1079a535c9f3144690cf85d88a51e07b98c21454159Dan Shi            self._should_be_in_server_db(type(obj2))):
1089a535c9f3144690cf85d88a51e07b98c21454159Dan Shi            return False
1098a764d1fb6cd99d08cda6400bd35162fa3ac5fe0Jakob Juelich        if (not self._should_be_in_global(type(obj1)) ==
1108a764d1fb6cd99d08cda6400bd35162fa3ac5fe0Jakob Juelich            self._should_be_in_global(type(obj2))):
1118a764d1fb6cd99d08cda6400bd35162fa3ac5fe0Jakob Juelich            return False
1128a764d1fb6cd99d08cda6400bd35162fa3ac5fe0Jakob Juelich        return None
113