From 3d1cea3424a91d5ee25fc173dd6fd34bb6b7421c Mon Sep 17 00:00:00 2001 From: Sergey Lukjanov Date: Mon, 6 May 2013 14:01:12 +0400 Subject: [PATCH] Patch for minidom's writexml has been added * patch applied only for python < 2.7.3 * unit tests added for patch and xml conf generator * it has been tested at python 2.6.7, 2.7.2-2.7.4 Fixes: bug #1174841 Change-Id: I6db89a06a4fa86b62a582d0188d0b7f1a508dcd2 --- savanna/service/cluster_ops.py | 6 +++ savanna/tests/unit/test_cluster_ops.py | 54 +++++++++++++++++++ savanna/tests/unit/test_patches.py | 73 ++++++++++++++++++++++++++ savanna/utils/patches.py | 67 +++++++++++++++++++++++ 4 files changed, 200 insertions(+) create mode 100644 savanna/tests/unit/test_cluster_ops.py create mode 100644 savanna/tests/unit/test_patches.py create mode 100644 savanna/utils/patches.py diff --git a/savanna/service/cluster_ops.py b/savanna/service/cluster_ops.py index 9b8a92a7..8fef2f69 100644 --- a/savanna/service/cluster_ops.py +++ b/savanna/service/cluster_ops.py @@ -26,6 +26,7 @@ from savanna.storage.db import DB from savanna.storage.models import Node, ServiceUrl from savanna.storage.storage import update_cluster_status from savanna.utils.openstack.nova import novaclient +from savanna.utils.patches import patch_minidom_writexml LOG = logging.getLogger(__name__) @@ -303,6 +304,11 @@ def _generate_xml_configs(clmap): return xml_configs +# Patches minidom's writexml to avoid excess whitespaces in generated xml +# configuration files that brakes Hadoop. +patch_minidom_writexml() + + def _create_xml(configs, global_conf): doc = xml.Document() diff --git a/savanna/tests/unit/test_cluster_ops.py b/savanna/tests/unit/test_cluster_ops.py new file mode 100644 index 00000000..420119bc --- /dev/null +++ b/savanna/tests/unit/test_cluster_ops.py @@ -0,0 +1,54 @@ +# Copyright (c) 2013 Mirantis Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or +# implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from savanna.service.cluster_ops import _create_xml +import unittest + + +class ConfigGeneratorTest(unittest.TestCase): + def test_xml_generator(self): + config = { + 'key-1': 'value-1', + 'key-2': 'value-2', + 'key-3': 'value-3', + 'key-4': 'value-4', + 'key-5': 'value-5', + } + xml = _create_xml(config, config.keys()) + self.assertEqual(xml, """ + + + + key-3 + value-3 + + + key-2 + value-2 + + + key-1 + value-1 + + + key-5 + value-5 + + + key-4 + value-4 + + +""") diff --git a/savanna/tests/unit/test_patches.py b/savanna/tests/unit/test_patches.py new file mode 100644 index 00000000..16d4af59 --- /dev/null +++ b/savanna/tests/unit/test_patches.py @@ -0,0 +1,73 @@ +# Copyright (c) 2013 Mirantis Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or +# implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from savanna.utils.patches import patch_minidom_writexml +import unittest +import xml.dom.minidom as xml + + +class MinidomPatchesTest(unittest.TestCase): + def setUp(self): + patch_minidom_writexml() + + def _generate_n_prettify_xml(self): + doc = xml.Document() + pi = doc.createProcessingInstruction('xml-smth', + 'type="text/smth" ' + 'href="test.smth"') + doc.insertBefore(pi, doc.firstChild) + configuration = doc.createElement("root") + doc.appendChild(configuration) + for idx in xrange(0, 5): + elem = doc.createElement("element") + configuration.appendChild(elem) + name = doc.createElement("name") + elem.appendChild(name) + name_text = doc.createTextNode("key-%s" % idx) + name.appendChild(name_text) + value = doc.createElement("value") + elem.appendChild(value) + value_text = doc.createTextNode("value-%s" % idx) + value.appendChild(value_text) + + return doc.toprettyxml(indent=" ") + + def test_minidom_toprettyxml(self): + self.assertEqual(self._generate_n_prettify_xml(), + """ + + + + key-0 + value-0 + + + key-1 + value-1 + + + key-2 + value-2 + + + key-3 + value-3 + + + key-4 + value-4 + + +""") diff --git a/savanna/utils/patches.py b/savanna/utils/patches.py new file mode 100644 index 00000000..864ac067 --- /dev/null +++ b/savanna/utils/patches.py @@ -0,0 +1,67 @@ +# Copyright (c) 2013 Mirantis Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or +# implied. +# See the License for the specific language governing permissions and +# limitations under the License. + + +def patch_minidom_writexml(): + """Patch for xml.dom.minidom toprettyxml bug with whitespaces around text + + (This patch will be applied for all Python versions < 2.7.3) + + Issue: http://bugs.python.org/issue4147 + Patch: http://hg.python.org/cpython/rev/cb6614e3438b/ + Description: http://ronrothman.com/public/leftbraned/xml-dom-minidom-\ + toprettyxml-and-silly-whitespace/#best-solution + """ + + import sys + if sys.version_info >= (2, 7, 3): + return + + from xml.dom.minidom import Element, Node, Text, _write_data + + def writexml(self, writer, indent="", addindent="", newl=""): + # indent = current indentation + # addindent = indentation to add to higher levels + # newl = newline string + writer.write(indent + "<" + self.tagName) + + attrs = self._get_attributes() + a_names = attrs.keys() + a_names.sort() + + for a_name in a_names: + writer.write(" %s=\"" % a_name) + _write_data(writer, attrs[a_name].value) + writer.write("\"") + if self.childNodes: + writer.write(">") + if (len(self.childNodes) == 1 + and self.childNodes[0].nodeType == Node.TEXT_NODE): + self.childNodes[0].writexml(writer, '', '', '') + else: + writer.write(newl) + for node in self.childNodes: + node.writexml(writer, indent + addindent, addindent, newl) + writer.write(indent) + writer.write("%s" % (self.tagName, newl)) + else: + writer.write("/>%s" % (newl)) + + Element.writexml = writexml + + def writexml(self, writer, indent="", addindent="", newl=""): + _write_data(writer, "%s%s%s" % (indent, self.data, newl)) + + Text.writexml = writexml