Revision 433ab53a

b/ncclient/content/__init__.py
12 12
# See the License for the specific language governing permissions and
13 13
# limitations under the License.
14 14

  
15
'This module serves as an XML abstraction layer'
15
"This module serves as an XML abstraction layer"
16 16

  
17 17
import logging
18 18
logger = logging.getLogger('ncclient.content')
b/ncclient/content/builder.py
1
# Copyright 2009 Shikhar Bhushan
2
#
3
# Licensed under the Apache License, Version 2.0 (the "License");
4
# you may not use this file except in compliance with the License.
5
# You may obtain a copy of the License at
6
#
7
#    http://www.apache.org/licenses/LICENSE-2.0
8
#
9
# Unless required by applicable law or agreed to in writing, software
10
# distributed under the License is distributed on an "AS IS" BASIS,
11
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
# See the License for the specific language governing permissions and
13
# limitations under the License.
14

  
15
"TODO: docstring"
16

  
17
from xml.etree import cElementTree as ET
18

  
19
from ns import BASE_NS
20

  
21
try:
22
    register_namespace = ET.register_namespace
23
except AttributeError:
24
    def register_namespace(prefix, uri):
25
        from xml.etree import ElementTree
26
        # cElementTree uses ElementTree's _namespace_map, so that's ok
27
        ElementTree._namespace_map[uri] = prefix
28

  
29
register_namespace('netconf', BASE_NS)
30

  
31
class TreeBuilder:
32
    """Build an ElementTree.Element instance from an XML tree specification
33
    based on nested dictionaries.
34
    """
35
    
36
    def __init__(self, spec):
37
        "TODO: docstring"
38
        self._root = TreeBuilder.build(spec)
39
        
40
    def to_string(self, encoding='utf-8'):
41
        "TODO: docstring"
42
        xml = ET.tostring(self._root, encoding)
43
        # some etree versions don't always include xml decl
44
        # this is a problem with some devices
45
        if not xml.startswith('<?xml'):
46
            return '<?xml version="1.0" encoding="%s"?>%s' % (encoding, xml)
47
        else:
48
            return xml
49
    
50
    @property
51
    def tree(self):
52
        "TODO: docstring"
53
        return self._root
54
    
55
    @classmethod
56
    def build(cls, spec):
57
        "TODO: docstring"
58
        if spec.has_key('tag'):
59
            ele = ET.Element(spec.get('tag'), spec.get('attributes', {}))
60
            ele.text = spec.get('text', '')
61
            children = spec.get('children', [])
62
            if isinstance(children, dict):
63
                children = [children]
64
            for child in children:
65
                ele.append(TreeBuilder.build(child))
66
            return ele
67
        elif spec.has_key('comment'):
68
            return ET.Comment(spec.get('comment'))
69
        else:
70
            raise ValueError('Invalid tree spec')
/dev/null
1
# Copyright 2009 Shikhar Bhushan
2
#
3
# Licensed under the Apache License, Version 2.0 (the "License");
4
# you may not use this file except in compliance with the License.
5
# You may obtain a copy of the License at
6
#
7
#    http://www.apache.org/licenses/LICENSE-2.0
8
#
9
# Unless required by applicable law or agreed to in writing, software
10
# distributed under the License is distributed on an "AS IS" BASIS,
11
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
# See the License for the specific language governing permissions and
13
# limitations under the License.
14

  
15
from xml.etree import cElementTree as ET
16

  
17
from common import BASE_NS
18
from common import qualify as _
19

  
20
try:
21
    register_namespace = ET.register_namespace
22
except AttributeError:
23
    def register_namespace(prefix, uri):
24
        from xml.etree import ElementTree
25
        # cElementTree uses ElementTree's _namespace_map
26
        ElementTree._namespace_map[uri] = prefix
27

  
28
register_namespace('netconf', BASE_NS)
29

  
30
class TreeBuilder:
31
    '''Build an ElementTree.Element instance from an XML tree specification
32
    based on nested dictionaries.
33
    '''
34
    
35
    def __init__(self, spec):
36
        self._root = TreeBuilder.build(spec)
37
        
38
    def to_string(self, encoding='utf-8'):
39
        # etree does not include the <?xml..?> processing instruction
40
        # if encoding is utf-8. this is a problem with cisco routers.
41
        prepend = '<?xml version="1.0" encoding="UTF-8"?>' if encoding=='utf-8' else ''
42
        return prepend + ET.tostring(self._root, encoding)
43
    
44
    @property
45
    def tree(self):
46
        return self._root
47
    
48
    @staticmethod
49
    def build(spec):
50
        'Returns constructed ElementTree.Element'
51
        if spec.has_key('tag'):
52
            ele = ET.Element(spec.get('tag'), spec.get('attributes', {}))
53
            ele.text = spec.get('text', '')
54
            children = spec.get('children', [])
55
            if isinstance(children, dict):
56
                children = [children]
57
            for child in children:
58
                ele.append(TreeBuilder.build(child))
59
            return ele
60
        elif spec.has_key('comment'):
61
            return ET.Comment(spec.get('comment'))
62
        else:
63
            raise ValueError('Invalid tree spec')
64

  
65

  
66
class HelloBuilder:
67
        
68
    @staticmethod
69
    def build(capabilities, encoding='utf-8'):
70
        children = [{'tag': 'capability', 'text': uri } for uri in capabilities]
71
        spec = {
72
            'tag': _('hello', BASE_NS),
73
            'children': [{
74
                        'tag': u'capabilities',
75
                        'children': children
76
                        }]
77
            }
78
        return TreeBuilder(spec).to_string(encoding)
79

  
80
class RPCBuilder:
81
    
82
    @staticmethod
83
    def build(msgid, op, encoding='utf-8'):
84
        if isinstance(op, basestring):
85
            return RPCBuilder.build_from_string(msgid, op, encoding)
86
        else:
87
            return RPCBuilder.build_from_spec(msgid, op, encoding)
88
    
89
    @staticmethod
90
    def build_from_spec(msgid, opspec, encoding='utf-8'):
91
        spec = {
92
            'tag': _('rpc', BASE_NS),
93
            'attributes': {'message-id': msgid},
94
            'children': opspec
95
            }
96
        return TreeBuilder(spec).to_string(encoding)
97
    
98
    @staticmethod
99
    def build_from_string(msgid, opstr, encoding='utf-8'):
100
        decl = '<?xml version="1.0" encoding="%s"?>' % encoding
101
        doc = (u'''<rpc message-id="%s" xmlns="%s">%s</rpc>''' %
102
               (msgid, BASE_NS, opstr)).encode(encoding)
103
        return (decl + doc)
/dev/null
1
# Copyright 2009 Shikhar Bhushan
2
#
3
# Licensed under the Apache License, Version 2.0 (the "License");
4
# you may not use this file except in compliance with the License.
5
# You may obtain a copy of the License at
6
#
7
#    http://www.apache.org/licenses/LICENSE-2.0
8
#
9
# Unless required by applicable law or agreed to in writing, software
10
# distributed under the License is distributed on an "AS IS" BASIS,
11
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
# See the License for the specific language governing permissions and
13
# limitations under the License.
14

  
15
BASE_NS = 'urn:ietf:params:xml:ns:netconf:base:1.0'
16
CISCO_BS = 'urn:ietf:params:netconf:base:1.0'
17

  
18
def qualify(tag, namespace=None):
19
    'Returns qualified name of form `{namespace}tag`'
20
    if namespace is not None:
21
        return '{%s}%s' % (namespace, tag)
22
    else:
23
        return tag
24

  
25
unqualify = lambda tag: tag[tag.rfind('}')+1:]
b/ncclient/content/ns.py
1
# Copyright 2009 Shikhar Bhushan
2
#
3
# Licensed under the Apache License, Version 2.0 (the "License");
4
# you may not use this file except in compliance with the License.
5
# You may obtain a copy of the License at
6
#
7
#    http://www.apache.org/licenses/LICENSE-2.0
8
#
9
# Unless required by applicable law or agreed to in writing, software
10
# distributed under the License is distributed on an "AS IS" BASIS,
11
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
# See the License for the specific language governing permissions and
13
# limitations under the License.
14

  
15
"Namespacing"
16

  
17
BASE_NS = 'urn:ietf:params:xml:ns:netconf:base:1.0'
18

  
19
# cisco devices return incorrectly-namespaced xml
20
CISCO_BS = 'urn:ietf:params:netconf:base:1.0'
21

  
22
qualify = lambda tag, ns: '{%s}%s' % (namespace, tag)
23

  
24
unqualify = lambda tag: tag[tag.rfind('}')+1:]
/dev/null
1
# Copyright 2009 Shikhar Bhushan
2
#
3
# Licensed under the Apache License, Version 2.0 (the "License");
4
# you may not use this file except in compliance with the License.
5
# You may obtain a copy of the License at
6
#
7
#    http://www.apache.org/licenses/LICENSE-2.0
8
#
9
# Unless required by applicable law or agreed to in writing, software
10
# distributed under the License is distributed on an "AS IS" BASIS,
11
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
# See the License for the specific language governing permissions and
13
# limitations under the License.
14

  
15
from xml.etree import cElementTree as ET
16
from cStringIO import StringIO
17

  
18
from common import BASE_NS
19
from common import qualify as _
20
from common import unqualify as __
21

  
22
class HelloParser:
23

  
24
    @staticmethod
25
    def parse(raw):
26
        'Returns tuple of (session-id, ["capability_uri", ...])'
27
        sid, capabilities = 0, []
28
        root = ET.fromstring(raw)
29
        # cisco's too posh to namespace its hello
30
        if __(root.tag) == 'hello':
31
            for child in root.getchildren():
32
                if __(child.tag) == 'session-id':
33
                    sid = child.text
34
                elif __(child.tag) == 'capabilities':
35
                    for cap in child.getiterator('capability'): 
36
                        capabilities.append(cap.text)
37
                    for cap in child.getiterator(_('capability', BASE_NS)):
38
                        capabilities.append(cap.text)
39
        return sid, capabilities
40

  
41

  
42
class RootParser:
43
    '''Parser for the top-level element of an XML document. Does not look at any
44
    sub-elements. It is useful for efficiently determining the type of a received
45
    message.
46
    '''
47
    
48
    @staticmethod
49
    def parse(raw, recognized=[]):
50
        '''Parse the top-level element from a string representing an XML document.
51
        
52
        recognized is a list of tag names that will be successfully parsed.
53
        The tag names should be qualified with namespace.
54
        
55
        Returns a `(tag, attributes)` tuple, where `tag` is a string representing
56
        the qualified name of the recognized element and `attributes` is an
57
        `{attribute: value}` dictionary.
58
        '''
59
        fp = StringIO(raw)
60
        for event, element in ET.iterparse(fp, events=('start',)):
61
            for ele in recognized:
62
                if element.tag == ele:
63
                    return (element.tag, element.attrib)
64
            break
65

  
66

  
67
class RPCReplyParser:
68
    
69
    @staticmethod
70
    def parse_rpc_error(root, ns):
71
        err_dict = {}
72
        for tag in ('error-type', 'error-tag', 'error-severity', 'error-app-tag',
73
                    'error-path', 'error-message', 'error-info'):
74
            ele = root.find(_(tag, ns))
75
            if ele is not None:
76
                err_dict[tag] = ele.text
77
        
78
    @staticmethod
79
    def parse(raw):
80
        oktags = (_('ok', BASE_NS), _('ok', CISCO_NS))
81
        root = ET.fromstring(raw)
82
        ok = root.find(_('ok', BASE_NS))
83
        if ok is None:
84
            ok = root.find(_('ok', CISCO_BS))
85
        ns = BASE_NS
86
        errs = root.findall(_('rpc-error', BASE_NS))
87
        if not errs:
88
            errs = root.findall(_('rpc-error', CISCO_BS))
89
            ns = CISCO_NS
90
        ret = [ (err, RPCReplyParser.parse_rpc_error(err, ns)) for err in errs ]
91
        return ok, ret
b/ncclient/content/root.py
1
# Copyright 2009 Shikhar Bhushan
2
#
3
# Licensed under the Apache License, Version 2.0 (the "License");
4
# you may not use this file except in compliance with the License.
5
# You may obtain a copy of the License at
6
#
7
#    http://www.apache.org/licenses/LICENSE-2.0
8
#
9
# Unless required by applicable law or agreed to in writing, software
10
# distributed under the License is distributed on an "AS IS" BASIS,
11
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
# See the License for the specific language governing permissions and
13
# limitations under the License.
14

  
15
from xml.etree import cElementTree as ET
16
from cStringIO import StringIO
17

  
18
def parse(raw, recognized=[]):
19
    '''Parse the top-level element from a string representing an XML document.
20
    
21
    recognized is a list of tag names that will be successfully parsed.
22
    The tag names should be qualified with namespace.
23
    
24
    Returns a `(tag, attributes)` tuple, where `tag` is a string representing
25
    the qualified name of the recognized element and `attributes` is an
26
    `{attribute: value}` dictionary.
27
    '''
28
    fp = StringIO(raw)
29
    for event, element in ET.iterparse(fp, events=('start',)):
30
        return (element.tag, element.attrib)

Also available in: Unified diff