1 arta 1.1 #!/home/jsoc/bin/linux_x86_64/activepython
2
3 import os.path
4 import sys
5 import getopt
6 import re
7 from subprocess import check_output, CalledProcessError
8
9 # Constants
10 VERS_FILE = 'jsoc_version.h'
11 SDP_CFG = 'configsdp.txt'
12 NET_CFG = 'config.local'
13 NET_CFGMAP = 'config.local.map'
14 RET_SUCCESS = 0
15 RET_NOTDRMS = 1
16
17 PERL_PREFIX = """
18 #!/usr/bin/perl
19
20 package drmsparams;
21 """
22 arta 1.1
23 PERL_FXNS = """
24 sub new
25 {
26 my($clname) = shift;
27
28 my($self) =
29 {
30 _paramsH => undef
31 };
32
33 bless($self, $clname);
34 $self->{_paramsH} = $self->initialize();
35
36 return $self;
37 }
38
39 sub DESTROY
40 {
41 my($self) = shift;
42 }
43 arta 1.1
44 """
45
46 # Read arguments
47 # d - localization directory
48 # b - base name of all parameter files (e.g., -b drmsparams --> drmsparams.h, drmsparams.mk, drmsparams.pm, etc.)
49 # m - make file
50 def GetArgs(args):
51 rv = bool(0)
52 optD = {}
53
54 try:
55 opts, remainder = getopt.getopt(args, "hd:b:",["dir=", "base="])
56 except getopt.GetoptError:
57 print('Usage:')
58 print('localize.py [-h] -d <localization directory> -b <parameter file base>')
59 rv = bool(1)
60
61 if rv == bool(0):
62 for opt, arg in opts:
63 if opt == '-h':
64 arta 1.1 print('localize.py [-h] -d <localization directory> -b <parameter file base>')
65 elif opt in ("-d", "--dir"):
66 regexp = re.compile(r"(\S+)/?")
67 matchobj = regexp.match(arg)
68 if matchobj is None:
69 rv = bool(1)
70 else:
71 optD['dir'] = matchobj.group(1)
72 elif opt in ("-b", "--base"):
73 optD['base'] = arg
74 else:
75 optD[opt] = arg
76
77 return optD
78
79 def createMacroStr(key, val, keyColLen, status):
80 if keyColLen < len(key):
81 status = bool(1)
82 return None
83 else:
84 nsp = keyColLen - len(key)
85 arta 1.1 spaces = str()
86 for isp in range(nsp):
87 spaces += ' '
88 status = bool(0)
89 return '#define ' + key + spaces + val + '\n'
90
91 def createPerlConst(key, val, keyColLen, status):
92 if keyColLen < len(key):
93 status = bool(1)
94 return None
95 else:
96 nsp = keyColLen - len(key)
97 spaces = str()
98 for isp in range(nsp):
99 spaces += ' '
100 status = bool(0)
101 return 'use constant ' + key + ' => ' + spaces + val + ';\n'
102
103 def processParam(cfgfile, line, regexpComm, regexpDefs, regexpMake, regexpQuote, regexp, keymap, defs, cDefs, mDefs, perlConstSection, perlInitSection, section):
104 status = 0
105
106 arta 1.1 matchobj = regexpComm.match(line)
107 if not matchobj is None:
108 # Skip comment line
109 return bool(0)
110
111 matchobj = regexpDefs.match(line)
112 if not matchobj is None:
113 section.extend(list('defs'))
114 return bool(0)
115
116 matchobj = regexpMake.match(line)
117 if not matchobj is None:
118 section = 'make'
119 return bool(1)
120
121 if ''.join(section) == 'defs' or not cfgfile:
122 matchobj = regexp.match(line)
123 if not matchobj is None:
124 # We have a key-value line
125 keyCfgSp = matchobj.group(1)
126 val = matchobj.group(2)
127 arta 1.1
128 # Must map the indirect name to the actual name
129 if keymap:
130 # Map to actual name only if the keymap is not empty (which signifies NA).
131 if keyCfgSp in keymap:
132 key = keymap[keyCfgSp]
133 elif keyCfgSp == 'LOCAL_CONFIG_SET' or keyCfgSp == 'DRMS_SAMPLE_NAMESPACE':
134 # Ignore parameters that are not useful and shouldn't have been there in the first place
135 return bool(0)
136 elif not cfgfile:
137 # Should not be doing mapping for addenda
138 key = keyCfgSp
139 else:
140 raise Exception('badKeyMapKey', keyCfgSp)
141 else:
142 key = keyCfgSp
143
144 matchobj = regexpQuote.match(key)
145 if not matchobj is None:
146 quote = matchobj.group(1)
147 key = matchobj.group(2)
148 arta 1.1
149 # master defs dictionary
150 defs[key] = val
151
152 # C header file
153 if quote == "q":
154 # Add double-quotes
155 cDefs.extend(list(createMacroStr(key, '"' + val + '"', 40, status)))
156 elif quote == "p":
157 # Add parentheses
158 cDefs.extend(list(createMacroStr(key, '(' + val + ')', 40, status)))
159 elif quote == "a":
160 # Leave as-is
161 cDefs.extend(list(createMacroStr(key, val, 40, status)))
162 else:
163 # Unknown quote type
164 raise Exception('badQuoteQual', key)
165
166 if status:
167 raise Exception('paramNameTooLong', key)
168
169 arta 1.1 # Make file - val should never be quoted; just use as is
170 mDefs.extend(list(key + ' = ' + val + '\n'))
171
172 # Perl file - val should ALWAYS be single-quote quoted
173 # Save const info to a string
174 perlConstSection.extend(list(createPerlConst(key, "'" + val + "'", 40, status)))
175
176 if status:
177 raise Exception('paramNameTooLong', key)
178
179 # Save initialization information as a string. Now that we've defined
180 # constants (the names of which are the parameter names)
181 # we can refer to those in the init section. The key variable holds the
182 # name of the constant.
183 perlInitSection.extend(list(' $self->{_paramsH}->{' + key + '} = ' + key + ';\n'))
184 else:
185 # No quote qualifier
186 raise Exception('missingQuoteQual', key)
187
188 return bool(0)
189
190 arta 1.1 # We have some extraneous line or a newline - ignore.
191
192 # defs is a dictionary containing all parameters (should they be needed in this script)
193 def parseConfig(fin, cfile, mfile, pfile, keymap, addenda, defs, cDefs, mDefs, perlConstSection, perlInitSection):
194 rv = bool(0)
195
196 # Open required config file (config.local)
197 try:
198 # Examine each line, looking for key=value pairs.
199 regexpDefs = re.compile(r"^__DEFS__")
200 regexpMake = re.compile(r"^__MAKE__")
201 regexpComm = re.compile(r"^\s*#")
202 regexpQuote = re.compile(r"^\s*(\w):(.+)")
203 regexp = re.compile(r"^\s*(\S+)\s+(.+)")
204
205 section = list()
206
207 # Process the parameters in the configuration file
208 iscfg = bool(1)
209 if not fin is None:
210 for line in fin:
211 arta 1.1 ppRet = processParam(iscfg, line, regexpComm, regexpDefs, regexpMake, regexpQuote, regexp, keymap, defs, cDefs, mDefs, perlConstSection, perlInitSection, section)
212 if ppRet:
213 break;
214
215 # Process addenda - these are parameters that are not configurable and must be set in the
216 # NetDRMS build.
217 iscfg = bool(0)
218 for key in addenda:
219 item = key + ' ' + addenda[key]
220 ppRet = processParam(iscfg, item, regexpComm, regexpDefs, regexpMake, regexpQuote, regexp, keymap, defs, cDefs, mDefs, perlConstSection, perlInitSection, section)
221 if ppRet:
222 break;
223
224 except Exception as exc:
225 msg, violator = exc.args
226 if msg == 'badKeyMapKey':
227 # If we are here, then there was a non-empty keymap, and the parameter came from
228 # the configuration file.
229 print('Unknown parameter name ' + "'" + violator + "'" + ' in ' + cfgfile + '.', file=sys.stderr)
230 rv = bool(1)
231 elif msg == 'badQuoteQual':
232 arta 1.1 # The bad quote qualifier came from the configuration file, not the addenda, since
233 # we will have fixed any bad qualifiers in the addenda (which is populated by code).
234 print('Unknown quote qualifier ' + "'" + violator + "'" + ' in ' + cfgfile + '.', file=sys.stderr)
235 rv = bool(1)
236 elif msg == 'missingQuoteQual':
237 print('Missing quote qualifier for parameter ' + "'" + violator + "'" + ' in ' + cfgfile + '.', file=sys.stderr)
238 rv = bool(1)
239 elif msg == 'paramNameTooLong':
240 print('Macro name ' + "'" + violator + "' is too long.", file=sys.stderr)
241 rv = bool(1)
242 else:
243 # re-raise the exception
244 raise
245
246 return rv
247
248 def getMgrUIDLine(defs, uidParam):
249 rv = bool(0)
250
251 cmd = 'id -u ' + defs['SUMS_MANAGER']
252 try:
253 arta 1.1 ret = check_output(cmd, shell=True)
254 uidParam['q:SUMS_MANAGER_UID'] = ret.decode("utf-8")
255 except ValueError:
256 print('Unable to run cmd: ' + cmd + '.')
257 rv = bool(1)
258 except CalledProcessError:
259 print('Command ' + "'" + cmd + "'" + ' ran improperly.')
260 rv = bool(1)
261
262 return rv
263
264 def writeFiles(base, cfile, mfile, pfile, cDefs, mDefs, perlConstSection, perlInitSection):
265 rv = bool(0)
266
267 try:
268 with open(cfile, 'w') as cout, open(mfile, 'w') as mout, open(pfile, 'w') as pout:
269 # C file of macros
270 buf = '__' + base.upper() + '_H'
271 print('#ifndef ' + buf, file=cout)
272 print('#define ' + buf, file=cout)
273 print(''.join(cDefs), file=cout)
274 arta 1.1 print('#endif', file=cout)
275
276 # Make file of make variables
277 print('# This file contains a set of make-variable values - one for each configuration parameter.', file=mout)
278 print(''.join(mDefs), file=mout)
279
280 # Create the constants in the Perl file (mapping parameter name from config.local namespace to
281 # DRMS-module namespace
282 print(PERL_PREFIX, file=pout)
283 print(''.join(perlConstSection), file=pout)
284 print(PERL_FXNS, file=pout)
285 print('sub initialize', file=pout)
286 print('{', file=pout)
287 print(' my($self) = shift;', file=pout)
288 print('', file=pout)
289 print(''.join(perlInitSection), file=pout)
290 print('}', file=pout)
291
292 except IOError as exc:
293 sys.stderr.write(exc.strerror)
294 sys.stderr.write('Unable to open a parameter vile.')
295 arta 1.1 rv = bool(1)
296
297 return rv
298
299 def configureNet(cfgfile, cfile, mfile, pfile, base, keymap, addenda):
300 rv = bool(0)
301
302 defs = {}
303 cDefs = list()
304 mDefs = list()
305 perlConstSection = list()
306 perlInitSection = list()
307
308 try:
309 with open(cfgfile, 'r') as fin:
310 rv = parseConfig(fin, cfile, mfile, pfile, keymap, addenda, defs, cDefs, mDefs, perlConstSection, perlInitSection)
311 if rv == bool(0):
312 # Must add a parameter for the SUMS_MANAGER UID (for some reason)
313 uidParam = {}
314 rv = getMgrUIDLine(defs, uidParam)
315 if rv == bool(0):
316 arta 1.1 rv = parseConfig(None, cfile, mfile, pfile, keymap, uidParam, defs, cDefs, mDefs, perlConstSection, perlInitSection)
317
318 if rv == bool(0):
319 rv = writeFiles(base, cfile, mfile, pfile, cDefs, mDefs, perlConstSection, perlInitSection)
320 except IOError as exc:
321 sys.stderr.write(exc.strerror)
322 sys.stderr.write('Unable to read configuration file ' + cfgfile + '.')
323
324
325 def configureSdp(cfgfile, cfile, mfile, pfile, base, keymap, addenda):
326 rv = bool(0)
327
328 defs = {}
329 cDefs = list()
330 mDefs = list()
331 perlConstSection = list()
332 perlInitSection = list()
333
334 try:
335 with open(cfgfile, 'r') as fin:
336 rv = parseConfig(cfgfile, cfile, mfile, pfile, keymap, addenda, defs, cDefs, mDefs, perlConstSection, perlInitSection)
337 arta 1.1 if rv == bool(0):
338 rv = writeFiles(base, cfile, mfile, pfile, cDefs, mDefs, perlConstSection, perlInitSection)
339 except IOError as exc:
340 sys.stderr.write(exc.strerror)
341 sys.stderr.write('Unable to read configuration file ' + cfgfile + '.')
342
343 # Beginning of program
344 rv = RET_SUCCESS
345 net = bool(1)
346
347 # Parse arguments
348 if __name__ == "__main__":
349 optD = GetArgs(sys.argv[1:])
350
351 if not(optD is None):
352 # Ensure we are configuring a DRMS tree
353 cdir = os.path.realpath(os.getcwd())
354 versfile = cdir + '/base/' + VERS_FILE
355
356 if not os.path.isfile(versfile):
357 rv = RET_NOTDRMS
358 arta 1.1
359 # Determine whether we are localizing a Stanford build, or a NetDRMS build. If configsdp.txt exists, then
360 # it is a Stanford build, otherwise it is a NetDRMS build.
361 if rv == RET_SUCCESS:
362 stanfordFile = cdir + '/' + SDP_CFG
363 if os.path.isfile(stanfordFile):
364 net = bool(0)
365
366 cfile = optD['dir'] + '/' + optD['base'] + '.h'
367 mfile = optD['dir'] + '/' + optD['base'] + '.mk'
368 pfile = optD['dir'] + '/' + optD['base'] + '.pm'
369
370 if net:
371 addenda = {}
372
373 try:
374 with open(NET_CFGMAP, 'r') as fin:
375 regexpComm = re.compile(r"^\s*#")
376 regexp = re.compile(r"^\s*(\S+)\s+(\w:\S+)")
377 # Must map from config.local namespace to DRMS namespace (e.g., the names used for the C macros)
378 keymap = {}
379 arta 1.1 for line in fin:
380 matchobj = regexpComm.match(line)
381 if not matchobj is None:
382 # Skip comment line
383 continue
384
385 matchobj = regexp.match(line)
386 if not(matchobj is None):
387 # We have a key-value line
388 key = matchobj.group(1)
389 val = matchobj.group(2)
390 keymap[key] = val
391 except OSError:
392 sys.stderr.write('Unable to read configuration map-file ' + NET_CFGMAP + '.')
393 rv = bool(1)
394
395 # There are three parameters that are not configurable and must be set.
396 addenda['a:USER'] = 'NULL'
397 addenda['a:PASSWD'] = 'NULL'
398 addenda['p:DSDS_SUPPORT'] = '0'
399
400 arta 1.1 # We also need to set the UID of the SUMS manager. We have the name of the
401 # SUMS manager (it is in the configuration file)
402 configureNet(NET_CFG, cfile, mfile, pfile, optD['base'], keymap, addenda)
403 else:
404 configureSdp(SDP_CFG, cfile, mfile, pfile, optD['base'], {}, addenda)
405
406
407
|