aboutsummaryrefslogtreecommitdiff
path: root/lib/dodai/config/databases/__init__.py
diff options
context:
space:
mode:
Diffstat (limited to 'lib/dodai/config/databases/__init__.py')
-rw-r--r--lib/dodai/config/databases/__init__.py285
1 files changed, 285 insertions, 0 deletions
diff --git a/lib/dodai/config/databases/__init__.py b/lib/dodai/config/databases/__init__.py
new file mode 100644
index 0000000..c91e77f
--- /dev/null
+++ b/lib/dodai/config/databases/__init__.py
@@ -0,0 +1,285 @@
1# Copyright (C) 2010 Leonard Thomas
2#
3# This file is part of Dodai.
4#
5# Dodai is free software: you can redistribute it and/or modify
6# it under the terms of the GNU General Public License as published by
7# the Free Software Foundation, either version 3 of the License, or
8# (at your option) any later version.
9#
10# Dodai is distributed in the hope that it will be useful,
11# but WITHOUT ANY WARRANTY; without even the implied warranty of
12# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13# GNU General Public License for more details.
14#
15# You should have received a copy of the GNU General Public License
16# along with Dodai. If not, see <http://www.gnu.org/licenses/>.
17
18
19from dodai.exception import DatabaseEmptyOptionException
20from dodai.exception import DatabasePortException
21from dodai.exception import DatabaseHostnameException
22from dodai.exception import DatabaseProtocolException
23from dodai.exception import DatabaseConnectionException
24from dodai.exception import UnknownDatabaseConnectionException
25from dodai.tools import list_to_english
26
27class ConfigDatabases(object):
28 """An object that is used for creating database connection objects
29
30 """
31 def __init__(self, default_handler, default_name):
32 self.connections = {}
33 self._handlers = {}
34 DatabaseConnection.DEFAULT_HANDLER = default_name
35 self.add_handler(default_name, default_handler)
36
37 def add(self, sections):
38 """Adds a dictionary of sections to this object that could
39 be used for creating database connection objects. The dictionary
40 should be formatted as follows:
41
42 sections[name] = {option: value, option: value, ....}
43
44 The 'name' is the section name. It is an identifier that is used
45 to load the database:
46
47 The options and values can have the following:
48
49 protocol: The database engine (postgresql, mysql, sqlite,
50 oracle, mssql etc..)
51
52 hostname: The hostname or ip address of the database server
53
54 port: The port that should be used to connect to the
55 database server should be a number between 1
56 and 65535
57
58 username: The username used to connect to the database server
59
60 password: The password used to connect to the database server
61
62 database: The database to connect to once connected to the
63 server
64
65 schema: The schema that will be used. This option does
66 not do anything but will exist in the db object
67 for use
68
69 filename: The file path to the database file (sqlite)
70
71 protocol_extra: Used to tell the handler which python db engine
72 to use. For example one might want to tell
73 the sqlalchemy handler to use the psycopg2
74 python object.
75
76 handler: The handler used to build the orm object. Dodai
77 only works with sqlalchemy so the default 'sa'
78 is used.
79
80 """
81 for name, section in sections.items():
82 self.connections[name] = DatabaseConnection(section, name,
83 self._handlers)
84
85 def load(self, section_name):
86 """Returns a database connection object of the given section_name.
87 Throws Exceptions for any type of configuration errors or missing
88 configuration data
89
90 """
91 if section_name in self.connections:
92 return self.connections[section_name].load()
93 else:
94 raise UnknownDatabaseConnectionException(section_name)
95
96 def add_handler(self, name, handler):
97 """Adds the given handler and name to this objects handlers
98
99 """
100 self._handlers[name] = handler
101
102class DatabaseConnection(object):
103
104 DEFAULT_HANDLER = None
105
106 def __init__(self, section, name, handlers):
107 self.section = section
108 self.name = name
109 self.handlers = handlers
110 self.validate = DatabaseConnectionValidator.load(section, name)
111 self.obj = None
112
113 def load(self):
114 if not self.obj:
115 self.validate()
116 handler = self._get_handler()
117 self.obj = handler(self.section, self.name)
118 return self.obj
119
120 def _get_handler(self):
121 if self.section.has_key('handler'):
122 name = self.section['handler']
123 if name in self.handlers:
124 return self.handlers[name]
125 return self.handlers[self.DEFAULT_HANDLER]
126
127
128class DatabaseConnectionValidator(object):
129
130 def __init__(self, section, name, validators=[]):
131 self.section = section
132 self.name = name
133 self.validators = validators
134
135 def __call__(self):
136 return self.validate()
137
138 def validate(self):
139 """Validates the connection. Returns true if valid. Throws
140 DodaiDatabaseConnectionConfigurationError on any errors
141
142 """
143 raise NotImplementedError()
144
145 def _validate_option(self, name):
146 try:
147 value = self.section[name]
148 except KeyError:
149 value = None
150
151 if not value:
152 raise DatabaseEmptyOptionException(self.name, name)
153 return True
154
155 def _validate_protocol(self):
156
157 self._validate_option('protocol')
158
159 if self.section['protocol'] not in self.PROTOCOLS:
160 raise DatabaseProtocolException(self.name,
161 self.section['protocol'], self.DB_TYPE,
162 self.PROTOCOLS)
163 return True
164
165 @staticmethod
166 def load(section, name):
167 """Static method (factory) that loads the correct database
168 validation class.
169
170 Attributes:
171 section: Dictionary of key val connection information
172 name: String name of the section
173
174 """
175 action = None
176 validators = [DatabaseConnectionValidatorServer,
177 DatabaseConnectionValidatorFile]
178
179 for validator in validators:
180 obj = validator.load(section, name)
181 if obj:
182 return obj
183
184 return DatabaseConnectionValidatorUnknown(section, name, validators)
185
186class DatabaseConnectionValidatorServer(DatabaseConnectionValidator):
187
188 DB_TYPE = 'server'
189 REQUIRED = ['protocol', 'hostname', 'port', 'username', 'password',
190 'database']
191 PROTOCOLS = ['postgresql', 'mysql', 'mssql', 'oracle']
192
193 def _validate_port(self):
194 self._validate_option('port')
195
196 try:
197 port = int(self.section['port'])
198 except ValueError:
199 port = self.section['port']
200 else:
201 if port >=1 and port <=65535:
202 return True
203 raise DatabasePortException(self.name, port)
204
205 def validate(self):
206 """Validates the connection. Returns true if valid. Throws
207 DodaiDatabaseConnectionConfigurationError on any errors
208
209 """
210 self._validate_protocol()
211 self._validate_port()
212 self._validate_option('hostname')
213 self._validate_option('username')
214 self._validate_option('password')
215 self._validate_option('database')
216 return True
217
218 @classmethod
219 def load(cls, section, name):
220 """Return this validation class if it is possible that the
221 given connection information contains enough data to make
222 a database server connection.
223
224 Attributes:
225 section: Dictionary of key val connection information
226 name: String name of the section
227
228 """
229 if section.has_key('protocol'):
230 if section['protocol'] in cls.PROTOCOLS:
231 return cls(section, name)
232 keys = section.keys()
233 for key in keys:
234 if key in ['hostname', 'port']:
235 return cls(section, name)
236
237
238class DatabaseConnectionValidatorFile(DatabaseConnectionValidator):
239
240 DB_TYPE = 'file'
241 REQUIRED = ['protocol', 'filename']
242 PROTOCOLS = ['sqlite']
243
244 def validate(self):
245 """Validates the connection. Returns true if valid. Throws
246 DodaiDatabaseConnectionConfigurationError on any errors
247
248 """
249 self._validate_protocol()
250 self._validate_option('filename')
251 return True
252
253 @classmethod
254 def load(cls, section, name):
255 """Return this validation class if it is possible that the
256 given connection information contains enough data to make
257 a database file connection.
258
259 Attributes:
260 section: Dictionary of key val connection information
261 name: String name of the section
262
263 """
264 if section.has_key('protocol'):
265 if section['protocol'] in cls.PROTOCOLS:
266 return cls(section, name)
267 keys = section.keys()
268 for key in keys:
269 if key in ['filename']:
270 return cls(section, name)
271
272
273class DatabaseConnectionValidatorUnknown(DatabaseConnectionValidator):
274
275 DB_TYPE = 'unkonwn'
276 REQUIRED = []
277 PROTOCOLS = []
278
279 def validate(self):
280 """Validates the connection. Returns true if valid. Throws
281 DodaiDatabaseConnectionConfigurationError on any errors
282
283 """
284
285 raise DatabaseConnectionException(self.name, self.validators)