Statistics
| Branch: | Tag: | Revision:

root / ncclient / content.py @ 408abf6d

History | View | Annotate | Download (7 kB)

1 94dd4342 Shikhar Bhushan
# Copyright 2009 Shikhar Bhushan
2 94dd4342 Shikhar Bhushan
#
3 94dd4342 Shikhar Bhushan
# Licensed under the Apache License, Version 2.0 (the "License");
4 94dd4342 Shikhar Bhushan
# you may not use this file except in compliance with the License.
5 94dd4342 Shikhar Bhushan
# You may obtain a copy of the License at
6 94dd4342 Shikhar Bhushan
#
7 94dd4342 Shikhar Bhushan
#    http://www.apache.org/licenses/LICENSE-2.0
8 94dd4342 Shikhar Bhushan
#
9 94dd4342 Shikhar Bhushan
# Unless required by applicable law or agreed to in writing, software
10 94dd4342 Shikhar Bhushan
# distributed under the License is distributed on an "AS IS" BASIS,
11 94dd4342 Shikhar Bhushan
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 94dd4342 Shikhar Bhushan
# See the License for the specific language governing permissions and
13 94dd4342 Shikhar Bhushan
# limitations under the License.
14 94dd4342 Shikhar Bhushan
15 a7cb58ce Shikhar Bhushan
16 a7cb58ce Shikhar Bhushan
"""The :mod:`content` module provides methods for creating XML documents, parsing XML, and converting between different XML representations. It uses :mod:`~xml.etree.ElementTree` internally.
17 a7cb58ce Shikhar Bhushan
"""
18 a7cb58ce Shikhar Bhushan
19 4de03d63 Shikhar Bhushan
from cStringIO import StringIO
20 94dd4342 Shikhar Bhushan
from xml.etree import cElementTree as ET
21 94dd4342 Shikhar Bhushan
22 ebf2bbc6 Shikhar Bhushan
from ncclient import NCClientError
23 ebf2bbc6 Shikhar Bhushan
24 a14c36f9 Shikhar Bhushan
class ContentError(NCClientError):
25 614a698d Shikhar Bhushan
    "Raised by methods of the :mod:`content` module in case of an error."
26 a14c36f9 Shikhar Bhushan
    pass
27 bf31e33e Shikhar Bhushan
28 65c6a607 Shikhar Bhushan
### Namespace-related
29 94dd4342 Shikhar Bhushan
30 a7cb58ce Shikhar Bhushan
#: Base NETCONf namespace
31 94dd4342 Shikhar Bhushan
BASE_NS = 'urn:ietf:params:xml:ns:netconf:base:1.0'
32 a7cb58ce Shikhar Bhushan
#: ... and this is BASE_NS according to Cisco devices tested
33 94dd4342 Shikhar Bhushan
CISCO_BS = 'urn:ietf:params:netconf:base:1.0'
34 408abf6d Shikhar Bhushan
#: namespace for Tail-f data model
35 408abf6d Shikhar Bhushan
TAILF_AAA_1_1 = 'http://tail-f.com/ns/aaa/1.1'
36 408abf6d Shikhar Bhushan
TAILF_EXECD_1_1 = 'http://tail-f.com/ns/execd/1.1'
37 94dd4342 Shikhar Bhushan
38 94dd4342 Shikhar Bhushan
try:
39 94dd4342 Shikhar Bhushan
    register_namespace = ET.register_namespace
40 94dd4342 Shikhar Bhushan
except AttributeError:
41 94dd4342 Shikhar Bhushan
    def register_namespace(prefix, uri):
42 94dd4342 Shikhar Bhushan
        from xml.etree import ElementTree
43 94dd4342 Shikhar Bhushan
        # cElementTree uses ElementTree's _namespace_map, so that's ok
44 94dd4342 Shikhar Bhushan
        ElementTree._namespace_map[uri] = prefix
45 94dd4342 Shikhar Bhushan
46 e91a5349 Shikhar Bhushan
# we'd like BASE_NS to be prefixed as "netconf"
47 94dd4342 Shikhar Bhushan
register_namespace('netconf', BASE_NS)
48 408abf6d Shikhar Bhushan
register_namespace('aaa', TAILF_AAA_1_1)
49 408abf6d Shikhar Bhushan
register_namespace('execd', TAILF_EXECD_1_1)
50 94dd4342 Shikhar Bhushan
51 65c6a607 Shikhar Bhushan
qualify = lambda tag, ns=BASE_NS: tag if ns is None else '{%s}%s' % (ns, tag)
52 bf31e33e Shikhar Bhushan
53 408abf6d Shikhar Bhushan
#: Deprecated
54 65c6a607 Shikhar Bhushan
multiqualify = lambda tag, nslist=(BASE_NS, CISCO_BS): [qualify(tag, ns) for ns in nslist]
55 94dd4342 Shikhar Bhushan
56 94dd4342 Shikhar Bhushan
unqualify = lambda tag: tag[tag.rfind('}')+1:]
57 94dd4342 Shikhar Bhushan
58 65c6a607 Shikhar Bhushan
### XML with Python data structures
59 65c6a607 Shikhar Bhushan
60 65c6a607 Shikhar Bhushan
class DictTree:
61 65c6a607 Shikhar Bhushan
62 65c6a607 Shikhar Bhushan
    @staticmethod
63 65c6a607 Shikhar Bhushan
    def Element(spec):
64 614a698d Shikhar Bhushan
        """DictTree -> Element
65 4f650d54 Shikhar Bhushan

66 614a698d Shikhar Bhushan
        :type spec: :obj:`dict` or :obj:`string` or :class:`~xml.etree.ElementTree.Element`
67 4f650d54 Shikhar Bhushan

68 614a698d Shikhar Bhushan
        :rtype: :class:`~xml.etree.ElementTree.Element`
69 614a698d Shikhar Bhushan
        """
70 65c6a607 Shikhar Bhushan
        if iselement(spec):
71 65c6a607 Shikhar Bhushan
            return spec
72 65c6a607 Shikhar Bhushan
        elif isinstance(spec, basestring):
73 65c6a607 Shikhar Bhushan
            return XML.Element(spec)
74 65c6a607 Shikhar Bhushan
        if not isinstance(spec, dict):
75 65c6a607 Shikhar Bhushan
            raise ContentError("Invalid tree spec")
76 65c6a607 Shikhar Bhushan
        if 'tag' in spec:
77 4f650d54 Shikhar Bhushan
            ele = ET.Element(spec.get('tag'), spec.get('attrib', {}))
78 65c6a607 Shikhar Bhushan
            ele.text = spec.get('text', '')
79 65c6a607 Shikhar Bhushan
            ele.tail = spec.get('tail', '')
80 65c6a607 Shikhar Bhushan
            subtree = spec.get('subtree', [])
81 65c6a607 Shikhar Bhushan
            # might not be properly specified as list but may be dict
82 0b7d3b31 Shikhar Bhushan
            if not isinstance(subtree, list):
83 65c6a607 Shikhar Bhushan
                subtree = [subtree]
84 65c6a607 Shikhar Bhushan
            for subele in subtree:
85 65c6a607 Shikhar Bhushan
                ele.append(DictTree.Element(subele))
86 65c6a607 Shikhar Bhushan
            return ele
87 65c6a607 Shikhar Bhushan
        elif 'comment' in spec:
88 65c6a607 Shikhar Bhushan
            return ET.Comment(spec.get('comment'))
89 65c6a607 Shikhar Bhushan
        else:
90 65c6a607 Shikhar Bhushan
            raise ContentError('Invalid tree spec')
91 4f650d54 Shikhar Bhushan
92 65c6a607 Shikhar Bhushan
    @staticmethod
93 0cdb8b3c Shikhar Bhushan
    def XML(spec, encoding='UTF-8'):
94 614a698d Shikhar Bhushan
        """DictTree -> XML
95 4f650d54 Shikhar Bhushan

96 614a698d Shikhar Bhushan
        :type spec: :obj:`dict` or :obj:`string` or :class:`~xml.etree.ElementTree.Element`
97 4f650d54 Shikhar Bhushan

98 614a698d Shikhar Bhushan
        :arg encoding: chraracter encoding
99 4f650d54 Shikhar Bhushan

100 614a698d Shikhar Bhushan
        :rtype: string
101 614a698d Shikhar Bhushan
        """
102 e52e8478 Shikhar Bhushan
        return Element.XML(DictTree.Element(spec), encoding)
103 65c6a607 Shikhar Bhushan
104 65c6a607 Shikhar Bhushan
class Element:
105 4f650d54 Shikhar Bhushan
106 65c6a607 Shikhar Bhushan
    @staticmethod
107 65c6a607 Shikhar Bhushan
    def DictTree(ele):
108 614a698d Shikhar Bhushan
        """DictTree -> Element
109 4f650d54 Shikhar Bhushan

110 614a698d Shikhar Bhushan
        :type spec: :class:`~xml.etree.ElementTree.Element`
111 614a698d Shikhar Bhushan
        :rtype: :obj:`dict`
112 614a698d Shikhar Bhushan
        """
113 65c6a607 Shikhar Bhushan
        return {
114 65c6a607 Shikhar Bhushan
            'tag': ele.tag,
115 65c6a607 Shikhar Bhushan
            'attributes': ele.attrib,
116 65c6a607 Shikhar Bhushan
            'text': ele.text,
117 65c6a607 Shikhar Bhushan
            'tail': ele.tail,
118 65c6a607 Shikhar Bhushan
            'subtree': [ Element.DictTree(child) for child in root.getchildren() ]
119 65c6a607 Shikhar Bhushan
        }
120 4f650d54 Shikhar Bhushan
121 65c6a607 Shikhar Bhushan
    @staticmethod
122 0cdb8b3c Shikhar Bhushan
    def XML(ele, encoding='UTF-8'):
123 614a698d Shikhar Bhushan
        """Element -> XML
124 4f650d54 Shikhar Bhushan

125 614a698d Shikhar Bhushan
        :type spec: :class:`~xml.etree.ElementTree.Element`
126 614a698d Shikhar Bhushan
        :arg encoding: character encoding
127 614a698d Shikhar Bhushan
        :rtype: :obj:`string`
128 614a698d Shikhar Bhushan
        """
129 65c6a607 Shikhar Bhushan
        xml = ET.tostring(ele, encoding)
130 e52e8478 Shikhar Bhushan
        if xml.startswith('<?xml'):
131 e52e8478 Shikhar Bhushan
            return xml
132 e52e8478 Shikhar Bhushan
        else:
133 e52e8478 Shikhar Bhushan
            return '<?xml version="1.0" encoding="%s"?>%s' % (encoding, xml)
134 65c6a607 Shikhar Bhushan
135 65c6a607 Shikhar Bhushan
class XML:
136 4f650d54 Shikhar Bhushan
137 65c6a607 Shikhar Bhushan
    @staticmethod
138 4de03d63 Shikhar Bhushan
    def DictTree(xml):
139 614a698d Shikhar Bhushan
        """XML -> DictTree
140 4f650d54 Shikhar Bhushan

141 614a698d Shikhar Bhushan
        :type spec: :obj:`string`
142 614a698d Shikhar Bhushan
        :rtype: :obj:`dict`
143 614a698d Shikhar Bhushan
        """
144 4de03d63 Shikhar Bhushan
        return Element.DictTree(XML.Element(xml))
145 4f650d54 Shikhar Bhushan
146 65c6a607 Shikhar Bhushan
    @staticmethod
147 65c6a607 Shikhar Bhushan
    def Element(xml):
148 614a698d Shikhar Bhushan
        """XML -> Element
149 4f650d54 Shikhar Bhushan

150 614a698d Shikhar Bhushan
        :type xml: :obj:`string`
151 614a698d Shikhar Bhushan
        :rtype: :class:`~xml.etree.ElementTree.Element`
152 614a698d Shikhar Bhushan
        """
153 65c6a607 Shikhar Bhushan
        return ET.fromstring(xml)
154 d771dffc Shikhar Bhushan
155 4de03d63 Shikhar Bhushan
dtree2ele = DictTree.Element
156 4de03d63 Shikhar Bhushan
dtree2xml = DictTree.XML
157 4de03d63 Shikhar Bhushan
ele2dtree = Element.DictTree
158 4de03d63 Shikhar Bhushan
ele2xml = Element.XML
159 4de03d63 Shikhar Bhushan
xml2dtree = XML.DictTree
160 4de03d63 Shikhar Bhushan
xml2ele = XML.Element
161 4de03d63 Shikhar Bhushan
162 d771dffc Shikhar Bhushan
### Other utility functions
163 d771dffc Shikhar Bhushan
164 d771dffc Shikhar Bhushan
iselement = ET.iselement
165 d771dffc Shikhar Bhushan
166 614a698d Shikhar Bhushan
def find(ele, tag, nslist=[]):
167 216bb34c Shikhar Bhushan
    """If *nslist* is empty, same as :meth:`xml.etree.ElementTree.Element.find`. If it is not, *tag* is interpreted as an unqualified name and qualified using each item in *nslist* (with a :const:`None` item in *nslit* meaning no qualification is done). The first match is returned.
168 4f650d54 Shikhar Bhushan

169 614a698d Shikhar Bhushan
    :arg nslist: optional list of namespaces
170 216bb34c Shikhar Bhushan
    :type nslit: `string` `list`
171 d771dffc Shikhar Bhushan
    """
172 614a698d Shikhar Bhushan
    if nslist:
173 d771dffc Shikhar Bhushan
        for qname in multiqualify(tag):
174 d771dffc Shikhar Bhushan
            found = ele.find(qname)
175 d771dffc Shikhar Bhushan
            if found is not None:
176 614a698d Shikhar Bhushan
                return found
177 614a698d Shikhar Bhushan
    else:
178 614a698d Shikhar Bhushan
        return ele.find(tag)
179 d771dffc Shikhar Bhushan
180 d771dffc Shikhar Bhushan
def parse_root(raw):
181 614a698d Shikhar Bhushan
    """Efficiently parses the root element of an XML document.
182 4f650d54 Shikhar Bhushan

183 216bb34c Shikhar Bhushan
    :arg raw: XML document
184 614a698d Shikhar Bhushan
    :type raw: string
185 614a698d Shikhar Bhushan
    :returns: a tuple of `(tag, attributes)`, where `tag` is the (qualified) name of the element and `attributes` is a dictionary of its attributes.
186 216bb34c Shikhar Bhushan
    :rtype: `tuple`
187 0cdb8b3c Shikhar Bhushan
    """
188 d771dffc Shikhar Bhushan
    fp = StringIO(raw[:1024]) # this is a guess but start element beyond 1024 bytes would be a bit absurd
189 d771dffc Shikhar Bhushan
    for event, element in ET.iterparse(fp, events=('start',)):
190 d771dffc Shikhar Bhushan
        return (element.tag, element.attrib)
191 d771dffc Shikhar Bhushan
192 0b7d3b31 Shikhar Bhushan
def validated_element(rep, tags=None, attrs=None, text=None):
193 614a698d Shikhar Bhushan
    """Checks if the root element meets the supplied criteria. Returns a :class:`~xml.etree.ElementTree.Element` instance if so, otherwise raises :exc:`ContentError`.
194 4f650d54 Shikhar Bhushan

195 0b7d3b31 Shikhar Bhushan
    :arg tags: tag name or a list of allowable tag names
196 614a698d Shikhar Bhushan
    :arg attrs: list of required attribute names, each item may be a list of allowable alternatives
197 614a698d Shikhar Bhushan
    :arg text: textual content to match
198 614a698d Shikhar Bhushan
    :type rep: :obj:`dict` or :obj:`string` or :class:`~xml.etree.ElementTree.Element`
199 0cdb8b3c Shikhar Bhushan
    """
200 d771dffc Shikhar Bhushan
    ele = dtree2ele(rep)
201 614a698d Shikhar Bhushan
    err = False
202 0b7d3b31 Shikhar Bhushan
    if tags:
203 0b7d3b31 Shikhar Bhushan
        if isinstance(tags, basestring):
204 0b7d3b31 Shikhar Bhushan
            tags = [tags]
205 614a698d Shikhar Bhushan
        if ele.tag not in tags:
206 614a698d Shikhar Bhushan
            err = True
207 614a698d Shikhar Bhushan
    if attrs:
208 d771dffc Shikhar Bhushan
        for req in attrs:
209 614a698d Shikhar Bhushan
            if isinstance(req, basestring): req = [req]
210 614a698d Shikhar Bhushan
            for alt in req:
211 614a698d Shikhar Bhushan
                if alt in ele.attrib:
212 d771dffc Shikhar Bhushan
                    break
213 d771dffc Shikhar Bhushan
            else:
214 614a698d Shikhar Bhushan
                err = True
215 614a698d Shikhar Bhushan
    if text and ele.text != text:
216 614a698d Shikhar Bhushan
        err = True
217 614a698d Shikhar Bhushan
    if err:
218 614a698d Shikhar Bhushan
        raise ContentError("Element [%s] does not meet requirements" % ele.tag)
219 d771dffc Shikhar Bhushan
    return ele