restructured and better
[ncclient] / ncclient / session.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 import logging
16
17 import content
18
19 from threading import Thread, Event
20 from Queue import Queue
21
22 from capability import CAPABILITIES
23 from error import ClientError
24 from subject import Subject
25
26 logger = logging.getLogger('ncclient.session')
27
28 class SessionError(ClientError):
29     
30     pass
31
32 class Session(Thread, Subject):
33     
34     def __init__(self):
35         Thread.__init__(self, name='session')
36         Subject.__init__(self, listeners=[Session.HelloListener(self)])
37         self._client_capabilities = CAPABILITIES
38         self._server_capabilities = None # yet
39         self._id = None # session-id
40         self._connected = False
41         self._error = None
42         self._init_event = Event()
43         self._q = Queue()
44     
45     def connect(self):
46         raise NotImplementedError
47
48     def send(self, message):
49         message = (u'<?xml version="1.0" encoding="UTF-8"?>%s' % message).encode('utf-8')
50         logger.debug('queueing message: \n%s' % message)
51         self._q.put(message)
52
53     def run(self):
54         raise NotImplementedError
55     
56     ### Properties
57
58     @property
59     def client_capabilities(self):
60         return self._client_capabilities
61     
62     @property
63     def serve_capabilities(self):
64         return self._server_capabilities
65     
66     @property
67     def connected(self):
68         return self._connected
69     
70     @property
71     def id(self):
72         return self._id    
73
74     class HelloListener:
75         
76         def __init__(self, session):
77             self._session = session
78         
79         def _done(self, err=None):
80             if err is not None:
81                 self._session._error = err
82             self._session.remove_listener(self)
83             self._session._init_event.set()
84         
85         def reply(self, data):
86             err = None
87             try:
88                 id, capabilities = content.parse_hello(data)
89                 logger.debug('session_id: %s | capabilities: \n%s', id, capabilities)
90                 self._session._id, self._session.capabilities = id, capabilities
91             except Exception as e:
92                 err = e
93             finally:
94                 self._done(err)
95         
96         def close(self, err):
97             self._done(err)
98     
99     ### Methods for which subclasses should call super after they are done
100     
101     def _connect(self):
102         self._connected = True
103         # start the subclass' main loop
104         self.start()
105         # queue client's hello message for sending
106         self.send(content.make_hello(self._client_capabilities))
107         # we expect server's hello message, wait for _init_event to be set by HelloListener
108         self._init_event.wait()
109         # there may have been an error
110         if self._error:
111             self._close()
112             raise self._error
113     
114     def _close(self):
115         self._connected = False