Statistics
| Branch: | Tag: | Revision:

root / ncclient / transport / session.py @ 583c11f6

History | View | Annotate | Download (6.2 kB)

1 d095a59e Shikhar Bhushan
# Copyright 2009 Shikhar Bhushan
2 d095a59e Shikhar Bhushan
#
3 d095a59e Shikhar Bhushan
# Licensed under the Apache License, Version 2.0 (the "License");
4 d095a59e Shikhar Bhushan
# you may not use this file except in compliance with the License.
5 d095a59e Shikhar Bhushan
# You may obtain a copy of the License at
6 d095a59e Shikhar Bhushan
#
7 d095a59e Shikhar Bhushan
#    http://www.apache.org/licenses/LICENSE-2.0
8 d095a59e Shikhar Bhushan
#
9 d095a59e Shikhar Bhushan
# Unless required by applicable law or agreed to in writing, software
10 d095a59e Shikhar Bhushan
# distributed under the License is distributed on an "AS IS" BASIS,
11 d095a59e Shikhar Bhushan
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 d095a59e Shikhar Bhushan
# See the License for the specific language governing permissions and
13 d095a59e Shikhar Bhushan
# limitations under the License.
14 d095a59e Shikhar Bhushan
15 d6688264 Shikhar Bhushan
from Queue import Queue
16 4de03d63 Shikhar Bhushan
from threading import Thread, Lock, Event
17 e91a5349 Shikhar Bhushan
18 583c11f6 Shikhar Bhushan
from ncclient import content
19 2f8bc438 Shikhar Bhushan
from ncclient.capabilities import Capabilities
20 d095a59e Shikhar Bhushan
21 41e2ed46 Shikhar Bhushan
import logging
22 41e2ed46 Shikhar Bhushan
logger = logging.getLogger('ncclient.transport.session')
23 41e2ed46 Shikhar Bhushan
24 a14c36f9 Shikhar Bhushan
class Session(Thread):
25 d095a59e Shikhar Bhushan
    
26 2f8bc438 Shikhar Bhushan
    def __init__(self, capabilities):
27 a14c36f9 Shikhar Bhushan
        Thread.__init__(self)
28 583c11f6 Shikhar Bhushan
        self.set_daemon(True)
29 583c11f6 Shikhar Bhushan
        self._listeners = set() # 3.0's weakset ideal
30 a14c36f9 Shikhar Bhushan
        self._lock = Lock()
31 583c11f6 Shikhar Bhushan
        self.set_name('session')
32 d6688264 Shikhar Bhushan
        self._q = Queue()
33 2f8bc438 Shikhar Bhushan
        self._client_capabilities = capabilities
34 d095a59e Shikhar Bhushan
        self._server_capabilities = None # yet
35 d095a59e Shikhar Bhushan
        self._id = None # session-id
36 d095a59e Shikhar Bhushan
        self._connected = False # to be set/cleared by subclass implementation
37 41e2ed46 Shikhar Bhushan
        logger.debug('%r created: client_capabilities=%r' %
38 41e2ed46 Shikhar Bhushan
                     (self, self._client_capabilities))
39 d095a59e Shikhar Bhushan
    
40 a14c36f9 Shikhar Bhushan
    def _dispatch_message(self, raw):
41 a14c36f9 Shikhar Bhushan
        "TODO: docstring"
42 a14c36f9 Shikhar Bhushan
        try:
43 583c11f6 Shikhar Bhushan
            root = content.parse_root(raw)
44 a14c36f9 Shikhar Bhushan
        except Exception as e:
45 a14c36f9 Shikhar Bhushan
            logger.error('error parsing dispatch message: %s' % e)
46 a14c36f9 Shikhar Bhushan
            return
47 a14c36f9 Shikhar Bhushan
        with self._lock:
48 a14c36f9 Shikhar Bhushan
            listeners = list(self._listeners)
49 a14c36f9 Shikhar Bhushan
        for l in listeners:
50 a14c36f9 Shikhar Bhushan
            logger.debug('dispatching message to %r' % l)
51 a14c36f9 Shikhar Bhushan
            try:
52 a14c36f9 Shikhar Bhushan
                l.callback(root, raw)
53 a14c36f9 Shikhar Bhushan
            except Exception as e:
54 a14c36f9 Shikhar Bhushan
                logger.warning('[error] %r' % e)
55 a14c36f9 Shikhar Bhushan
    
56 a14c36f9 Shikhar Bhushan
    def _dispatch_error(self, err):
57 a14c36f9 Shikhar Bhushan
        "TODO: docstring"
58 a14c36f9 Shikhar Bhushan
        with self._lock:
59 a14c36f9 Shikhar Bhushan
            listeners = list(self._listeners)
60 a14c36f9 Shikhar Bhushan
        for l in listeners:
61 a14c36f9 Shikhar Bhushan
            logger.debug('dispatching error to %r' % l)
62 a14c36f9 Shikhar Bhushan
            try:
63 a14c36f9 Shikhar Bhushan
                l.errback(err)
64 a14c36f9 Shikhar Bhushan
            except Exception as e:
65 a14c36f9 Shikhar Bhushan
                logger.warning('error %r' % e)
66 a14c36f9 Shikhar Bhushan
    
67 65c6a607 Shikhar Bhushan
    def _post_connect(self):
68 65c6a607 Shikhar Bhushan
        "Greeting stuff"
69 65c6a607 Shikhar Bhushan
        init_event = Event()
70 65c6a607 Shikhar Bhushan
        error = [None] # so that err_cb can bind error[0]. just how it is.
71 65c6a607 Shikhar Bhushan
        # callbacks
72 65c6a607 Shikhar Bhushan
        def ok_cb(id, capabilities):
73 65c6a607 Shikhar Bhushan
            self._id = id
74 583c11f6 Shikhar Bhushan
            self._server_capabilities = capabilities
75 65c6a607 Shikhar Bhushan
            init_event.set()
76 65c6a607 Shikhar Bhushan
        def err_cb(err):
77 65c6a607 Shikhar Bhushan
            error[0] = err
78 65c6a607 Shikhar Bhushan
            init_event.set()
79 65c6a607 Shikhar Bhushan
        listener = HelloHandler(ok_cb, err_cb)
80 65c6a607 Shikhar Bhushan
        self.add_listener(listener)
81 65c6a607 Shikhar Bhushan
        self.send(HelloHandler.build(self._client_capabilities))
82 65c6a607 Shikhar Bhushan
        logger.debug('starting main loop')
83 65c6a607 Shikhar Bhushan
        self.start()
84 65c6a607 Shikhar Bhushan
        # we expect server's hello message
85 65c6a607 Shikhar Bhushan
        init_event.wait()
86 65c6a607 Shikhar Bhushan
        # received hello message or an error happened
87 65c6a607 Shikhar Bhushan
        self.remove_listener(listener)
88 65c6a607 Shikhar Bhushan
        if error[0]:
89 65c6a607 Shikhar Bhushan
            raise error[0]
90 65c6a607 Shikhar Bhushan
        logger.info('initialized: session-id=%s | server_capabilities=%s' %
91 65c6a607 Shikhar Bhushan
                     (self._id, self._server_capabilities))
92 65c6a607 Shikhar Bhushan
    
93 a14c36f9 Shikhar Bhushan
    def add_listener(self, listener):
94 a14c36f9 Shikhar Bhushan
        logger.debug('installing listener %r' % listener)
95 583c11f6 Shikhar Bhushan
        if not isinstance(listener, SessionListener):
96 583c11f6 Shikhar Bhushan
            raise SessionError("Listener must be a SessionListener type")
97 a14c36f9 Shikhar Bhushan
        with self._lock:
98 a14c36f9 Shikhar Bhushan
            self._listeners.add(listener)
99 a14c36f9 Shikhar Bhushan
    
100 a14c36f9 Shikhar Bhushan
    def remove_listener(self, listener):
101 a14c36f9 Shikhar Bhushan
        logger.debug('discarding listener %r' % listener)
102 a14c36f9 Shikhar Bhushan
        with self._lock:
103 a14c36f9 Shikhar Bhushan
            self._listeners.discard(listener)
104 a14c36f9 Shikhar Bhushan
    
105 a14c36f9 Shikhar Bhushan
    def get_listener_instance(self, cls):
106 a14c36f9 Shikhar Bhushan
        with self._lock:
107 a14c36f9 Shikhar Bhushan
            for listener in self._listeners:
108 a14c36f9 Shikhar Bhushan
                if isinstance(listener, cls):
109 a14c36f9 Shikhar Bhushan
                    return listener
110 a14c36f9 Shikhar Bhushan
    
111 6625258b Shikhar Bhushan
    def connect(self, *args, **kwds):
112 d095a59e Shikhar Bhushan
        raise NotImplementedError
113 d095a59e Shikhar Bhushan
114 d095a59e Shikhar Bhushan
    def run(self):
115 d095a59e Shikhar Bhushan
        raise NotImplementedError
116 d095a59e Shikhar Bhushan
    
117 d6688264 Shikhar Bhushan
    def send(self, message):
118 d6688264 Shikhar Bhushan
        logger.debug('queueing %s' % message)
119 d6688264 Shikhar Bhushan
        self._q.put(message)
120 d6688264 Shikhar Bhushan
    
121 d095a59e Shikhar Bhushan
    ### Properties
122 d095a59e Shikhar Bhushan
    
123 d095a59e Shikhar Bhushan
    @property
124 d095a59e Shikhar Bhushan
    def client_capabilities(self):
125 d095a59e Shikhar Bhushan
        return self._client_capabilities
126 d095a59e Shikhar Bhushan
    
127 d095a59e Shikhar Bhushan
    @property
128 d095a59e Shikhar Bhushan
    def server_capabilities(self):
129 d095a59e Shikhar Bhushan
        return self._server_capabilities
130 d095a59e Shikhar Bhushan
    
131 d095a59e Shikhar Bhushan
    @property
132 d095a59e Shikhar Bhushan
    def connected(self):
133 d095a59e Shikhar Bhushan
        return self._connected
134 d095a59e Shikhar Bhushan
    
135 d095a59e Shikhar Bhushan
    @property
136 d095a59e Shikhar Bhushan
    def id(self):
137 583c11f6 Shikhar Bhushan
        "`session-id` if session is initialized, :const:`None` otherwise"
138 d095a59e Shikhar Bhushan
        return self._id
139 94803aaf Shikhar Bhushan
    
140 94803aaf Shikhar Bhushan
    @property
141 94803aaf Shikhar Bhushan
    def can_pipeline(self):
142 94803aaf Shikhar Bhushan
        return True
143 583c11f6 Shikhar Bhushan
144 583c11f6 Shikhar Bhushan
145 583c11f6 Shikhar Bhushan
class SessionListener(object):
146 583c11f6 Shikhar Bhushan
    
147 583c11f6 Shikhar Bhushan
    def callback(self, root, raw):
148 583c11f6 Shikhar Bhushan
        raise NotImplementedError
149 583c11f6 Shikhar Bhushan
    
150 583c11f6 Shikhar Bhushan
    def errback(self, ex):
151 583c11f6 Shikhar Bhushan
        raise NotImplementedError
152 583c11f6 Shikhar Bhushan
153 583c11f6 Shikhar Bhushan
154 583c11f6 Shikhar Bhushan
class HelloHandler(SessionListener):
155 583c11f6 Shikhar Bhushan
    
156 583c11f6 Shikhar Bhushan
    def __init__(self, init_cb, error_cb):
157 583c11f6 Shikhar Bhushan
        self._init_cb = init_cb
158 583c11f6 Shikhar Bhushan
        self._error_cb = error_cb
159 583c11f6 Shikhar Bhushan
    
160 583c11f6 Shikhar Bhushan
    def callback(self, root, raw):
161 583c11f6 Shikhar Bhushan
        if content.unqualify(root[0]) == 'hello':
162 583c11f6 Shikhar Bhushan
            try:
163 583c11f6 Shikhar Bhushan
                id, capabilities = HelloHandler.parse(raw)
164 583c11f6 Shikhar Bhushan
            except Exception as e:
165 583c11f6 Shikhar Bhushan
                self._error_cb(e)
166 583c11f6 Shikhar Bhushan
            else:
167 583c11f6 Shikhar Bhushan
                self._init_cb(id, capabilities)
168 583c11f6 Shikhar Bhushan
    
169 583c11f6 Shikhar Bhushan
    def errback(self, err):
170 583c11f6 Shikhar Bhushan
        self._error_cb(err)
171 583c11f6 Shikhar Bhushan
    
172 583c11f6 Shikhar Bhushan
    @staticmethod
173 583c11f6 Shikhar Bhushan
    def build(capabilities):
174 583c11f6 Shikhar Bhushan
        "Given a list of capability URI's returns <hello> message XML string"
175 583c11f6 Shikhar Bhushan
        spec = {
176 583c11f6 Shikhar Bhushan
            'tag': content.qualify('hello'),
177 583c11f6 Shikhar Bhushan
            'subtree': [{
178 583c11f6 Shikhar Bhushan
                'tag': 'capabilities',
179 583c11f6 Shikhar Bhushan
                'subtree': # this is fun :-)
180 583c11f6 Shikhar Bhushan
                    [{'tag': 'capability', 'text': uri} for uri in capabilities]
181 583c11f6 Shikhar Bhushan
                }]
182 583c11f6 Shikhar Bhushan
            }
183 583c11f6 Shikhar Bhushan
        return content.dtree2xml(spec)
184 583c11f6 Shikhar Bhushan
    
185 583c11f6 Shikhar Bhushan
    @staticmethod
186 583c11f6 Shikhar Bhushan
    def parse(raw):
187 583c11f6 Shikhar Bhushan
        "Returns tuple of (session-id (str), capabilities (Capabilities)"
188 583c11f6 Shikhar Bhushan
        sid, capabilities = 0, []
189 583c11f6 Shikhar Bhushan
        root = content.xml2ele(raw)
190 583c11f6 Shikhar Bhushan
        for child in root.getchildren():
191 583c11f6 Shikhar Bhushan
            tag = content.unqualify(child.tag)
192 583c11f6 Shikhar Bhushan
            if tag == 'session-id':
193 583c11f6 Shikhar Bhushan
                sid = child.text
194 583c11f6 Shikhar Bhushan
            elif tag == 'capabilities':
195 583c11f6 Shikhar Bhushan
                for cap in child.getchildren():
196 583c11f6 Shikhar Bhushan
                    if content.unqualify(cap.tag) == 'capability':
197 583c11f6 Shikhar Bhushan
                        capabilities.append(cap.text)
198 583c11f6 Shikhar Bhushan
        return sid, Capabilities(capabilities)