47
47
pg_config_data = {}
48
48
49
49
50
+ """
51
+ Predefined exceptions
52
+ """
50
53
class ClusterException (Exception ):pass
51
54
class QueryException (Exception ):pass
52
55
53
56
57
+ """
58
+ Transaction wrapper returned by Node
59
+ """
60
+ class NodeConnection (object ):
61
+ def __init__ (self ,parent_node ,dbname ):
62
+ self .parent_node = parent_node
63
+
64
+ self .connection = pglib .connect (
65
+ database = dbname ,
66
+ user = get_username (),
67
+ port = parent_node .port ,
68
+ host = "127.0.0.1"
69
+ )
70
+
71
+ self .cursor = self .connection .cursor ()
72
+
73
+ def __enter__ (self ):
74
+ return self
75
+
76
+ def __exit__ (self ,type ,value ,tb ):
77
+ self .connection .close ()
78
+
79
+ def execute (self ,query ):
80
+ self .cursor .execute (query )
81
+ return self .cursor .fetchall ()
82
+
83
+ def close (self ):
84
+ self .connection .close ()
85
+
86
+
54
87
class PostgresNode :
55
88
def __init__ (self ,name ,port ):
56
89
self .name = name
@@ -134,6 +167,8 @@ def init(self, allows_streaming=False):
134
167
135
168
self .set_replication_conf ()
136
169
170
+ return self
171
+
137
172
def init_from_backup (self ,root_node ,backup_name ,has_streaming = False ,hba_permit_replication = True ):
138
173
"""Initializes cluster from backup, made by another node"""
139
174
@@ -175,7 +210,9 @@ def append_conf(self, filename, string):
175
210
"""
176
211
config_name = "%s/%s" % (self .data_dir ,filename )
177
212
with open (config_name ,"a" )as conf :
178
- conf .write (string )
213
+ conf .write ('' .join ([string ,'\n ' ]))
214
+
215
+ return self
179
216
180
217
def pg_ctl (self ,command ,params ):
181
218
"""Runs pg_ctl with specified params
@@ -192,12 +229,17 @@ def pg_ctl(self, command, params):
192
229
193
230
with open (self .output_filename ,"a" )as file_out , \
194
231
open (self .error_filename ,"a" )as file_err :
195
- return subprocess .call (
232
+
233
+ res = subprocess .call (
196
234
arguments + [command ],
197
235
stdout = file_out ,
198
236
stderr = file_err
199
237
)
200
238
239
+ if res > 0 :
240
+ with open (self .error_filename ,"r" )as errfile :
241
+ raise ClusterException (errfile .readlines ()[- 1 ])
242
+
201
243
def start (self ):
202
244
""" Starts cluster """
203
245
logfile = self .logs_dir + "/postgresql.log"
@@ -206,27 +248,30 @@ def start(self):
206
248
"-w" :None ,
207
249
"-l" :logfile ,
208
250
}
209
- if self .pg_ctl ("start" ,params ):
210
- raise ClusterException ("Cluster startup failed" )
251
+ self .pg_ctl ("start" ,params )
211
252
212
253
self .working = True
213
254
255
+ return self
256
+
214
257
def stop (self ):
215
258
""" Stops cluster """
216
259
params = {
217
260
"-D" :self .data_dir ,
218
261
"-w" :None
219
262
}
220
- if self .pg_ctl ("stop" ,params ):
221
- raise ClusterException ("Cluster stop failed" )
263
+ self .pg_ctl ("stop" ,params )
222
264
223
265
self .working = False
224
266
267
+ return self
268
+
225
269
def reload (self ):
226
270
"""Reloads config files"""
227
271
params = {"-D" :self .data_dir }
228
- if self .pg_ctl ("reload" ,params ):
229
- raise ClusterException ("Cluster reload failed" )
272
+ self .pg_ctl ("reload" ,params )
273
+
274
+ return self
230
275
231
276
def cleanup (self ):
232
277
"""Stops cluster if needed and removes the data directory"""
@@ -238,6 +283,8 @@ def cleanup(self):
238
283
# remove data directory
239
284
shutil .rmtree (self .data_dir )
240
285
286
+ return self
287
+
241
288
def psql (self ,dbname ,query ):
242
289
"""Executes a query by the psql
243
290
@@ -291,21 +338,8 @@ def poll_query_until(self, dbname, query):
291
338
292
339
def execute (self ,dbname ,query ):
293
340
"""Executes the query and returns all rows"""
294
- connection = pglib .connect (
295
- database = dbname ,
296
- user = get_username (),
297
- port = self .port ,
298
- host = "127.0.0.1"
299
- )
300
- cur = connection .cursor ()
301
-
302
- cur .execute (query )
303
- res = cur .fetchall ()
304
-
305
- cur .close ()
306
- connection .close ()
307
-
308
- return res
341
+ with self .connect (dbname )as node_con :
342
+ return node_con .execute (query )
309
343
310
344
def backup (self ,name ):
311
345
"""Performs pg_basebackup"""
@@ -325,6 +359,9 @@ def backup(self, name):
325
359
326
360
return backup_path
327
361
362
+ def connect (self ,dbname ):
363
+ return NodeConnection (parent_node = self ,dbname = dbname )
364
+
328
365
329
366
def get_username ():
330
367
""" Returns current user name """