|
Package qb ::
Package utils
|
|
1 '''
2 A package for convenience functions and classes
3 '''
4
5
6
7
8
9
10 import logging
11 import sys
12 import os, os.path
13 import re
14 import time
15 import inspect
16 import traceback
17 import shutil
18 import platform
19 import tempfile
20
21 from qb import convertpath, workerpathmap
22
23
24
25
26 import files
27 import flags
28 import logging
29 import qbTokens
30 import regex
31 from exceptions import *
32 from enums import ENUMS
33
34
35
36
37
39 '''
40 Add a path to the python module search path.
41
42 @param path: The file path to add.
43
44 @type path: C{str}
45
46 @param append: Either insert the path at the head of sys.path, or append
47 to the end.
48
49 @type append: C{bool} default: False
50 '''
51 if path:
52 fullPath = os.path.abspath(path)
53 if fullPath not in sys.path:
54 if append:
55 idx = len(sys.path) + 1
56 else:
57 idx = 0
58 sys.path.insert(idx, fullPath)
59
60
62 '''
63 Get the path to the module that called this instance
64
65 @return: The file path to the backend's class module
66
67 @rtype: C{str}
68 '''
69 modulePath = ''
70
71 frames = inspect.getouterframes(inspect.currentframe())
72 callingModulePath = frames[1][1]
73 modulePath = os.path.abspath(callingModulePath)
74
75 for frame in frames:
76 del frame
77
78 return modulePath
79
80
82 return float('%s.%s' % (sys.version_info[0], sys.version_info[1]))
83
84
93
94
96 """
97 @param cmd: a python command to perform path translation on, according to the worker's
98 worker_path_map. The cmd is scanned for any string wrapped in C{QB_CONVERT_PATH()}
99 @type cmd: C{str}
100
101 @return: the cmd with any paths translated as necessary
102 @rtype: C{str}
103 """
104 preTranslationCmd = cmd
105 while cmd.count('QB_CONVERT_PATH'):
106
107 unconvertedPath = regex.RGX_CONVERT_PATH_TOKEN.search(cmd).group(1).strip()
108
109
110 convertedPath = convertpath(unconvertedPath)
111
112
113 if convertedPath.endswith('\\'):
114 convertedPath += '\\'
115
116
117 if convertedPath.count(' ') and not re.search('^".*"$', convertedPath):
118 convertedPath = '"%s"' % convertedPath
119
120
121
122 cmd = regex.RGX_CONVERT_PATH_TOKEN.sub(convertedPath.encode('string_escape'), cmd, 1)
123
124 if cmd != preTranslationCmd:
125 logging.info('Paths in the command have been translated as per this worker\'s worker_path_map')
126 logging.info('%s %s' % (' '*4, preTranslationCmd))
127 logging.info('%s -> %s' % (' '*1, cmd))
128
129 return cmd
130
131
132
133
134
136 '''
137 Attempt to perform an authenticated copy. An existing destination file will be overwritten.
138
139 Prompt for authentication on OS X, otherwise just return an error message on other OS's upon failure.
140
141 @param src: copy source file
142 @type src: C{str}
143
144 @param dst: copy destination file
145 @type src: C{str}
146
147 @return: Return an error message; a null-string indicates success
148 @rtype: C{str}
149 '''
150 errMsg = ''
151
152
153
154 try:
155 dstStat = os.stat(dst)
156 origMode = dstStat.st_mode
157 origTimeStamp = dstStat.st_mtime
158 except OSError:
159
160 origMode = None
161 origTimeStamp = 0
162
163 if not os.path.exists(src):
164
165 errMsg = 'Source file %s does not exist.' % src
166 logging.error(errMsg)
167
168 if os.path.exists(dst):
169 logging.log(logging.WARNING+5, 'Destination file exists, will be over-written: %s' % dst)
170 else:
171 logging.log(logging.INFO+5, 'Destination file does not exist, will be created: %s' % dst)
172
173 if platform.system() == 'Darwin':
174
175 result = os.system('cat %s | /usr/libexec/authopen -w -c %s' % (src, dst))
176 if result != 0:
177 errMsg = 'Unable to write to "%s". Permission denied.' % dst
178 logging.error(errMsg)
179 else:
180 if os.path.isfile(dst) and not os.access(dst, os.W_OK):
181
182 try:
183 origMode = os.stat(dst).st_mode
184 os.chmod(dst, origMode|stat.S_IWRITE|stat.S_IWGRP|stat.S_IWOTH)
185 except OSError:
186 errMsg = 'Unable to make existing destination file "%s" overwritable.' % dst
187 logging.error(errMsg)
188
189 if not errMsg:
190 try:
191
192
193
194 shutil.copy(src, dst)
195 logging.info('Copied %s --> %s' % (src, dst))
196 except IOError:
197 errMsg = 'Unable to copy %s --> %s. Permission denied.' % (src, dst)
198 logging.error(errMsg)
199
200 if not errMsg:
201 try:
202 os.chmod(dst, origMode)
203 pass
204 except:
205 logging.warning('Failed to re-set file mode for %s' % dst)
206 pass
207
208 if origTimeStamp >= os.stat(dst).st_mtime:
209 errMsg = 'File copy failed somehow, destination timestamp is not later than source file timestamp'
210 logging.error(errMsg)
211
212 return errMsg
213
214
216 '''
217 Attempt to perform a file write that is expected to require authentication.
218
219 @param data: data to write to the destination file
220 @type data: C{str}
221
222 @param dst: destination file
223 @type dst: C{str}
224
225 @param concat: if true, concat the data to the destination, otherwise overwrite destination
226 @type concat: C{boolean}
227
228 @return: Return an error message; a null-string indicates success
229 @rtype: C{str}
230 '''
231 errMsg = ''
232
233 if not data:
234 errMsg = 'No data supplied to write to file %s.' % dst
235
236 if not errMsg:
237 if not os.path.isfile(dst) and platform.system() != 'Darwin':
238
239
240
241 logging.log(logging.WARNING+5, 'Destination file %s does not exist, will be created.' % dst)
242 try:
243 fh = open(dst, 'w')
244 fh.write('')
245 fh.close()
246 except IOError, e:
247 errMsg = 'Unable to create file: %s' % dst
248 logging.error(errMsg)
249
250 elif os.path.isfile(dst):
251
252
253
254
255 logging.log(logging.WARNING+5, 'Destination file %s will be overwritten, creating a backup copy.' % dst)
256
257 backupFname = getTimestampedFileName(dst)
258 errMsg = sudoCopy(dst, backupFname)
259 if not errMsg:
260 logging.info('Backup file created: %s' % backupFname)
261 else:
262 logging.error(errMsg)
263 logging.warning('Unable to create a backup file: %s' % backupFname)
264
265 if not errMsg:
266
267
268
269 if platform.system() == 'Darwin':
270
271 (fd, tmpFile) = tempfile.mkstemp()
272 fh = open(tmpFile, 'w')
273 fh.writelines(data)
274 fh.close()
275
276 if concat and os.path.isfile(dst):
277 result = os.system('cat %(dst)s %(src)s | /usr/libexec/authopen -w -c %(dst)s' % {'src':tmpFile, 'dst':dst})
278 else:
279 result = os.system('cat %s | /usr/libexec/authopen -w -c %s' % (tmpFile, dst))
280
281 os.unlink(tmpFile)
282
283 if result != 0:
284 errMsg = 'Unable to write to destination file: %s' % dst
285 logging.error(errMsg)
286
287 else:
288 if concat:
289 fOpenMode = 'w+'
290 else:
291 fOpenMode = 'w'
292
293 try:
294 fh = open(dst, fOpenMode)
295 fh.writelines(data)
296 fh.close()
297 except IOError, e:
298 errMsg = e
299 logging.error(errMsg)
300
301
302 return errMsg
303
304
306 '''
307 Generate a time-stamped backup file name:
308 qb.conf -> qb.20121231_235959.conf
309
310 @param fName: A file name
311 @type fName: C{str}
312
313 @return: A filename with a timestamp embedded in front of the file extension
314 @rtype: C{str}
315 '''
316 timeFmt = '%Y_%m%d_%H%M%S'
317 timestamp = time.strftime(timeFmt, time.localtime())
318
319 (fRoot, fExt) = os.path.splitext(fName)
320 newName = '%s.%s' % (fRoot, timestamp)
321 if fExt:
322 newName += '%s' % fExt
323
324 return newName
325