Statistics
| Branch: | Tag: | Revision:

root / ncclient / transport / session.py @ 6e571704

History | View | Annotate | Download (7.6 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 9667bcb2 Shikhar Bhushan
from ncclient.xml_ import *
19 2f8bc438 Shikhar Bhushan
from ncclient.capabilities import Capabilities
20 d095a59e Shikhar Bhushan
21 66cd54c8 Shikhar Bhushan
from errors import TransportError
22 66cd54c8 Shikhar Bhushan
23 41e2ed46 Shikhar Bhushan
import logging
24 41e2ed46 Shikhar Bhushan
logger = logging.getLogger('ncclient.transport.session')
25 41e2ed46 Shikhar Bhushan
26 a14c36f9 Shikhar Bhushan
class Session(Thread):
27 216bb34c Shikhar Bhushan
28 4f650d54 Shikhar Bhushan
    "Base class for use by transport protocol implementations."
29 4f650d54 Shikhar Bhushan
30 2f8bc438 Shikhar Bhushan
    def __init__(self, capabilities):
31 a14c36f9 Shikhar Bhushan
        Thread.__init__(self)
32 4f650d54 Shikhar Bhushan
        self.setDaemon(True)
33 9667bcb2 Shikhar Bhushan
        self._listeners = set()
34 a14c36f9 Shikhar Bhushan
        self._lock = Lock()
35 4f650d54 Shikhar Bhushan
        self.setName('session')
36 d6688264 Shikhar Bhushan
        self._q = Queue()
37 2f8bc438 Shikhar Bhushan
        self._client_capabilities = capabilities
38 d095a59e Shikhar Bhushan
        self._server_capabilities = None # yet
39 d095a59e Shikhar Bhushan
        self._id = None # session-id
40 d095a59e Shikhar Bhushan
        self._connected = False # to be set/cleared by subclass implementation
41 41e2ed46 Shikhar Bhushan
        logger.debug('%r created: client_capabilities=%r' %
42 41e2ed46 Shikhar Bhushan
                     (self, self._client_capabilities))
43 4f650d54 Shikhar Bhushan
44 a14c36f9 Shikhar Bhushan
    def _dispatch_message(self, raw):
45 a14c36f9 Shikhar Bhushan
        try:
46 9667bcb2 Shikhar Bhushan
            root = parse_root(raw)
47 a14c36f9 Shikhar Bhushan
        except Exception as e:
48 a14c36f9 Shikhar Bhushan
            logger.error('error parsing dispatch message: %s' % e)
49 a14c36f9 Shikhar Bhushan
            return
50 a14c36f9 Shikhar Bhushan
        with self._lock:
51 a14c36f9 Shikhar Bhushan
            listeners = list(self._listeners)
52 a14c36f9 Shikhar Bhushan
        for l in listeners:
53 6f557d54 Shikhar Bhushan
            logger.debug('dispatching message to %r: %s' % (l, raw))
54 0304f041 Shikhar Bhushan
            l.callback(root, raw) # no try-except; fail loudly if you must!
55 0304f041 Shikhar Bhushan
    
56 a14c36f9 Shikhar Bhushan
    def _dispatch_error(self, err):
57 a14c36f9 Shikhar Bhushan
        with self._lock:
58 a14c36f9 Shikhar Bhushan
            listeners = list(self._listeners)
59 a14c36f9 Shikhar Bhushan
        for l in listeners:
60 a14c36f9 Shikhar Bhushan
            logger.debug('dispatching error to %r' % l)
61 0304f041 Shikhar Bhushan
            try: # here we can be more considerate with catching exceptions
62 0304f041 Shikhar Bhushan
                l.errback(err) 
63 a14c36f9 Shikhar Bhushan
            except Exception as e:
64 0b7d3b31 Shikhar Bhushan
                logger.warning('error dispatching to %r: %r' % (l, e))
65 4f650d54 Shikhar Bhushan
66 65c6a607 Shikhar Bhushan
    def _post_connect(self):
67 65c6a607 Shikhar Bhushan
        "Greeting stuff"
68 65c6a607 Shikhar Bhushan
        init_event = Event()
69 65c6a607 Shikhar Bhushan
        error = [None] # so that err_cb can bind error[0]. just how it is.
70 65c6a607 Shikhar Bhushan
        # callbacks
71 65c6a607 Shikhar Bhushan
        def ok_cb(id, capabilities):
72 65c6a607 Shikhar Bhushan
            self._id = id
73 583c11f6 Shikhar Bhushan
            self._server_capabilities = capabilities
74 65c6a607 Shikhar Bhushan
            init_event.set()
75 65c6a607 Shikhar Bhushan
        def err_cb(err):
76 65c6a607 Shikhar Bhushan
            error[0] = err
77 65c6a607 Shikhar Bhushan
            init_event.set()
78 65c6a607 Shikhar Bhushan
        listener = HelloHandler(ok_cb, err_cb)
79 65c6a607 Shikhar Bhushan
        self.add_listener(listener)
80 65c6a607 Shikhar Bhushan
        self.send(HelloHandler.build(self._client_capabilities))
81 65c6a607 Shikhar Bhushan
        logger.debug('starting main loop')
82 65c6a607 Shikhar Bhushan
        self.start()
83 65c6a607 Shikhar Bhushan
        # we expect server's hello message
84 65c6a607 Shikhar Bhushan
        init_event.wait()
85 65c6a607 Shikhar Bhushan
        # received hello message or an error happened
86 65c6a607 Shikhar Bhushan
        self.remove_listener(listener)
87 65c6a607 Shikhar Bhushan
        if error[0]:
88 65c6a607 Shikhar Bhushan
            raise error[0]
89 216bb34c Shikhar Bhushan
        #if ':base:1.0' not in self.server_capabilities:
90 216bb34c Shikhar Bhushan
        #    raise MissingCapabilityError(':base:1.0')
91 4f650d54 Shikhar Bhushan
        logger.info('initialized: session-id=%s | server_capabilities=%s' %
92 4f650d54 Shikhar Bhushan
                    (self._id, self._server_capabilities))
93 4f650d54 Shikhar Bhushan
94 a14c36f9 Shikhar Bhushan
    def add_listener(self, listener):
95 4f650d54 Shikhar Bhushan
        """Register a listener that will be notified of incoming messages and
96 4f650d54 Shikhar Bhushan
        errors.
97 4f650d54 Shikhar Bhushan

98 216bb34c Shikhar Bhushan
        :type listener: :class:`SessionListener`
99 0cdb8b3c Shikhar Bhushan
        """
100 a14c36f9 Shikhar Bhushan
        logger.debug('installing listener %r' % listener)
101 583c11f6 Shikhar Bhushan
        if not isinstance(listener, SessionListener):
102 583c11f6 Shikhar Bhushan
            raise SessionError("Listener must be a SessionListener type")
103 a14c36f9 Shikhar Bhushan
        with self._lock:
104 a14c36f9 Shikhar Bhushan
            self._listeners.add(listener)
105 4f650d54 Shikhar Bhushan
106 a14c36f9 Shikhar Bhushan
    def remove_listener(self, listener):
107 4f650d54 Shikhar Bhushan
        """Unregister some listener; ignore if the listener was never
108 216bb34c Shikhar Bhushan
        registered.
109 216bb34c Shikhar Bhushan

110 216bb34c Shikhar Bhushan
        :type listener: :class:`SessionListener`
111 216bb34c Shikhar Bhushan
        """
112 a14c36f9 Shikhar Bhushan
        logger.debug('discarding listener %r' % listener)
113 a14c36f9 Shikhar Bhushan
        with self._lock:
114 a14c36f9 Shikhar Bhushan
            self._listeners.discard(listener)
115 4f650d54 Shikhar Bhushan
116 a14c36f9 Shikhar Bhushan
    def get_listener_instance(self, cls):
117 216bb34c Shikhar Bhushan
        """If a listener of the specified type is registered, returns the
118 216bb34c Shikhar Bhushan
        instance.
119 4f650d54 Shikhar Bhushan

120 216bb34c Shikhar Bhushan
        :type cls: :class:`SessionListener`
121 0cdb8b3c Shikhar Bhushan
        """
122 a14c36f9 Shikhar Bhushan
        with self._lock:
123 a14c36f9 Shikhar Bhushan
            for listener in self._listeners:
124 a14c36f9 Shikhar Bhushan
                if isinstance(listener, cls):
125 a14c36f9 Shikhar Bhushan
                    return listener
126 4f650d54 Shikhar Bhushan
127 0cdb8b3c Shikhar Bhushan
    def connect(self, *args, **kwds): # subclass implements
128 d095a59e Shikhar Bhushan
        raise NotImplementedError
129 d095a59e Shikhar Bhushan
130 0cdb8b3c Shikhar Bhushan
    def run(self): # subclass implements
131 d095a59e Shikhar Bhushan
        raise NotImplementedError
132 4f650d54 Shikhar Bhushan
133 d6688264 Shikhar Bhushan
    def send(self, message):
134 19e7c7f6 Shikhar Bhushan
        """Send the supplied *message* (xml string) to NETCONF server."""
135 66cd54c8 Shikhar Bhushan
        if not self.connected:
136 66cd54c8 Shikhar Bhushan
            raise TransportError('Not connected to NETCONF server')
137 d6688264 Shikhar Bhushan
        logger.debug('queueing %s' % message)
138 d6688264 Shikhar Bhushan
        self._q.put(message)
139 4f650d54 Shikhar Bhushan
140 d095a59e Shikhar Bhushan
    ### Properties
141 0cdb8b3c Shikhar Bhushan
142 0cdb8b3c Shikhar Bhushan
    @property
143 0cdb8b3c Shikhar Bhushan
    def connected(self):
144 4f650d54 Shikhar Bhushan
        "Connection status of the session."
145 0cdb8b3c Shikhar Bhushan
        return self._connected
146 0cdb8b3c Shikhar Bhushan
147 d095a59e Shikhar Bhushan
    @property
148 d095a59e Shikhar Bhushan
    def client_capabilities(self):
149 4f650d54 Shikhar Bhushan
        "Client's :class:`Capabilities`"
150 d095a59e Shikhar Bhushan
        return self._client_capabilities
151 4f650d54 Shikhar Bhushan
152 d095a59e Shikhar Bhushan
    @property
153 d095a59e Shikhar Bhushan
    def server_capabilities(self):
154 4f650d54 Shikhar Bhushan
        "Server's :class:`Capabilities`"
155 d095a59e Shikhar Bhushan
        return self._server_capabilities
156 4f650d54 Shikhar Bhushan
157 d095a59e Shikhar Bhushan
    @property
158 d095a59e Shikhar Bhushan
    def id(self):
159 19e7c7f6 Shikhar Bhushan
        """A string representing the `session-id`. If the session has not been initialized it will be `None`"""
160 d095a59e Shikhar Bhushan
        return self._id
161 4f650d54 Shikhar Bhushan
162 583c11f6 Shikhar Bhushan
163 583c11f6 Shikhar Bhushan
class SessionListener(object):
164 4f650d54 Shikhar Bhushan
165 4f650d54 Shikhar Bhushan
    """Base class for :class:`Session` listeners, which are notified when a new
166 4f650d54 Shikhar Bhushan
    NETCONF message is received or an error occurs.
167 4f650d54 Shikhar Bhushan

168 0cdb8b3c Shikhar Bhushan
    .. note::
169 4f650d54 Shikhar Bhushan
        Avoid time-intensive tasks in a callback's context.
170 0cdb8b3c Shikhar Bhushan
    """
171 4f650d54 Shikhar Bhushan
172 583c11f6 Shikhar Bhushan
    def callback(self, root, raw):
173 19e7c7f6 Shikhar Bhushan
        """Called when a new XML document is received. The *root* argument allows the callback to determine whether it wants to further process the document.
174 4f650d54 Shikhar Bhushan

175 19e7c7f6 Shikhar Bhushan
        Here, *root* is a tuple of *(tag, attributes)* where *tag* is the qualified name of the root element and *attributes* is a dictionary of its attributes (also qualified names).
176 4f650d54 Shikhar Bhushan

177 19e7c7f6 Shikhar Bhushan
        *raw* will contain the XML document as a string.
178 0cdb8b3c Shikhar Bhushan
        """
179 583c11f6 Shikhar Bhushan
        raise NotImplementedError
180 4f650d54 Shikhar Bhushan
181 583c11f6 Shikhar Bhushan
    def errback(self, ex):
182 0cdb8b3c Shikhar Bhushan
        """Called when an error occurs.
183 4f650d54 Shikhar Bhushan

184 4f650d54 Shikhar Bhushan
        :type ex: :exc:`Exception`
185 0cdb8b3c Shikhar Bhushan
        """
186 583c11f6 Shikhar Bhushan
        raise NotImplementedError
187 583c11f6 Shikhar Bhushan
188 583c11f6 Shikhar Bhushan
189 583c11f6 Shikhar Bhushan
class HelloHandler(SessionListener):
190 4f650d54 Shikhar Bhushan
191 583c11f6 Shikhar Bhushan
    def __init__(self, init_cb, error_cb):
192 583c11f6 Shikhar Bhushan
        self._init_cb = init_cb
193 583c11f6 Shikhar Bhushan
        self._error_cb = error_cb
194 4f650d54 Shikhar Bhushan
195 583c11f6 Shikhar Bhushan
    def callback(self, root, raw):
196 9667bcb2 Shikhar Bhushan
        tag, attrs = root
197 9667bcb2 Shikhar Bhushan
        if tag == qualify("hello"):
198 583c11f6 Shikhar Bhushan
            try:
199 583c11f6 Shikhar Bhushan
                id, capabilities = HelloHandler.parse(raw)
200 583c11f6 Shikhar Bhushan
            except Exception as e:
201 583c11f6 Shikhar Bhushan
                self._error_cb(e)
202 583c11f6 Shikhar Bhushan
            else:
203 583c11f6 Shikhar Bhushan
                self._init_cb(id, capabilities)
204 4f650d54 Shikhar Bhushan
205 583c11f6 Shikhar Bhushan
    def errback(self, err):
206 583c11f6 Shikhar Bhushan
        self._error_cb(err)
207 4f650d54 Shikhar Bhushan
208 583c11f6 Shikhar Bhushan
    @staticmethod
209 583c11f6 Shikhar Bhushan
    def build(capabilities):
210 583c11f6 Shikhar Bhushan
        "Given a list of capability URI's returns <hello> message XML string"
211 6e571704 Leonidas Poulopoulos
        hello = new_ele("hello")
212 9667bcb2 Shikhar Bhushan
        caps = sub_ele(hello, "capabilities")
213 9667bcb2 Shikhar Bhushan
        def fun(uri): sub_ele(caps, "capability").text = uri
214 9667bcb2 Shikhar Bhushan
        map(fun, capabilities)
215 9667bcb2 Shikhar Bhushan
        return to_xml(hello)
216 4f650d54 Shikhar Bhushan
217 583c11f6 Shikhar Bhushan
    @staticmethod
218 583c11f6 Shikhar Bhushan
    def parse(raw):
219 583c11f6 Shikhar Bhushan
        "Returns tuple of (session-id (str), capabilities (Capabilities)"
220 583c11f6 Shikhar Bhushan
        sid, capabilities = 0, []
221 9667bcb2 Shikhar Bhushan
        root = to_ele(raw)
222 583c11f6 Shikhar Bhushan
        for child in root.getchildren():
223 9667bcb2 Shikhar Bhushan
            if child.tag == qualify("session-id"):
224 583c11f6 Shikhar Bhushan
                sid = child.text
225 9667bcb2 Shikhar Bhushan
            elif child.tag == qualify("capabilities"):
226 583c11f6 Shikhar Bhushan
                for cap in child.getchildren():
227 9667bcb2 Shikhar Bhushan
                    if cap.tag == qualify("capability"):
228 583c11f6 Shikhar Bhushan
                        capabilities.append(cap.text)
229 583c11f6 Shikhar Bhushan
        return sid, Capabilities(capabilities)