77fc6b80d86ee6857db051fcc89b068fd2a2ebd5
[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 from threading import Thread, Event
17 from Queue import Queue
18
19 from capability import CAPABILITIES
20 from content import hello
21 from error import ClientError
22 from subject import Subject
23
24 logger = logging.getLogger('ncclient.session')
25
26 class SessionError(ClientError):
27     
28     pass
29
30 class Session(Thread, Subject):
31     
32     def __init__(self):
33         Thread.__init__(self, name='session')
34         Subject.__init__(self, listeners=[Session.HelloListener(self)])
35         self._client_capabilities = CAPABILITIES
36         self._server_capabilities = None # yet
37         self._id = None # session-id
38         self._connected = False
39         self._error = None
40         self._init_event = Event()
41         self._q = Queue()
42     
43     def connect(self):
44         raise NotImplementedError
45
46     def send(self, message):
47         message = (u'<?xml version="1.0" encoding="UTF-8"?>%s' % message).encode('utf-8')
48         logger.debug('queueing message: \n%s' % message)
49         self._q.put(message)
50
51     def run(self):
52         raise NotImplementedError
53     
54     ### Properties
55
56     @property
57     def client_capabilities(self):
58         return self._client_capabilities
59     
60     @property
61     def serve_capabilities(self):
62         return self._server_capabilities
63     
64     @property
65     def connected(self):
66         return self._connected
67     
68     @property
69     def id(self):
70         return self._id    
71
72     class HelloListener:
73         
74         def __init__(self, session):
75             self._session = session
76         
77         def _done(self, err=None):
78             if err is not None:
79                 self._session._error = err
80             self._session.remove_listener(self)
81             self._session._init_event.set()
82         
83         ### Events
84         
85         def reply(self, data):
86             err = None
87             try:
88                 id, capabilities = hello.parse(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(hello.make(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