Statistics
| Branch: | Tag: | Revision:

root / ncclient / session / session.py @ 8b4b9936

History | View | Annotate | Download (4.9 kB)

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
import logging
16
from threading import Thread, Lock, Event
17
from Queue import Queue
18

    
19
from capabilities import Capabilities, CAPABILITIES
20

    
21
logger = logging.getLogger('ncclient.session')
22

    
23
class SessionError(Exception):
24
    
25
    pass
26

    
27
class SessionCloseError(SessionError):
28
    
29
    def __init__(self, in_buf, out_buf=None):
30
        SessionError.__init__(self)
31
        self._in_buf, self._out_buf = in_buf, out_buf
32
        
33
    def __str__(self):
34
        msg = 'Session closed by remote endpoint.'
35
        if self._in_buf:
36
            msg += '\nIN_BUFFER: %s' % self._in_buf
37
        if self._out_buf:
38
            msg += '\nOUT_BUFFER: %s' % self._out_buf
39
        return msg
40
    
41
class Session(Thread):
42
    
43
    def __init__(self):
44
        Thread.__init__(self, name='session')
45
        self._client_capabilities = CAPABILITIES
46
        self._server_capabilities = None # yet
47
        self._id = None # session-id
48
        self._q = Queue()
49
        self._connected = False # to be set/cleared by subclass implementation
50
        self._listeners = set([])
51
        self._lock = Lock()
52
    
53
    def _post_connect(self):
54
        from ncclient.content.builders import HelloBuilder
55
        # queue client's hello message for sending
56
        self.send(HelloBuilder.build(self._client_capabilities))
57
        
58
        error = None
59
        proceed = Event()
60
        def ok_cb(id, capabilities):
61
            self._id, self._capabilities = id, Capabilities(capabilities)
62
            proceed.set()
63
        def err_cb(err):
64
            error = err
65
            proceed.set()
66
        listener = HelloListener(ok_cb, err_cb)
67
        self.add_listener(listener)
68
        
69
        # start the subclass' main loop
70
        self.start()        
71
        # we expect server's hello message
72
        proceed.wait()
73
        # received hello message or an error happened
74
        self.remove_listener(listener)
75
        if error:
76
            self._close()
77
            raise self._error
78
    
79
    def send(self, message):
80
        logger.debug('queueing message: \n%s' % message)
81
        self._q.put(message)
82
    
83
    def connect(self):
84
        raise NotImplementedError
85

    
86
    def run(self):
87
        raise NotImplementedError
88
        
89
    def capabilities(self, whose='client'):
90
        if whose == 'client':
91
            return self._client_capabilities
92
        elif whose == 'server':
93
            return self._server_capabilities
94
    
95
    ### Session is a subject for arbitary listeners
96
    
97
    def has_listener(self, listener):
98
        with self._lock:
99
            return (listener in self._listeners)
100
    
101
    def add_listener(self, listener):
102
        with self._lock:
103
            self._listeners.add(listener)
104
    
105
    def remove_listener(self, listener):
106
        with self._lock:
107
            self._listeners.discard(listener)
108
    
109
    def dispatch(self, event, *args, **kwds):
110
        # holding the lock while doing callbacks could lead to a deadlock
111
        # if one of the above methods is called
112
        with self._lock:
113
            listeners = list(self._listeners)
114
        for l in listeners:
115
            try:
116
                logger.debug('dispatching [%s] to [%s]' % (event, l))
117
                getattr(l, event)(*args, **kwds)
118
            except Exception as e:
119
                logger.warning(e)
120
    
121
    ### Properties
122
    
123
    @property
124
    def client_capabilities(self):
125
        return self._client_capabilities
126
    
127
    @property
128
    def server_capabilities(self):
129
        return self._server_capabilities
130
    
131
    @property
132
    def connected(self):
133
        return self._connected
134
    
135
    @property
136
    def id(self):
137
        return self._id
138

    
139

    
140
class HelloListener:
141
    
142
    def __init__(self, init_cb, error_cb):
143
        self._init_cb, self._error_cb = init_cb, error_cb
144
    
145
    def __str__(self):
146
        return 'HelloListener'
147
    
148
    ### Events
149
    
150
    def reply(self, raw):
151
        from ncclient.content.parsers import HelloParser
152
        try:
153
            id, capabilities = HelloParser.parse(raw)
154
        except Exception as e:
155
            self._error_cb(e)
156
        else:
157
            self._init_cb(id, capabilities)
158
    
159
    def error(self, err):
160
        self._error_cb(err)
161

    
162

    
163
class DebugListener:
164
    
165
    def __str__(self):
166
        return 'DebugListener'
167
    
168
    def reply(self, raw):
169
        logger.debug('DebugListener:reply:%s' % raw)
170
    
171
    def error(self, err):
172
        logger.debug('DebugListener:error:%s' % err)