blob: b29b932af5ec6bea159ab85f3bb19f23dce5d9fb [file] [log] [blame]
Kurt Taylor6a6f5be2013-04-02 18:53:47 -04001# Copyright 2012 IBM Corp.
Dan Smithba6cb162012-08-14 07:22:42 -07002# All Rights Reserved.
3#
4# Licensed under the Apache License, Version 2.0 (the "License"); you may
5# not use this file except in compliance with the License. You may obtain
6# a copy of the License at
7#
8# http://www.apache.org/licenses/LICENSE-2.0
9#
10# Unless required by applicable law or agreed to in writing, software
11# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13# License for the specific language governing permissions and limitations
14# under the License.
15
Attila Fazekasc74aede2013-09-11 13:04:02 +020016import collections
17
Dan Smithba6cb162012-08-14 07:22:42 -070018XMLNS_11 = "http://docs.openstack.org/compute/api/v1.1"
ivan-zhu8f992be2013-07-31 14:56:58 +080019XMLNS_V3 = "http://docs.openstack.org/compute/api/v1.1"
Dan Smithba6cb162012-08-14 07:22:42 -070020
Ann Kamyshnikova26111542013-12-26 12:25:43 +040021NEUTRON_NAMESPACES = {
22 'router': "http://docs.openstack.org/ext/neutron/router/api/v1.0",
23 'provider': 'http://docs.openstack.org/ext/provider/api/v1.0',
24}
25
Dan Smithba6cb162012-08-14 07:22:42 -070026
27# NOTE(danms): This is just a silly implementation to help make generating
28# XML faster for prototyping. Could be replaced with proper etree gorp
29# if desired
30class Element(object):
31 def __init__(self, element_name, *args, **kwargs):
32 self.element_name = element_name
33 self._attrs = kwargs
34 self._elements = list(args)
35
36 def add_attr(self, name, value):
37 self._attrs[name] = value
38
39 def append(self, element):
40 self._elements.append(element)
41
42 def __str__(self):
ivan-zhuae5f98a2013-10-18 15:57:54 +080043 args = " ".join(['%s="%s"' %
44 (k, v if v is not None else "")
45 for k, v in self._attrs.items()])
Dan Smithba6cb162012-08-14 07:22:42 -070046 string = '<%s %s' % (self.element_name, args)
47 if not self._elements:
48 string += '/>'
49 return string
50
51 string += '>'
52
53 for element in self._elements:
54 string += str(element)
55
56 string += '</%s>' % self.element_name
57
58 return string
59
60 def __getitem__(self, name):
61 for element in self._elements:
62 if element.element_name == name:
63 return element
64 raise KeyError("No such element `%s'" % name)
65
66 def __getattr__(self, name):
67 if name in self._attrs:
68 return self._attrs[name]
69 return object.__getattr__(self, name)
70
71 def attributes(self):
72 return self._attrs.items()
73
74 def children(self):
75 return self._elements
76
77
78class Document(Element):
79 def __init__(self, *args, **kwargs):
80 if 'version' not in kwargs:
81 kwargs['version'] = '1.0'
82 if 'encoding' not in kwargs:
83 kwargs['encoding'] = 'UTF-8'
84 Element.__init__(self, '?xml', *args, **kwargs)
85
86 def __str__(self):
ivan-zhuae5f98a2013-10-18 15:57:54 +080087 args = " ".join(['%s="%s"' %
88 (k, v if v is not None else "")
89 for k, v in self._attrs.items()])
Dan Smithba6cb162012-08-14 07:22:42 -070090 string = '<?xml %s?>\n' % args
91 for element in self._elements:
92 string += str(element)
93 return string
94
95
96class Text(Element):
97 def __init__(self, content=""):
98 Element.__init__(self, None)
99 self.__content = content
100
101 def __str__(self):
102 return self.__content
103
104
Rossella Sblendidoc1724112013-11-28 14:16:48 +0100105def parse_array(node, plurals=None):
106 array = []
107 for child in node.getchildren():
108 array.append(xml_to_json(child,
109 plurals))
110 return array
111
112
113def xml_to_json(node, plurals=None):
Dan Smithba6cb162012-08-14 07:22:42 -0700114 """This does a really braindead conversion of an XML tree to
115 something that looks like a json dump. In cases where the XML
116 and json structures are the same, then this "just works". In
Attila Fazekasb2902af2013-02-16 16:22:44 +0100117 others, it requires a little hand-editing of the result.
118 """
Dan Smithba6cb162012-08-14 07:22:42 -0700119 json = {}
Ann Kamyshnikovaf6bd5782014-01-13 14:08:04 +0400120 bool_flag = False
121 int_flag = False
122 long_flag = False
Dan Smithba6cb162012-08-14 07:22:42 -0700123 for attr in node.keys():
Davanum Srinivas8d226cc2013-03-08 09:35:36 -0500124 if not attr.startswith("xmlns"):
125 json[attr] = node.get(attr)
Ann Kamyshnikovaf6bd5782014-01-13 14:08:04 +0400126 if json[attr] == 'bool':
127 bool_flag = True
128 elif json[attr] == 'int':
129 int_flag = True
130 elif json[attr] == 'long':
131 long_flag = True
Dan Smithba6cb162012-08-14 07:22:42 -0700132 if not node.getchildren():
Ann Kamyshnikovaf6bd5782014-01-13 14:08:04 +0400133 if bool_flag:
134 return node.text == 'True'
135 elif int_flag:
136 return int(node.text)
137 elif long_flag:
138 return long(node.text)
139 else:
140 return node.text or json
Dan Smithba6cb162012-08-14 07:22:42 -0700141 for child in node.getchildren():
142 tag = child.tag
143 if tag.startswith("{"):
144 ns, tag = tag.split("}", 1)
Ann Kamyshnikova26111542013-12-26 12:25:43 +0400145 for key, uri in NEUTRON_NAMESPACES.iteritems():
146 if uri == ns[1:]:
147 tag = key + ":" + tag
Rossella Sblendidoc1724112013-11-28 14:16:48 +0100148 if plurals is not None and tag in plurals:
Andrea Frittoli513c8392014-01-10 14:16:34 +0000149 json[tag] = parse_array(child, plurals)
Rossella Sblendidoc1724112013-11-28 14:16:48 +0100150 else:
Andrea Frittoli513c8392014-01-10 14:16:34 +0000151 json[tag] = xml_to_json(child, plurals)
Dan Smithba6cb162012-08-14 07:22:42 -0700152 return json
Attila Fazekasc74aede2013-09-11 13:04:02 +0200153
154
155def deep_dict_to_xml(dest, source):
156 """Populates the ``dest`` xml element with the ``source`` ``Mapping``
157 elements, if the source Mapping's value is also a ``Mapping``
158 they will be recursively added as a child elements.
159 :param source: A python ``Mapping`` (dict)
160 :param dest: XML child element will be added to the ``dest``
161 """
162 for element, content in source.iteritems():
163 if isinstance(content, collections.Mapping):
164 xml_element = Element(element)
165 deep_dict_to_xml(xml_element, content)
166 dest.append(xml_element)
167 else:
168 dest.append(Element(element, content))