Revision 45fef9ac

b/src/__init__.py
14 14

  
15 15
__version__ = "0.01"
16 16

  
17
class NETCONFClientError(Exception): pass
17

  
18
class ncclientError(Exception): pass
19

  
20
class NETCONFError(ncclientError): pass
21

  
22
# Decorators
23

  
24
def override(func): func
b/src/content/__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 .. import ncclientError, NETCONFError
16

  
17
class ContentError(ncclientError): pass
18

  
b/src/listener.py
20 20
    def has_listener(self, listener):
21 21
        return (listener in self._listeners)
22 22
    
23
    def add_listner(self, listener):
23
    def add_listener(self, listener):
24 24
            self._listeners.append(listener)
25 25

  
26 26
    def remove_listener(self, listener):
b/src/session.py
12 12
# See the License for the specific language governing permissions and
13 13
# limitations under the License.
14 14

  
15
import threading
15
from content import Creator, Parser
16 16

  
17
class SessionError(NETCONFClientError): pass
17
from threading import Thread
18
from listener import Subject, Listener
18 19

  
19
class Session(threading.Thread):
20
    
21
    def __init__(self, capabilities, reply_cb):
22
        Thread.__init__(self)
23
        self.capabilities = {
24
            'client': capabilities,
25
            'server': None #yet
26
            }
27
        self._q = Queue.Queue()
28
        self._cb = reply_cb
20
class SessionError(ncclientError): pass
21

  
22
class Session(Thread, Subject, Listener):
23
    
24
    def __init__(self, capabilities=None):
25
        Thread.__init__(self, name='session')
26
        Subject.__init__(self, listeners=[self])
27
        Thread.setDaemon(True)
28
        self.client_capabilities = capabilities
29
        self.server_capabilities = None # yet
29 30
        self.id = None # session-id
30
        self.connected = False
31
        self.is_connected = False
32
        self._q = Queue.Queue()
31 33
    
32 34
    def _make_hello(self):
33
        # <capabilities> should be repr(capabilities['client'])
34
        # rest, hmm..
35 35
        pass
36 36
    
37
    def _init(self, msg):
38
        # session-id = 
39
        # capabilities['server'] = 
40
        self.connected = True
37
    def _init(self, id, capabilities):
38
        self.id = id
39
        self.capabilities[SERVER] = capabilities
40
        self.is_connected = True
41
    
42
    @override
43
    def _close(self):
44
        raise NotImplementedError
41 45
    
42 46
    def connect(self):
43
        self.start()
47
        self._greet()
48
        Thread.start()
49
    
50
    def send(self, msg):
51
        if self.is_connected:
52
            self._q.add(msg)
53
        else:
54
            raise SessionError('Attempted to send message while not connected')
44 55
        
56
    ### Thread methods
57

  
58
    @override
45 59
    def run(self):
46 60
        raise NotImplementedError
47
        
48
    def send(self, msg):
49
        if self.connected:
50
            self._q.add(msg)
61
    
62
    ### Subject methods
63
    
64
    def add_listener(self, listener):
65
        if not self.is_connected:
66
            raise SessionError('Listeners may only be added after session initialisation')
51 67
        else:
52
            raise SessionError('''Attempted to send message before
53
                               NETCONF session initialisation''')
54
            
68
            Subject.add_listner(self, listener)
69
    
70
    ### Listener methods
71
    # these are relevant for the initial greeting only
72
    
73
    def reply(self, data):
74
        p = Parser(data)
75
        s = p['session']
76
        id = s['@id']
77
        capabilities = Capabilities()
78
        capabilities.fromXML(p['capabilities'])
79
        self._init(id, capabilities)
80
        self.remove_listener(self)
81
    
82
    def error(self, data):
83
        self._close()
84
        raise SSHError('Session initialization failed')
85
    
b/src/ssh.py
26 26
class SSHSession(Session):
27 27
    
28 28
    BUF_SIZE = 4096
29
    
30 29
    MSG_DELIM = ']]>>]]>'
30
    MSG_DELIM_LEN = len(MSG_DELIM)
31 31
    
32
    def __init__(self, capabilities, reply_cb=None,
33
                 load_known_hosts=True,
32
    def __init__(self, capabilities, load_known_hosts=True,
34 33
                 missing_host_key_policy=paramiko.RejectPolicy):
35 34
        Session.__init__(self, capabilities)
36 35
        self._inBuf = ''
......
38 37
        self._client = SSHClient()
39 38
        if load_known_hosts:
40 39
            self._client.load_system_host_keys()
41
        self._client.set_missing_host_key_policy(host_key_policy)
42
        
43
    def _greet_cb(self, reply):
44
        self._init(reply)
45
        self._cb = self._real_cb
46
        
47
    def _greet(self):
48
        self._real_cb = self._cb
49
        self._cb = self._greet_cb
50
        self._q.add(self._make_hello())
51
        
40
        self._client.set_missing_host_key_policy(missing_host_key_policy)
41
    
52 42
    def load_host_keys(self, filename):
53 43
        self._client.load_host_keys(filename)
54
        
44
    
55 45
    def set_missing_host_key_policy(self, policy):
56 46
        self._client.set_missing_host_key_policy(policy)
57
        
58
    def set_reply_callback(self, cb):
59
        self._cb = cb
60 47
    
61 48
    def connect(self, hostname, port=830, username=None, password=None,
62 49
                key_filename=None, timeout=None, allow_agent=True,
......
69 56
        self._channel = transport.open_session()
70 57
        self._channel.invoke_subsystem('netconf')
71 58
        self._greet()
72
        Session.connect(self) # starts thread
59
        self.start()
60

  
61
    def _close(self):
62
        self._channel.shutdown(2)
73 63
    
74 64
    def run(self):
75 65
        sock = self._channel
......
87 77
                data = sock.recv(BUF_SIZE)
88 78
                if data:
89 79
                    self._inBuf += data
90
                    # probably not very efficient:
91
                    pos = self._inBuf.find(DELIM)
92
                    if pos != -1:
93
                        msg = self._inBuf[:pos]
94
                        self._inBuf = self._inBuf[(pos + len(DELIM)):]
95
                        self._reply_cb(msg)
80
                    (before, _, after) = self._inBuf.partition(MSG_DELIM)
81
                    if after:
82
                         # we don't want this thread to ground to a halt
83
                         # because of an error dispatching one reply...
84
                        try: self.dispatch('reply', before)
85
                        except: pass
86
                        self._inBuf = after
96 87
                else:
97
                    connected = False
98
                    # it's not an error if we asked for it
99
                    # via CloseSession -- need a way to know this
100
                    raise SessionError
101
                    
88
                    self.dispatch('error', self._inBuf)
89

  
90

  
102 91
class CallbackPolicy(paramiko.MissingHostKeyPolicy):
103 92
    
104 93
    def __init__(self, cb):

Also available in: Unified diff