[autotest] suite-scheduler sanity check should not rely on django

Resubmit of CL:288396
This reverts commit babd70ccd5866fe5eb875e9c725a129d3c881c2c.
It also contains a fix for importing django.

TEST=Test on a ganeti
/usr/local/autotest/site_utils/suite_scheduler/suite_scheduler.py -b -d
/usr/local/autotest/logs -f /usr/local/autotest/suite_scheduler.ini
TEST=Test on a geneti
/usr/local/autotest/site_utils/suite_scheduler/suite_scheduler.py --sanity
(remove site-packages)
BUG=chromium:474671

Change-Id: I155750de1d05b6e3f2625a4ae210622f748975f7
Reviewed-on: https://chromium-review.googlesource.com/290251
Tested-by: Fang Deng <fdeng@chromium.org>
Reviewed-by: Dan Shi <dshi@chromium.org>
Commit-Queue: Fang Deng <fdeng@chromium.org>
diff --git a/scheduler/scheduler_lib.py b/scheduler/scheduler_lib.py
index cdd6c18..304572c 100644
--- a/scheduler/scheduler_lib.py
+++ b/scheduler/scheduler_lib.py
@@ -18,6 +18,7 @@
 from autotest_lib.database import database_connection
 from autotest_lib.frontend import setup_django_environment
 from autotest_lib.frontend.afe import readonly_connection
+from autotest_lib.server import utils as server_utils
 
 
 DB_CONFIG_SECTION = 'AUTOTEST_WEB'
@@ -42,24 +43,12 @@
     """Raised by the scheduler when an inconsistent state occurs."""
 
 
-class Singleton(type):
-    """Enforce that only one client class is instantiated per process."""
-    _instances = {}
-
-    def __call__(cls, *args, **kwargs):
-        """Fetch the instance of a class to use for subsequent calls."""
-        if cls not in cls._instances:
-            cls._instances[cls] = super(Singleton, cls).__call__(
-                    *args, **kwargs)
-        return cls._instances[cls]
-
-
 class ConnectionManager(object):
     """Manager for the django database connections.
 
     The connection is used through scheduler_models and monitor_db.
     """
-    __metaclass__ = Singleton
+    __metaclass__ = server_utils.Singleton
 
     def __init__(self, readonly=True, autocommit=True):
         """Set global django database options for correct connection handling.
diff --git a/scheduler/scheduler_lib_unittest.py b/scheduler/scheduler_lib_unittest.py
index 0ef7f28..b7bda22 100755
--- a/scheduler/scheduler_lib_unittest.py
+++ b/scheduler/scheduler_lib_unittest.py
@@ -12,6 +12,7 @@
 from autotest_lib.database import database_connection
 from autotest_lib.frontend import setup_django_environment
 from autotest_lib.frontend.afe import readonly_connection
+from autotest_lib.server import utils as server_utils
 from autotest_lib.scheduler import scheduler_lib
 from django.db import utils as django_utils
 
@@ -23,7 +24,7 @@
         self.connection_manager = None
         readonly_connection.set_globally_disabled = mock.MagicMock()
         setup_django_environment.enable_autocommit = mock.MagicMock()
-        scheduler_lib.Singleton._instances = {}
+        server_utils.Singleton._instances = {}
 
 
     def tearDown(self):
diff --git a/server/site_utils.py b/server/site_utils.py
index 0f6e303..a0df249 100644
--- a/server/site_utils.py
+++ b/server/site_utils.py
@@ -49,6 +49,18 @@
     pass
 
 
+class Singleton(type):
+    """Enforce that only one client class is instantiated per process."""
+    _instances = {}
+
+    def __call__(cls, *args, **kwargs):
+        """Fetch the instance of a class to use for subsequent calls."""
+        if cls not in cls._instances:
+            cls._instances[cls] = super(Singleton, cls).__call__(
+                    *args, **kwargs)
+        return cls._instances[cls]
+
+
 def ParseBuildName(name):
     """Format a build name, given board, type, milestone, and manifest num.
 
diff --git a/site_utils/suite_scheduler/suite_scheduler.py b/site_utils/suite_scheduler/suite_scheduler.py
index d4b07d8..c43afdc 100755
--- a/site_utils/suite_scheduler/suite_scheduler.py
+++ b/site_utils/suite_scheduler/suite_scheduler.py
@@ -42,7 +42,15 @@
 from autotest_lib.client.common_lib import global_config
 from autotest_lib.client.common_lib import logging_config, logging_manager
 from autotest_lib.server.cros.dynamic_suite import frontend_wrappers
-from autotest_lib.site_utils import server_manager_utils
+try:
+    from autotest_lib.frontend import setup_django_environment
+    # server_manager_utils depend on django which
+    # may not be available when people run checks with --sanity
+    from autotest_lib.site_utils import server_manager_utils
+except ImportError:
+    server_manager_utils = None
+    logging.debug('Could not load server_manager_utils module, expected '
+                  'if you are running sanity check or pre-submit hook')
 
 
 CONFIG_SECTION = 'SCHEDULER'
@@ -227,6 +235,9 @@
     # If server database is enabled, check if the server has role
     # `suite_scheduler`. If the server does not have suite_scheduler role,
     # exception will be raised and suite scheduler will not continue to run.
+    if not server_manager_utils:
+        raise ImportError(
+            'Could not import autotest_lib.site_utils.server_manager_utils')
     if server_manager_utils.use_server_db():
         server_manager_utils.confirm_server_has_role(hostname='localhost',
                                                      role='suite_scheduler')
diff --git a/site_utils/suite_scheduler/task.py b/site_utils/suite_scheduler/task.py
index 109bf18..0ef61d8 100644
--- a/site_utils/suite_scheduler/task.py
+++ b/site_utils/suite_scheduler/task.py
@@ -16,8 +16,8 @@
 from constants import Builds
 
 import common
+from autotest_lib.server import utils as server_utils
 from autotest_lib.server.cros.dynamic_suite import constants
-from autotest_lib.scheduler import scheduler_lib
 
 
 class MalformedConfigEntry(Exception):
@@ -48,7 +48,7 @@
     scheduler's ini file.
     """
 
-    __metaclass__ = scheduler_lib.Singleton
+    __metaclass__ = server_utils.Singleton
 
     # True if suite_scheduler is running for sanity check. When it's set to
     # True, the code won't make gsutil call to get the actual tot milestone to