Statistics
| Branch: | Tag: | Revision:

root / ncclient / content.py @ 4f650d54

History | View | Annotate | Download (6.4 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 4de03d63 Shikhar Bhushan
from cStringIO import StringIO
16 94dd4342 Shikhar Bhushan
from xml.etree import cElementTree as ET
17 94dd4342 Shikhar Bhushan
18 ebf2bbc6 Shikhar Bhushan
from ncclient import NCClientError
19 ebf2bbc6 Shikhar Bhushan
20 a14c36f9 Shikhar Bhushan
class ContentError(NCClientError):
21 614a698d Shikhar Bhushan
    "Raised by methods of the :mod:`content` module in case of an error."
22 a14c36f9 Shikhar Bhushan
    pass
23 bf31e33e Shikhar Bhushan
24 65c6a607 Shikhar Bhushan
### Namespace-related
25 94dd4342 Shikhar Bhushan
26 614a698d Shikhar Bhushan
# : Base NETCONf namespace
27 94dd4342 Shikhar Bhushan
BASE_NS = 'urn:ietf:params:xml:ns:netconf:base:1.0'
28 614a698d Shikhar Bhushan
# : ... and this is BASE_NS according to Cisco devices tested
29 94dd4342 Shikhar Bhushan
CISCO_BS = 'urn:ietf:params:netconf:base:1.0'
30 94dd4342 Shikhar Bhushan
31 94dd4342 Shikhar Bhushan
try:
32 94dd4342 Shikhar Bhushan
    register_namespace = ET.register_namespace
33 94dd4342 Shikhar Bhushan
except AttributeError:
34 94dd4342 Shikhar Bhushan
    def register_namespace(prefix, uri):
35 94dd4342 Shikhar Bhushan
        from xml.etree import ElementTree
36 94dd4342 Shikhar Bhushan
        # cElementTree uses ElementTree's _namespace_map, so that's ok
37 94dd4342 Shikhar Bhushan
        ElementTree._namespace_map[uri] = prefix
38 94dd4342 Shikhar Bhushan
39 e91a5349 Shikhar Bhushan
# we'd like BASE_NS to be prefixed as "netconf"
40 94dd4342 Shikhar Bhushan
register_namespace('netconf', BASE_NS)
41 94dd4342 Shikhar Bhushan
42 65c6a607 Shikhar Bhushan
qualify = lambda tag, ns=BASE_NS: tag if ns is None else '{%s}%s' % (ns, tag)
43 bf31e33e Shikhar Bhushan
44 0cdb8b3c Shikhar Bhushan
# deprecated
45 65c6a607 Shikhar Bhushan
multiqualify = lambda tag, nslist=(BASE_NS, CISCO_BS): [qualify(tag, ns) for ns in nslist]
46 94dd4342 Shikhar Bhushan
47 94dd4342 Shikhar Bhushan
unqualify = lambda tag: tag[tag.rfind('}')+1:]
48 94dd4342 Shikhar Bhushan
49 65c6a607 Shikhar Bhushan
### XML with Python data structures
50 65c6a607 Shikhar Bhushan
51 65c6a607 Shikhar Bhushan
class DictTree:
52 65c6a607 Shikhar Bhushan
53 65c6a607 Shikhar Bhushan
    @staticmethod
54 65c6a607 Shikhar Bhushan
    def Element(spec):
55 614a698d Shikhar Bhushan
        """DictTree -> Element
56 4f650d54 Shikhar Bhushan

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

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

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

89 614a698d Shikhar Bhushan
        :arg encoding: chraracter encoding
90 4f650d54 Shikhar Bhushan

91 614a698d Shikhar Bhushan
        :rtype: string
92 614a698d Shikhar Bhushan
        """
93 e52e8478 Shikhar Bhushan
        return Element.XML(DictTree.Element(spec), encoding)
94 65c6a607 Shikhar Bhushan
95 65c6a607 Shikhar Bhushan
class Element:
96 4f650d54 Shikhar Bhushan
97 65c6a607 Shikhar Bhushan
    @staticmethod
98 65c6a607 Shikhar Bhushan
    def DictTree(ele):
99 614a698d Shikhar Bhushan
        """DictTree -> Element
100 4f650d54 Shikhar Bhushan

101 614a698d Shikhar Bhushan
        :type spec: :class:`~xml.etree.ElementTree.Element`
102 614a698d Shikhar Bhushan
        :rtype: :obj:`dict`
103 614a698d Shikhar Bhushan
        """
104 65c6a607 Shikhar Bhushan
        return {
105 65c6a607 Shikhar Bhushan
            'tag': ele.tag,
106 65c6a607 Shikhar Bhushan
            'attributes': ele.attrib,
107 65c6a607 Shikhar Bhushan
            'text': ele.text,
108 65c6a607 Shikhar Bhushan
            'tail': ele.tail,
109 65c6a607 Shikhar Bhushan
            'subtree': [ Element.DictTree(child) for child in root.getchildren() ]
110 65c6a607 Shikhar Bhushan
        }
111 4f650d54 Shikhar Bhushan
112 65c6a607 Shikhar Bhushan
    @staticmethod
113 0cdb8b3c Shikhar Bhushan
    def XML(ele, encoding='UTF-8'):
114 614a698d Shikhar Bhushan
        """Element -> XML
115 4f650d54 Shikhar Bhushan

116 614a698d Shikhar Bhushan
        :type spec: :class:`~xml.etree.ElementTree.Element`
117 614a698d Shikhar Bhushan
        :arg encoding: character encoding
118 614a698d Shikhar Bhushan
        :rtype: :obj:`string`
119 614a698d Shikhar Bhushan
        """
120 65c6a607 Shikhar Bhushan
        xml = ET.tostring(ele, encoding)
121 e52e8478 Shikhar Bhushan
        if xml.startswith('<?xml'):
122 e52e8478 Shikhar Bhushan
            return xml
123 e52e8478 Shikhar Bhushan
        else:
124 e52e8478 Shikhar Bhushan
            return '<?xml version="1.0" encoding="%s"?>%s' % (encoding, xml)
125 65c6a607 Shikhar Bhushan
126 65c6a607 Shikhar Bhushan
class XML:
127 4f650d54 Shikhar Bhushan
128 65c6a607 Shikhar Bhushan
    @staticmethod
129 4de03d63 Shikhar Bhushan
    def DictTree(xml):
130 614a698d Shikhar Bhushan
        """XML -> DictTree
131 4f650d54 Shikhar Bhushan

132 614a698d Shikhar Bhushan
        :type spec: :obj:`string`
133 614a698d Shikhar Bhushan
        :rtype: :obj:`dict`
134 614a698d Shikhar Bhushan
        """
135 4de03d63 Shikhar Bhushan
        return Element.DictTree(XML.Element(xml))
136 4f650d54 Shikhar Bhushan
137 65c6a607 Shikhar Bhushan
    @staticmethod
138 65c6a607 Shikhar Bhushan
    def Element(xml):
139 614a698d Shikhar Bhushan
        """XML -> Element
140 4f650d54 Shikhar Bhushan

141 614a698d Shikhar Bhushan
        :type xml: :obj:`string`
142 614a698d Shikhar Bhushan
        :rtype: :class:`~xml.etree.ElementTree.Element`
143 614a698d Shikhar Bhushan
        """
144 65c6a607 Shikhar Bhushan
        return ET.fromstring(xml)
145 d771dffc Shikhar Bhushan
146 4de03d63 Shikhar Bhushan
dtree2ele = DictTree.Element
147 4de03d63 Shikhar Bhushan
dtree2xml = DictTree.XML
148 4de03d63 Shikhar Bhushan
ele2dtree = Element.DictTree
149 4de03d63 Shikhar Bhushan
ele2xml = Element.XML
150 4de03d63 Shikhar Bhushan
xml2dtree = XML.DictTree
151 4de03d63 Shikhar Bhushan
xml2ele = XML.Element
152 4de03d63 Shikhar Bhushan
153 d771dffc Shikhar Bhushan
### Other utility functions
154 d771dffc Shikhar Bhushan
155 d771dffc Shikhar Bhushan
iselement = ET.iselement
156 d771dffc Shikhar Bhushan
157 614a698d Shikhar Bhushan
def find(ele, tag, nslist=[]):
158 614a698d 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`. The first match is returned.
159 4f650d54 Shikhar Bhushan

160 614a698d Shikhar Bhushan
    :arg nslist: optional list of namespaces
161 d771dffc Shikhar Bhushan
    """
162 614a698d Shikhar Bhushan
    if nslist:
163 d771dffc Shikhar Bhushan
        for qname in multiqualify(tag):
164 d771dffc Shikhar Bhushan
            found = ele.find(qname)
165 d771dffc Shikhar Bhushan
            if found is not None:
166 614a698d Shikhar Bhushan
                return found
167 614a698d Shikhar Bhushan
    else:
168 614a698d Shikhar Bhushan
        return ele.find(tag)
169 d771dffc Shikhar Bhushan
170 d771dffc Shikhar Bhushan
def parse_root(raw):
171 614a698d Shikhar Bhushan
    """Efficiently parses the root element of an XML document.
172 4f650d54 Shikhar Bhushan

173 614a698d Shikhar Bhushan
    :type raw: string
174 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.
175 0cdb8b3c Shikhar Bhushan
    """
176 d771dffc Shikhar Bhushan
    fp = StringIO(raw[:1024]) # this is a guess but start element beyond 1024 bytes would be a bit absurd
177 d771dffc Shikhar Bhushan
    for event, element in ET.iterparse(fp, events=('start',)):
178 d771dffc Shikhar Bhushan
        return (element.tag, element.attrib)
179 d771dffc Shikhar Bhushan
180 614a698d Shikhar Bhushan
def validated_element(rep, tag=None, attrs=None, text=None):
181 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`.
182 4f650d54 Shikhar Bhushan

183 614a698d Shikhar Bhushan
    :arg tag: tag name or a list of allowable tag names
184 614a698d Shikhar Bhushan
    :arg attrs: list of required attribute names, each item may be a list of allowable alternatives
185 614a698d Shikhar Bhushan
    :arg text: textual content to match
186 614a698d Shikhar Bhushan
    :type rep: :obj:`dict` or :obj:`string` or :class:`~xml.etree.ElementTree.Element`
187 0cdb8b3c Shikhar Bhushan
    """
188 d771dffc Shikhar Bhushan
    ele = dtree2ele(rep)
189 614a698d Shikhar Bhushan
    err = False
190 614a698d Shikhar Bhushan
    if tag:
191 614a698d Shikhar Bhushan
        if isinstance(tag, basestring): tag = [tag]
192 614a698d Shikhar Bhushan
        if ele.tag not in tags:
193 614a698d Shikhar Bhushan
            err = True
194 614a698d Shikhar Bhushan
    if attrs:
195 d771dffc Shikhar Bhushan
        for req in attrs:
196 614a698d Shikhar Bhushan
            if isinstance(req, basestring): req = [req]
197 614a698d Shikhar Bhushan
            for alt in req:
198 614a698d Shikhar Bhushan
                if alt in ele.attrib:
199 d771dffc Shikhar Bhushan
                    break
200 d771dffc Shikhar Bhushan
            else:
201 614a698d Shikhar Bhushan
                err = True
202 614a698d Shikhar Bhushan
    if text and ele.text != text:
203 614a698d Shikhar Bhushan
        err = True
204 614a698d Shikhar Bhushan
    if err:
205 614a698d Shikhar Bhushan
        raise ContentError("Element [%s] does not meet requirements" % ele.tag)
206 d771dffc Shikhar Bhushan
    return ele