Statistics
| Branch: | Tag: | Revision:

root / ncclient / content.py @ 6ab782f7

History | View | Annotate | Download (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 94dd4342 Shikhar Bhushan
"TODO: docstring"
16 94dd4342 Shikhar Bhushan
17 94dd4342 Shikhar Bhushan
from xml.etree import cElementTree as ET
18 94dd4342 Shikhar Bhushan
19 ebf2bbc6 Shikhar Bhushan
from ncclient import NCClientError
20 ebf2bbc6 Shikhar Bhushan
21 a14c36f9 Shikhar Bhushan
class ContentError(NCClientError):
22 a14c36f9 Shikhar Bhushan
    pass
23 bf31e33e Shikhar Bhushan
24 6bd810f9 Shikhar Bhushan
### Namespace-related ###
25 94dd4342 Shikhar Bhushan
26 94dd4342 Shikhar Bhushan
BASE_NS = 'urn:ietf:params:xml:ns:netconf:base:1.0'
27 1fca349b Shikhar Bhushan
# and this is BASE_NS according to cisco devices...
28 94dd4342 Shikhar Bhushan
CISCO_BS = 'urn:ietf:params:netconf:base:1.0'
29 94dd4342 Shikhar Bhushan
30 94dd4342 Shikhar Bhushan
try:
31 94dd4342 Shikhar Bhushan
    register_namespace = ET.register_namespace
32 94dd4342 Shikhar Bhushan
except AttributeError:
33 94dd4342 Shikhar Bhushan
    def register_namespace(prefix, uri):
34 94dd4342 Shikhar Bhushan
        from xml.etree import ElementTree
35 94dd4342 Shikhar Bhushan
        # cElementTree uses ElementTree's _namespace_map, so that's ok
36 94dd4342 Shikhar Bhushan
        ElementTree._namespace_map[uri] = prefix
37 94dd4342 Shikhar Bhushan
38 e91a5349 Shikhar Bhushan
# we'd like BASE_NS to be prefixed as "netconf"
39 94dd4342 Shikhar Bhushan
register_namespace('netconf', BASE_NS)
40 94dd4342 Shikhar Bhushan
41 bf31e33e Shikhar Bhushan
qualify = lambda tag, ns=BASE_NS: '{%s}%s' % (ns, tag)
42 bf31e33e Shikhar Bhushan
43 bf31e33e Shikhar Bhushan
# i would have written a def if lambdas weren't so much fun
44 b4e9d4c2 Shikhar Bhushan
multiqualify = lambda tag, nslist=(BASE_NS, CISCO_BS): [qualify(tag, ns)
45 b4e9d4c2 Shikhar Bhushan
                                                        for ns in nslist]
46 94dd4342 Shikhar Bhushan
47 94dd4342 Shikhar Bhushan
unqualify = lambda tag: tag[tag.rfind('}')+1:]
48 94dd4342 Shikhar Bhushan
49 a14c36f9 Shikhar Bhushan
### XML with Python data structures
50 a14c36f9 Shikhar Bhushan
51 a14c36f9 Shikhar Bhushan
def to_element(spec):
52 a14c36f9 Shikhar Bhushan
    "TODO: docstring"
53 a14c36f9 Shikhar Bhushan
    if iselement(spec):
54 a14c36f9 Shikhar Bhushan
        return spec
55 a14c36f9 Shikhar Bhushan
    elif isinstance(spec, basestring):
56 a14c36f9 Shikhar Bhushan
        return ET.XML(spec)
57 a14c36f9 Shikhar Bhushan
    if not isinstance(spec, dict):
58 a14c36f9 Shikhar Bhushan
        raise ContentError("Invalid tree spec")
59 a14c36f9 Shikhar Bhushan
    if 'tag' in spec:
60 a14c36f9 Shikhar Bhushan
        ele = ET.Element(spec.get('tag'), spec.get('attributes', {}))
61 a14c36f9 Shikhar Bhushan
        ele.text = spec.get('text', '')
62 a14c36f9 Shikhar Bhushan
        ele.tail = spec.get('tail', '')
63 a14c36f9 Shikhar Bhushan
        subtree = spec.get('subtree', [])
64 a14c36f9 Shikhar Bhushan
        # might not be properly specified as list but may be dict
65 a14c36f9 Shikhar Bhushan
        if isinstance(subtree, dict):
66 a14c36f9 Shikhar Bhushan
            subtree = [subtree]
67 a14c36f9 Shikhar Bhushan
        for subele in subtree:
68 a14c36f9 Shikhar Bhushan
            ele.append(XMLConverter.build(subele))
69 a14c36f9 Shikhar Bhushan
        return ele
70 a14c36f9 Shikhar Bhushan
    elif 'comment' in spec:
71 a14c36f9 Shikhar Bhushan
        return ET.Comment(spec.get('comment'))
72 a14c36f9 Shikhar Bhushan
    else:
73 a14c36f9 Shikhar Bhushan
        raise ContentError('Invalid tree spec')
74 a14c36f9 Shikhar Bhushan
75 a14c36f9 Shikhar Bhushan
def from_xml(xml):
76 a14c36f9 Shikhar Bhushan
    return ET.fromstring(xml)
77 a14c36f9 Shikhar Bhushan
78 a14c36f9 Shikhar Bhushan
def to_xml(repr, encoding='utf-8'):
79 a14c36f9 Shikhar Bhushan
    "TODO: docstring"
80 a14c36f9 Shikhar Bhushan
    xml = ET.tostring(to_element(repr), encoding)
81 a14c36f9 Shikhar Bhushan
    # some etree versions don't include xml decl with utf-8
82 a14c36f9 Shikhar Bhushan
    # this is a problem with some devices
83 a14c36f9 Shikhar Bhushan
    return (xml if xml.startswith('<?xml')
84 a14c36f9 Shikhar Bhushan
            else '<?xml version="1.0" encoding="%s"?>%s' % (encoding, xml))
85 a14c36f9 Shikhar Bhushan
86 a14c36f9 Shikhar Bhushan
87 a14c36f9 Shikhar Bhushan
## Utility functions
88 a14c36f9 Shikhar Bhushan
89 a14c36f9 Shikhar Bhushan
iselement = ET.iselement
90 a14c36f9 Shikhar Bhushan
91 a14c36f9 Shikhar Bhushan
def namespaced_find(ele, tag, strict=False):
92 a14c36f9 Shikhar Bhushan
    """In strict mode, doesn't work around Cisco implementations sending incorrectly
93 a14c36f9 Shikhar Bhushan
    namespaced XML. Supply qualified name if using strict mode.
94 c2a5b930 Shikhar Bhushan
    """
95 c2a5b930 Shikhar Bhushan
    found = None
96 a14c36f9 Shikhar Bhushan
    if strict:
97 c2a5b930 Shikhar Bhushan
        found = ele.find(tag)
98 c2a5b930 Shikhar Bhushan
    else:
99 c2a5b930 Shikhar Bhushan
        for qname in multiqualify(tag):
100 c2a5b930 Shikhar Bhushan
            found = ele.find(qname)
101 c2a5b930 Shikhar Bhushan
            if found is not None:
102 c2a5b930 Shikhar Bhushan
                break
103 c2a5b930 Shikhar Bhushan
    return found
104 cc9af1c3 Shikhar Bhushan
105 a14c36f9 Shikhar Bhushan
def parse_root(raw):
106 a14c36f9 Shikhar Bhushan
    '''Internal use.
107 a14c36f9 Shikhar Bhushan
    Parse the top-level element from XML string.
108 179b00d4 Shikhar Bhushan
    
109 a14c36f9 Shikhar Bhushan
    Returns a `(tag, attributes)` tuple, where `tag` is a string representing
110 a14c36f9 Shikhar Bhushan
    the qualified name of the root element and `attributes` is an
111 a14c36f9 Shikhar Bhushan
    `{attribute: value}` dictionary.
112 a14c36f9 Shikhar Bhushan
    '''
113 a14c36f9 Shikhar Bhushan
    fp = StringIO(raw[:1024]) # this is a guess but start element beyond 1024 bytes would be a bit absurd
114 a14c36f9 Shikhar Bhushan
    for event, element in ET.iterparse(fp, events=('start',)):
115 a14c36f9 Shikhar Bhushan
        return (element.tag, element.attrib)
116 a14c36f9 Shikhar Bhushan
117 a14c36f9 Shikhar Bhushan
def root_ensured(rep, req_tag, req_attrs=None):
118 a14c36f9 Shikhar Bhushan
    rep = to_element(rep)
119 a14c36f9 Shikhar Bhushan
    if rep.tag not in (req_tag, qualify(req_tag)):
120 ebf2bbc6 Shikhar Bhushan
        raise ContentError("Required root element [%s] not found" % req_tag)
121 ebf2bbc6 Shikhar Bhushan
    if req_attrs is not None:
122 ebf2bbc6 Shikhar Bhushan
        pass # TODO
123 ebf2bbc6 Shikhar Bhushan
    return rep