Revision 2acc860a

b/ncclient/capabilities.py
25 25
    def __contains__(self, key):
26 26
        return ( key in self._dict ) or ( key in self._dict.values() )
27 27
    
28
    def __iter__(self):
29
        return self._dict.keys().__iter__()
30
    
28 31
    def __repr__(self):
29
        return self.to_xml()
32
        return repr(self._dict.keys())
30 33
    
31 34
    def add(self, uri, shorthand=None):
32 35
        if shorthand is None:
......
44 47
                    del self._dict[uri]
45 48
                    break
46 49
    
47
    def to_xml(self):
48
        elems = ['<capability>%s</capability>' % uri for uri in self._dict]
49
        return ('<capabilities>%s</capabilities>' % ''.join(elems))
50
    
51 50
    @staticmethod
52 51
    def guess_shorthand(uri):
53 52
        if uri.startswith('urn:ietf:params:netconf:capability:'):
......
69 68
    ])
70 69

  
71 70
if __name__ == "__main__":
72
    assert(':validate' in CAPABILITIES) # test __contains__
73
    print CAPABILITIES # test __repr__
71
    assert(':validate' in CAPABILITIES) # test __contains__
b/ncclient/content.py
14 14

  
15 15
import logging
16 16
from xml.etree import cElementTree as ElementTree
17
from cStringIO import StringIO
17 18

  
18 19
logger = logging.getLogger('ncclient.content')
19 20

  
20
BASE_NS = 'urn:ietf:params:xml:ns:netconf:base:1.0'
21
NOTIFICATION_NS = 'urn:ietf:params:xml:ns:netconf:notification:1.0'
22

  
23
def qualify(tag, ns=BASE_NS):
24
    return '{%s}%s' % (ns, tag)
25 21

  
22
def qualify(tag, ns=None):
23
    if ns is None:
24
        return tag
25
    else:
26
        return '{%s}%s' % (ns, tag)
26 27
_ = qualify
27 28

  
28
def make_hello(capabilities):
29
    return '<hello xmlns="%s">%s</hello>' % (BASE_NS, capabilities)
30

  
31
def make_rpc(id, op):
32
    return '<rpc message-id="%s" xmlns="%s">%s</rpc>' % (id, BASE_NS, op)
33

  
34
def parse_hello(raw):
35
    from capabilities import Capabilities
36
    id, capabilities = 0, Capabilities()
37
    root = ElementTree.fromstring(raw)
38
    if root.tag == _('hello'):
39
        for child in root.getchildren():
40
            if child.tag == _('session-id'):
41
                id = int(child.text)
42
            elif child.tag == _('capabilities'):
43
                for cap in child.getiterator(_('capability')):
44
                    capabilities.add(cap.text)
45
    return id, capabilities
46

  
47
def parse_message_root(raw):
48
    from cStringIO import StringIO
49
    fp = StringIO(raw)
50
    for event, element in ElementTree.iterparse(fp, events=('start',)):
51
        if element.tag == _('rpc'):
52
            return element.attrib['message-id']
53
        elif element.tag == _('notification', NOTIFICATION_NS):
54
            return 'notification'
29

  
30
class RootElementParser:
31
    
32
    '''Parse the root element of an XML document. The tag and namespace of
33
    recognized elements, and attributes of interest can be customized.
34
    
35
    RootElementParser does not parse any sub-elements.
36
    '''
37
    
38
    def __init__(self, recognize=[]):
39
        self._recognize = recognize
40
    
41
    def recognize(self, element):
42
        '''Specify an element that should be successfully parsed.
43
        
44
        element should be a string that represents a qualified name of the form
45
        *{namespace}tag*.
46
        '''
47
        self._recognize.append((element, attrs))
48
    
49
    def parse(self, raw):
50
        '''Parse the root element from a string representing an XML document.
51
        
52
        Returns a (tag, attributes) tuple. tag is a string representing
53
        the qualified name of the recognized element. attributes is a
54
        {'attr': value} dictionary.
55
        '''
56
        fp = StringIO(raw)
57
        for event, element in ElementTree.iterparse(fp, events=('start',)):
58
            for e in self._recognize:
59
                if element.tag == e:
60
                    return (element.tag, element.attrib)
61
            break
62
        return None
63

  
64

  
65
###########
66

  
67
class XMLBuilder:
68
    
69
    @staticmethod
70
    def _element(node):
71
        element = ElementTree.Element( _(node.get('tag'),
72
                                         node.get('namespace', None)),
73
                                      node.get('attributes', {}))
74
        if node.has_key('children'):
75
            for child in node['children']:
76
                element.append(_make_element(child))
55 77
        else:
56
            return None
78
            return element
79
    
80
    @staticmethod
81
    def _etree(tree_dict):
82
        return ElementTree.ElementTree(XMLBuilder._element(tree_dict))
83
    
84
    @staticmethod
85
    def to_xml(tree_dict, encoding='utf-8'):
86
        fp = StringIO()
87
        self._etree(tree_dict).write(fp, encoding)
88
        return fp.get_value()
89

  
90

  
91
### Hello exchange
92

  
93
class Hello:
94
    
95
    NS = 'urn:ietf:params:xml:ns:netconf:base:1.0'
96
    
97
    @staticmethod
98
    def build(capabilities, encoding='utf-8'):
99
        hello = ElementTree.Element(_('hello', Hello.NS))
100
        caps = ElementTree.Element('capabilities')
101
        for uri in capabilities:
102
            cap = ElementTree.Element('capability')
103
            cap.text = uri
104
            caps.append(cap)
105
        hello.append(caps)
106
        tree = ElementTree.ElementTree(hello)
107
        fp = StringIO()
108
        tree.write(fp, encoding)
109
        return fp.getvalue()
110
    
111
    @staticmethod
112
    def parse(raw):
113
        'Returns tuple of (session-id, ["capability_uri", ...])'
114
        id, capabilities = 0, []
115
        root = ElementTree.fromstring(raw)
116
        if root.tag == _('hello', Hello.NS):
117
            for child in root.getchildren():
118
                if child.tag == _('session-id', Hello.NS):
119
                    id = int(child.text)
120
                elif child.tag == _('capabilities', Hello.NS):
121
                    for cap in child.getiterator(_('capability', Hello.NS)):
122
                        capabilities.append(cap.text)
123
        return id, capabilities
b/ncclient/listeners.py
32 32
    def __init__(self):
33 33
        self._id2rpc = WeakValueDictionary()
34 34
        self._expecting_close = False
35
        self._subscription = None
35
        sself._notification_rpc_id = None
36 36
    
37 37
    def __str__(self):
38 38
        return 'SessionListener'
39 39
    
40
    def set_subscription(self, id):   
41
        self._subscription = id
40
    
42 41
    
43 42
    def expect_close(self):
44 43
        self._expecting_close = True
......
68 67
            if not self._expecting_close:
69 68
                raise err
70 69

  
70

  
71
class HelloListener:
72
    
73
    def __str__(self):
74
        return 'HelloListener'
75
        
76
    def __init__(self, session):
77
        self._session = session
78
    
79
    def reply(self, data):
80
        try:
81
            id, capabilities = content.Hello.parse(data)
82
            logger.debug('HelloListener: session_id: %s; capabilities: %s', id, capabilities)
83
            self._session.initialize(id, capabilities)
84
        except Exception as e:
85
            self._session.initialize_error(e)
86
    
87
    def error(self, err):
88
        self._session.initialize_error(err)
89

  
90

  
71 91
class DebugListener:
72 92
    
73 93
    def __str__(self):
......
77 97
        logger.debug('DebugListener:reply:\n%s' % raw)
78 98
    
79 99
    def error(self, err):
80
        logger.debug('DebugListener:error:\n%s' % err)
100
        logger.debug('DebugListener:error:\n%s' % err)
b/ncclient/operations/__init__.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 ncclient import content as content
16
from ncclient.capabilities import CAPABILITIES
17

  
18
from retrieve import Get, GetConfig
19
from edit import EditConfig, DeleteConfig
20
from session import CloseSession, KillSession
21
from lock import Lock, Unlock
22
from notification import CreateSubscription
23

  
24
__all__ = [
25
    'Get',
26
    'GetConfig',
27
    'EditConfig',
28
    'DeleteConfig',
29
    'Lock',
30
    'Unlock',
31
    'CloseSession',
32
    'KillSession',
33
    'CreateSubscription',
34
    ]
b/ncclient/rpc.py
20 20

  
21 21
class RPC:
22 22
    
23
    metadata = {
24
        'tag': 'rpc',
25
        'xmlns': 'urn:ietf:params:xml:ns:netconf:base:1.0',
26
        }
27
    
23 28
    def __init__(self, session, async=False, parse=True):
24 29
        self._session = session
25 30
        self._async = async
......
36 41
    
37 42
    def _do_request(self, op):
38 43
        self._session.send(content.make_rpc(self._id, op))
44
        # content.make(RPC, attrs={'message-id': self._id}, children=(op,))
39 45
        if not self._async:
40 46
            self._reply_event.wait()
41 47
        return self._reply
......
66 72
    def session(self):
67 73
        return self._session
68 74

  
69

  
70 75
class RPCReply:
71
    pass
72

  
73
class RPCError:
74
    pass
76
    
77
    class RPCError:
78
        
79
        pass
b/ncclient/session.py
17 17
from Queue import Queue
18 18

  
19 19
import content
20
from capabilities import CAPABILITIES
20
from capabilities import Capabilities, CAPABILITIES
21 21
from error import ClientError
22 22
from subject import Subject
23 23

  
......
29 29
    
30 30
    def __init__(self):
31 31
        Thread.__init__(self, name='session')
32
        Subject.__init__(self, listeners=[Session.HelloListener(self)])
32
        Subject.__init__(self, listeners=[HelloListener(self)])
33 33
        self._client_capabilities = CAPABILITIES
34 34
        self._server_capabilities = None # yet
35 35
        self._id = None # session-id
......
42 42
        # start the subclass' main loop
43 43
        self.start()
44 44
        # queue client's hello message for sending
45
        self.send(content.make_hello(self._client_capabilities))
45
        self.send(content.Hello.build(self._client_capabilities))
46 46
        # we expect server's hello message, wait for _init_event to be set by HelloListener
47 47
        self._init_event.wait()
48 48
        # there may have been an error
......
50 50
            self._close()
51 51
            raise self._error
52 52
    
53
    def initialize(self, id, capabilities):
54
        self._id, self._capabilities = id, Capabilities(capabilities)
55
        self._init_event.set()
56
    
57
    def initialize_error(self, err):
58
        self._error = err
59
        self._init_event.set()
60
    
53 61
    def send(self, message):
54
        message = (u'<?xml version="1.0" encoding="UTF-8"?>%s' %
55
                   message).encode('utf-8')
56 62
        logger.debug('queueing message: \n%s' % message)
57 63
        self._q.put(message)
58 64
    
......
61 67

  
62 68
    def run(self):
63 69
        raise NotImplementedError
70
        
71
    def capabilities(self, whose='client'):
72
        if whose == 'client':
73
            return self._client_capabilities
74
        elif whose == 'server':
75
            return self._server_capabilities
64 76
    
65 77
    ### Properties
66

  
78
    
67 79
    @property
68 80
    def client_capabilities(self):
69 81
        return self._client_capabilities
......
79 91
    @property
80 92
    def id(self):
81 93
        return self._id
82
    
83
    class HelloListener:
84
        
85
        def __str__(self):
86
            return 'HelloListener'
87
        
88
        def __init__(self, session):
89
            self._session = session
90
        
91
        def _done(self, err=None):
92
            if err is not None:
93
                self._session._error = err
94
            self._session.remove_listener(self)
95
            self._session._init_event.set()
96
        
97
        def reply(self, data):
98
            err = None
99
            try:
100
                id, capabilities = content.parse_hello(data)
101
                logger.debug('session_id: %s | capabilities: \n%s', id, capabilities)
102
                self._session._id, self._session.capabilities = id, capabilities
103
            except Exception as e:
104
                err = e
105
            finally:
106
                self._done(err)
107
        
108
        def error(self, err):
109
            self._done(err)
b/ncclient/ssh.py
15 15
import logging
16 16
from cStringIO import StringIO
17 17
from os import SEEK_CUR
18
import socket
18 19

  
19 20
import paramiko
20 21

  
22

  
21 23
from session import Session, SessionError
22 24

  
23 25
logger = logging.getLogger('ncclient.ssh')
......
39 41
    MSG_DELIM = ']]>]]>'
40 42
    
41 43
    def __init__(self, load_known_hosts=True,
42
                 missing_host_key_policy=paramiko.RejectPolicy):
44
                 missing_host_key_policy=paramiko.RejectPolicy()):
43 45
        Session.__init__(self)
44 46
        self._client = paramiko.SSHClient()
45 47
        self._channel = None
......
93 95
        self._parsing_state = state
94 96
        self._parsing_pos = self._in_buf.tell()
95 97

  
96
    def load_host_keys(self, filename):
97
        self._client.load_host_keys(filename)
98
    
99
    def set_missing_host_key_policy(self, policy):
100
        self._client.set_missing_host_key_policy(policy)
101
    
102
    # paramiko exceptions ok?
103
    # user might be looking for ClientError
98
    #def load_host_keys(self, filename):
99
    #    self._client.load_host_keys(filename)
100
    #
101
    #def set_missing_host_key_policy(self, policy):
102
    #    self._client.set_missing_host_key_policy(policy)
103
    #
104
    #def connect(self, hostname, port=830, username=None, password=None,
105
    #            key_filename=None, timeout=None, allow_agent=True,
106
    #            look_for_keys=True):
107
    #    self._client.connect(hostname, port=port, username=username,
108
    #                        password=password, key_filename=key_filename,
109
    #                        timeout=timeout, allow_agent=allow_agent,
110
    #                        look_for_keys=look_for_keys)    
111
    #    transport = self._client.get_transport()
112
    #    self._channel = transport.open_session()
113
    #    self._channel.invoke_subsystem('netconf')
114
    #    self._channel.set_name('netconf')
115
    #    self._connected = True
116
    #    self._post_connect()
117

  
104 118
    def connect(self, hostname, port=830, username=None, password=None,
105 119
                key_filename=None, timeout=None, allow_agent=True,
106 120
                look_for_keys=True):
107
        self._client.connect(hostname, port=port, username=username,
108
                            password=password, key_filename=key_filename,
109
                            timeout=timeout, allow_agent=allow_agent,
110
                            look_for_keys=look_for_keys)    
111
        transport = self._client.get_transport()
112
        self._channel = transport.open_session()
113
        self._channel.invoke_subsystem('netconf')
114
        self._channel.set_name('netconf')
115
        self._connected = True
116
        self._post_connect()
121
        self._transport = paramiko.Transport()
117 122
    
118 123
    def run(self):
119 124
        chan = self._channel

Also available in: Unified diff