Statistics
| Branch: | Tag: | Revision:

root / ncclient / xml_.py @ master

History | View | Annotate | Download (4.3 kB)

1
# Copyright 2009 Shikhar Bhushan
2
# Copyright 2011 Leonidas Poulopoulos
3
#
4
# Licensed under the Apache License, Version 2.0 (the "License");
5
# you may not use this file except in compliance with the License.
6
# You may obtain 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,
12
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
# See the License for the specific language governing permissions and
14
# limitations under the License.
15

    
16
"Methods for creating, parsing, and dealing with XML and ElementTree objects."
17

    
18
from cStringIO import StringIO
19
from xml.etree import cElementTree as ET
20

    
21
# In case issues come up with XML generation/parsing
22
# make sure you have the ElementTree v1.2.7+ lib
23

    
24
from ncclient import NCClientError
25

    
26
class XMLError(NCClientError): pass
27

    
28
### Namespace-related
29

    
30
#: Base NETCONF namespace
31
BASE_NS_1_0 = "urn:ietf:params:xml:ns:netconf:base:1.0"
32
#: Namespace for Tail-f core data model
33
TAILF_AAA_1_1 = "http://tail-f.com/ns/aaa/1.1"
34
#: Namespace for Tail-f execd data model
35
TAILF_EXECD_1_1 = "http://tail-f.com/ns/execd/1.1"
36
#: Namespace for Cisco data model
37
CISCO_CPI_1_0 = "http://www.cisco.com/cpi_10/schema"
38
#: Namespace for Flowmon data model
39
FLOWMON_1_0 = "http://www.liberouter.org/ns/netopeer/flowmon/1.0"
40
#: Namespace for Juniper 9.6R4. Tested with Junos 9.6R4+
41
JUNIPER_1_1 = "http://xml.juniper.net/xnm/1.1/xnm"
42
#
43
try:
44
    register_namespace = ET.register_namespace
45
except AttributeError:
46
    def register_namespace(prefix, uri):
47
        from xml.etree import ElementTree
48
        # cElementTree uses ElementTree's _namespace_map, so that's ok
49
        ElementTree._namespace_map[uri] = prefix
50
register_namespace.func_doc = "ElementTree's namespace map determines the prefixes for namespace URI's when serializing to XML. This method allows modifying this map to specify a prefix for a namespace URI."
51

    
52
for (ns, pre) in {
53
    BASE_NS_1_0: 'nc',
54
    TAILF_AAA_1_1: 'aaa',
55
    TAILF_EXECD_1_1: 'execd',
56
    CISCO_CPI_1_0: 'cpi',
57
    FLOWMON_1_0: 'fm',
58
    JUNIPER_1_1: 'junos',
59
}.items(): 
60
    register_namespace(pre, ns)
61

    
62
qualify = lambda tag, ns=BASE_NS_1_0: tag if ns is None else "{%s}%s" % (ns, tag)
63
"""Qualify a *tag* name with a *namespace*, in :mod:`~xml.etree.ElementTree` fashion i.e. *{namespace}tagname*."""
64

    
65
def to_xml(ele, encoding="UTF-8"):
66
    "Convert and return the XML for an *ele* (:class:`~xml.etree.ElementTree.Element`) with specified *encoding*."
67
    xml = ET.tostring(ele, encoding)
68
    return xml if xml.startswith('<?xml') else '<?xml version="1.0" encoding="%s"?>%s' % (encoding, xml)
69

    
70
def to_ele(x):
71
    "Convert and return the :class:`~xml.etree.ElementTree.Element` for the XML document *x*. If *x* is already an :class:`~xml.etree.ElementTree.Element` simply returns that."
72
    return x if ET.iselement(x) else ET.fromstring(x)
73

    
74
def parse_root(raw):
75
    "Efficiently parses the root element of a *raw* XML document, returning a tuple of its qualified name and attribute dictionary."
76
    fp = StringIO(raw)
77
    for event, element in ET.iterparse(fp, events=('start',)):
78
        return (element.tag, element.attrib)
79

    
80
def validated_element(x, tags=None, attrs=None):
81
    """Checks if the root element of an XML document or Element meets the supplied criteria.
82
    
83
    *tags* if specified is either a single allowable tag name or sequence of allowable alternatives
84

85
    *attrs* if specified is a sequence of required attributes, each of which may be a sequence of several allowable alternatives
86

87
    Raises :exc:`XMLError` if the requirements are not met.
88
    """
89
    ele = to_ele(x)
90
    if tags:
91
        if isinstance(tags, basestring):
92
            tags = [tags]
93
        if ele.tag not in tags:
94
            raise XMLError("Element [%s] does not meet requirement" % ele.tag)
95
    if attrs:
96
        for req in attrs:
97
            if isinstance(req, basestring): req = [req]
98
            for alt in req:
99
                if alt in ele.attrib:
100
                    break
101
            else:
102
                raise XMLError("Element [%s] does not have required attributes" % ele.tag)
103
    return ele
104

    
105
new_ele = lambda tag, attrs={}, **extra: ET.Element(qualify(tag), attrs, **extra)
106

    
107
sub_ele = lambda parent, tag, attrs={}, **extra: ET.SubElement(parent, qualify(tag), attrs, **extra)
108