1
2
3 """
4 ::
5
6 Copyright 2010 Glencoe Software, Inc. All rights reserved.
7 Use is subject to license terms supplied in LICENSE.txt
8
9 """
10
11 """
12 Module which parses an icegrid XML file for configuration settings.
13
14 see ticket:800
15 see ticket:2213 - Replacing Java Preferences API
16 """
17
18 import re
19 import os
20 import path
21 import time
22 import logging
23 import portalocker
24
25 import xml.dom.minidom
26
27 try:
28 from xml.etree.ElementTree import XML, Element, SubElement, Comment, ElementTree, tostring
29 except ImportError:
30 from elementtree.ElementTree import XML, Element, SubElement, Comment, ElementTree, tostring
31
32
34 """
35 Object to record all the various locations
36 that the active configuration can come from.
37 """
38
39 - def __init__(self, user_specified = None):
40 self.fallback = "default"
41 self.user_specified = user_specified
42 self.from_os_environ = os.environ.get("OMERO_CONFIG", None)
43
45 if self.user_specified is not None:
46 return self.user_specified
47 elif self.from_os_environ is not None:
48 return self.from_os_environ
49 return None
50
52 self.user_specified = value
53
57
59 """
60 In some cases the environment chosen
61 should not be persisted.
62 """
63 if self.user_specified:
64 return self.user_specified
65 else:
66 return self.internal_value(config)
67
69 if self.user_specified:
70 return self.user_specified
71 elif self.from_os_environ:
72 return self.from_os_environ
73 else:
74 return self.internal_value(config)
75
76
78 """
79 dict-like wrapper around the config.xml file usually stored
80 in etc/grid. For a copy of the dict, use "as_map"
81 """
82 KEY = "omero.config.version"
83 VERSION = "4.2.1"
84 INTERNAL = "__ACTIVE__"
85 DEFAULT = "omero.config.profile"
86 IGNORE = (KEY, DEFAULT)
87
88 - def __init__(self, filename, env_config = None, exclusive = True):
89 self.logger = logging.getLogger(self.__class__.__name__)
90 self.XML = None
91 self.filename = filename
92 self.env_config = Environment(env_config)
93 self.exclusive = exclusive
94 self.save_on_close = True
95
96 try:
97
98
99 self.source = open(filename, "a+")
100 self.lock = self._open_lock()
101 except IOError:
102 self.logger.debug("open('%s', 'a+') failed" % filename)
103 self.lock = None
104 self.exclusive = False
105 self.save_on_close = False
106
107
108
109
110
111 val = self.env_config.is_non_default()
112 if val is not None:
113 raise Exception("Non-default OMERO_CONFIG on read-only: %s" % val)
114
115 self.source = open(filename, "r")
116
117 if self.exclusive:
118 try:
119 portalocker.lock(self.lock, portalocker.LOCK_NB|portalocker.LOCK_EX)
120 except portalocker.LockException, le:
121 self.lock = None
122 self.close()
123 raise
124
125 self.source.seek(0)
126 text = self.source.read()
127
128 if text:
129 self.XML = XML(text)
130 try:
131 self.version_check()
132 except:
133 self.close()
134 raise
135
136
137 if self.XML is None:
138 default = self.default()
139 self.XML = Element("icegrid")
140 properties = SubElement(self.XML, "properties", id=self.INTERNAL)
141 _ = SubElement(properties, "property", name=self.DEFAULT, value=default)
142 _ = SubElement(properties, "property", name=self.KEY, value=self.VERSION)
143 properties = SubElement(self.XML, "properties", id=default)
144 _ = SubElement(properties, "property", name=self.KEY, value=self.VERSION)
145
147 return open("%s.lock" % self.filename, "a+")
148
150 if self.lock is not None:
151 self.lock.close()
152 self.lock = None
153 try:
154 os.remove("%s.lock" % self.filename)
155 except:
156
157 self.logger.error("Failed to removed lock file, ignoring", exc_info=True)
158 pass
159
168
174
176 """
177 Currently we are assuming that all blocks without a 4.2.0 version
178 are bogus. The configuration script when it generates an initial
179 config.xml will use prefs.class to parse the existing values and
180 immediately do the upgrade.
181 """
182 if version == "4.2.0":
183
184
185
186 if props:
187 for x in props.getchildren():
188 if x.get("name", "").startswith("omero.ldap"):
189 orig = x.get("value", "")
190 val = orig.replace("${omero.dollar}", "")
191 val = val.replace("${", "@{")
192 x.set("value", val)
193 self.logger.info("Upgraded 4.2.0 property: %s => %s", orig, val)
194 else:
195 raise Exception("Version mismatch: %s has %s" % (props.get("id"), version))
196
199
200 - def properties(self, id = None, filter_internal = False):
201
202 if self.XML is None:
203 return None
204
205 props = self.XML.findall("./properties")
206 if id is None:
207 rv = list()
208 for x in props:
209 id = x.attrib["id"]
210 if filter_internal:
211 if id == self.INTERNAL:
212 continue
213 rv.append((id, x))
214 return rv
215 for p in props:
216 if "id" in p.attrib and p.attrib["id"] == id:
217 return p
218
226
232
239
241 """
242 Creates a fresh <icegrid> block (removing any unwanted
243 intra-element whitespace) and overwrites the file on disk.
244 """
245 icegrid = Element("icegrid")
246 comment = Comment("\n".join(["\n",
247 "\tThis file was generated at %s by the OmeroConfig system.",
248 "\tDo not edit directly but see bin/omero config for details.",
249 "\tThis file may be included into your IceGrid application.",
250 "\n"]) % time.ctime())
251 icegrid.append(comment)
252
253
254
255 default = self.env_config.for_save(self)
256 internal = SubElement(icegrid, "properties", id=self.INTERNAL)
257 SubElement(internal, "property", name=self.DEFAULT, value=default)
258 SubElement(internal, "property", name=self.KEY, value=self.VERSION)
259 to_copy = self.properties(default)
260 if to_copy is not None:
261 for x in to_copy.getchildren():
262 if x.get("name") != self.DEFAULT and x.get("name") != self.KEY:
263 SubElement(internal, "property", x.attrib)
264 else:
265
266 properties = SubElement(icegrid, "properties", id=default)
267 SubElement(properties, "property", name=self.KEY, value=self.VERSION)
268
269 prop_list = self.properties(None, True)
270 for k, p in prop_list:
271 self.clear_text(p)
272 icegrid.append(p)
273 self.source.seek(0)
274 self.source.truncate()
275 self.source.write(self.element_to_xml(icegrid))
276 self.source.flush()
277
279 try:
280
281
282
283 if self.XML is not None and self.save_on_close:
284 self.save()
285 self.XML = None
286 finally:
287 try:
288 if self.source is not None:
289 self.source.close()
290 self.source = None
291 finally:
292 self._close_lock()
293
295
296 if c is None:
297 return {}
298
299 rv = dict()
300 props = c.findall("./property")
301 for p in props:
302 if "name" in p.attrib:
303 rv[p.attrib["name"]] = p.attrib.get("value", "")
304 return rv
305
306 - def dict_to_text(self, parsed = None):
307
308 if parsed is None:
309 return
310
311 rv = ""
312 for k, v in parsed.items():
313 rv += "%s=%s" % (k, v)
314 return rv
315
317 string = tostring(elem, 'utf-8')
318 return xml.dom.minidom.parseString(string).toprettyxml(" ", "\n", None)
319
320 - def clear_text(self, p):
321 """
322 To prevent the accumulation of text outside of elements (including whitespace)
323 we walk the given element and remove tail from it and it's children.
324 """
325 p.tail = ""
326 p.text = ""
327 for p2 in p.getchildren():
328 self.clear_text(p2)
329
330
331
332
335
338
341
355
365