blob: 304572cf74762b2349de10d0b4529afd13ada74b [file] [log] [blame]
Prashanth B0e960282014-05-13 19:38:28 -07001#pylint: disable-msg=C0111
2
3# Copyright (c) 2014 The Chromium OS Authors. All rights reserved.
4# Use of this source code is governed by a BSD-style license that can be
5# found in the LICENSE file.
6
7"""Scheduler helper libraries.
8"""
9import logging
Prashanth B97ee2dd2014-08-14 15:21:42 -070010import os
Prashanth B0e960282014-05-13 19:38:28 -070011
12import common
13
Dan Shif6c65bd2014-08-29 16:15:07 -070014from autotest_lib.client.common_lib import global_config
Prashanth B0e960282014-05-13 19:38:28 -070015from autotest_lib.client.common_lib import logging_config
16from autotest_lib.client.common_lib import logging_manager
Dan Shif6c65bd2014-08-29 16:15:07 -070017from autotest_lib.client.common_lib import utils
Prashanth B0e960282014-05-13 19:38:28 -070018from autotest_lib.database import database_connection
19from autotest_lib.frontend import setup_django_environment
20from autotest_lib.frontend.afe import readonly_connection
Fang Dengf08814a2015-08-03 18:12:18 +000021from autotest_lib.server import utils as server_utils
Prashanth B0e960282014-05-13 19:38:28 -070022
23
24DB_CONFIG_SECTION = 'AUTOTEST_WEB'
25
Prashanth B4ec98672014-05-15 10:44:54 -070026# Translations necessary for scheduler queries to work with SQLite.
27# Though this is only used for testing it is included in this module to avoid
28# circular imports.
29_re_translator = database_connection.TranslatingDatabase.make_regexp_translator
30_DB_TRANSLATORS = (
31 _re_translator(r'NOW\(\)', 'time("now")'),
32 _re_translator(r'LAST_INSERT_ID\(\)', 'LAST_INSERT_ROWID()'),
33 # older SQLite doesn't support group_concat, so just don't bother until
34 # it arises in an important query
35 _re_translator(r'GROUP_CONCAT\((.*?)\)', r'\1'),
36 _re_translator(r'TRUNCATE TABLE', 'DELETE FROM'),
37 _re_translator(r'ISNULL\(([a-z,_]+)\)',
38 r'ifnull(nullif(\1, NULL), \1) DESC'),
39)
40
Prashanth B0e960282014-05-13 19:38:28 -070041
42class SchedulerError(Exception):
43 """Raised by the scheduler when an inconsistent state occurs."""
44
45
Prashanth B0e960282014-05-13 19:38:28 -070046class ConnectionManager(object):
47 """Manager for the django database connections.
48
49 The connection is used through scheduler_models and monitor_db.
50 """
Fang Dengf08814a2015-08-03 18:12:18 +000051 __metaclass__ = server_utils.Singleton
Prashanth B0e960282014-05-13 19:38:28 -070052
Prashanth Bcab7d5b2014-05-28 17:48:23 -070053 def __init__(self, readonly=True, autocommit=True):
Prashanth B0e960282014-05-13 19:38:28 -070054 """Set global django database options for correct connection handling.
55
56 @param readonly: Globally disable readonly connections.
57 @param autocommit: Initialize django autocommit options.
58 """
59 self.db_connection = None
60 # bypass the readonly connection
Jakob Juelich7bef8412014-10-14 19:11:54 -070061 readonly_connection.set_globally_disabled(readonly)
Prashanth B0e960282014-05-13 19:38:28 -070062 if autocommit:
63 # ensure Django connection is in autocommit
64 setup_django_environment.enable_autocommit()
65
66
67 @classmethod
68 def open_connection(cls):
69 """Open a new database connection.
70
71 @return: An instance of the newly opened connection.
72 """
73 db = database_connection.DatabaseConnection(DB_CONFIG_SECTION)
74 db.connect(db_type='django')
75 return db
76
77
78 def get_connection(self):
79 """Get a connection.
80
81 @return: A database connection.
82 """
83 if self.db_connection is None:
84 self.db_connection = self.open_connection()
85 return self.db_connection
86
87
88 def disconnect(self):
89 """Close the database connection."""
90 try:
91 self.db_connection.disconnect()
92 except Exception as e:
93 logging.debug('Could not close the db connection. %s', e)
94
95
96 def __del__(self):
97 self.disconnect()
98
99
100class SchedulerLoggingConfig(logging_config.LoggingConfig):
101 """Configure timestamped logging for a scheduler."""
102 GLOBAL_LEVEL = logging.INFO
103
104 @classmethod
105 def get_log_name(cls, timestamped_logfile_prefix):
106 """Get the name of a logfile.
107
108 @param timestamped_logfile_prefix: The prefix to apply to the
109 a timestamped log. Eg: 'scheduler' will create a logfile named
110 scheduler.log.2014-05-12-17.24.02.
111
112 @return: The timestamped log name.
113 """
114 return cls.get_timestamped_log_name(timestamped_logfile_prefix)
115
116
117 def configure_logging(self, log_dir=None, logfile_name=None,
118 timestamped_logfile_prefix='scheduler'):
119 """Configure logging to a specified logfile.
120
121 @param log_dir: The directory to log into.
122 @param logfile_name: The name of the log file.
123 @timestamped_logfile_prefix: The prefix to apply to the name of
124 the logfile, if a log file name isn't specified.
125 """
126 super(SchedulerLoggingConfig, self).configure_logging(use_console=True)
127
128 if log_dir is None:
129 log_dir = self.get_server_log_dir()
130 if not logfile_name:
131 logfile_name = self.get_log_name(timestamped_logfile_prefix)
132
133 self.add_file_handler(logfile_name, logging.DEBUG, log_dir=log_dir)
Prashanth B97ee2dd2014-08-14 15:21:42 -0700134 symlink_path = os.path.join(
135 log_dir, '%s.latest' % timestamped_logfile_prefix)
136 try:
137 os.unlink(symlink_path)
138 except OSError:
139 pass
140 os.symlink(os.path.join(log_dir, logfile_name), symlink_path)
Prashanth B0e960282014-05-13 19:38:28 -0700141
142
143def setup_logging(log_dir, log_name, timestamped_logfile_prefix='scheduler'):
144 """Setup logging to a given log directory and log file.
145
146 @param log_dir: The directory to log into.
147 @param log_name: Name of the log file.
148 @param timestamped_logfile_prefix: The prefix to apply to the logfile.
149 """
150 logging_manager.configure_logging(
151 SchedulerLoggingConfig(), log_dir=log_dir, logfile_name=log_name,
152 timestamped_logfile_prefix=timestamped_logfile_prefix)
Dan Shif6c65bd2014-08-29 16:15:07 -0700153
154
155def check_production_settings(scheduler_options):
156 """Check the scheduler option's production settings.
157
158 @param scheduler_options: Settings for scheduler.
159
160 @raises SchedulerError: If a loclhost scheduler is started with
161 production settings.
162 """
163 db_server = global_config.global_config.get_config_value('AUTOTEST_WEB',
164 'host')
165 if (not scheduler_options.production and
166 not utils.is_localhost(db_server)):
167 raise SchedulerError('Scheduler is not running in production mode, you '
168 'should not set database to hosts other than '
169 'localhost. It\'s currently set to %s.\nAdd option'
170 ' --production if you want to skip this check.' %
171 db_server)