Ensure proper ordering of XML arguments
Random hash ordering can cause the 'encoding' argument to be rendered
in the XML string before the 'version' argument, resulting in XML
deserialization errors on the Keystone side. This modifies
Document's string rendering to not rely on dict ordering when
constructing the header tag's version + encoding arguments and avoids
XML auth issues when using PYTHONHASHSEED=random.
Change-Id: I66d18a2f8da1088ec5d310a43c84df7e4d6a6f6b
diff --git a/tempest/common/xml_utils.py b/tempest/common/xml_utils.py
index b1bf789..7d460a4 100644
--- a/tempest/common/xml_utils.py
+++ b/tempest/common/xml_utils.py
@@ -14,6 +14,7 @@
# under the License.
import collections
+import copy
XMLNS_11 = "http://docs.openstack.org/compute/api/v1.1"
XMLNS_V3 = "http://docs.openstack.org/compute/api/v1.1"
@@ -78,16 +79,19 @@
class Document(Element):
def __init__(self, *args, **kwargs):
- if 'version' not in kwargs:
- kwargs['version'] = '1.0'
- if 'encoding' not in kwargs:
- kwargs['encoding'] = 'UTF-8'
Element.__init__(self, '?xml', *args, **kwargs)
def __str__(self):
- args = " ".join(['%s="%s"' %
- (k, v if v is not None else "")
- for k, v in self._attrs.items()])
+ attrs = copy.copy(self._attrs)
+ # pop the required standard attrs out and render in required
+ # order.
+ vers = attrs.pop('version', '1.0')
+ enc = attrs.pop('encoding', 'UTF-8')
+ args = 'version="%s" encoding="%s"' % (vers, enc)
+ if attrs:
+ args = " ".join([args] + ['%s="%s"' %
+ (k, v if v is not None else "")
+ for k, v in attrs.items()])
string = '<?xml %s?>\n' % args
for element in self._elements:
string += str(element)
diff --git a/tempest/tests/test_xml_utils.py b/tempest/tests/test_xml_utils.py
new file mode 100644
index 0000000..53e31c4
--- /dev/null
+++ b/tempest/tests/test_xml_utils.py
@@ -0,0 +1,35 @@
+#
+# Copyright 2014 Hewlett-Packard Development Company, L.P.
+#
+# 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 tempest.common import xml_utils
+from tempest.tests import base
+
+
+class TestDocumentXML(base.TestCase):
+ def test_xml_document_ordering_version_encoding(self):
+ expected = '<?xml version="1.0" encoding="UTF-8"?>'
+ xml_out = str(xml_utils.Document())
+ self.assertEqual(expected, xml_out.strip())
+
+ xml_out = str(xml_utils.Document(encoding='UTF-8', version='1.0'))
+ self.assertEqual(expected, xml_out.strip())
+
+ xml_out = str(xml_utils.Document(version='1.0', encoding='UTF-8'))
+ self.assertEqual(expected, xml_out.strip())
+
+ def test_xml_document_additonal_attrs(self):
+ expected = '<?xml version="1.0" encoding="UTF-8" foo="bar"?>'
+ xml_out = str(xml_utils.Document(foo='bar'))
+ self.assertEqual(expected, xml_out.strip())