Revision 35ad9d81

b/ncclient/capability.py
34 34
            shorthand = Capabilities.guess_shorthand(uri)
35 35
        self._dict[uri] = shorthand
36 36
    
37
    set = add
38
    
37 39
    def remove(self, key):
38 40
        if key in self._dict:
39 41
            del self._dict[key]
......
42 44
                if self._dict[uri] == key:
43 45
                    del self._dict[uri]
44 46
                    break
45
    
47
        
46 48
    @staticmethod
47 49
    def guess_shorthand(uri):
48 50
        if uri.startswith('urn:ietf:params:netconf:capability:'):
49 51
            return (':' + uri.split(':')[5])
52

  
50 53
    
51 54
CAPABILITIES = Capabilities([
52 55
    'urn:ietf:params:netconf:base:1.0',
b/ncclient/content/__init__.py
12 12
# See the License for the specific language governing permissions and
13 13
# limitations under the License.
14 14

  
15
from ..error import ClientError, NETCONFError
15
NETCONF_NS = 'urn:ietf:params:xml:ns:netconf:base:1.0'
16 16

  
17
class ContentError(ClientError): pass
18

  
19
class ValidationError(NETCONFError): pass
b/ncclient/content/error.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
from ..error import ClientError, NETCONFError
16

  
17
class ContentError(ClientError):
18
    pass
b/ncclient/content/hello.py
17 17

  
18 18
logging.getLogger('ncclient.content.hello')
19 19

  
20
from . import NETCONF_NS
21
from .util import qualify as _
20 22
from ..capability import Capabilities
21 23

  
22
ns = 'urn:ietf:params:xml:ns:netconf:base:1.0'
23

  
24 24
def make(capabilities):
25
    return '<hello xmlns="%s">%s</hello>' % (ns, capabilities)
25
    return '<hello xmlns="%s">%s</hello>' % (NETCONF_NS, capabilities)
26 26

  
27 27
def parse(raw):
28 28
    id, capabilities = 0, Capabilities()
29
    hello = ElementTree.fromstring(raw)
30
    for child in hello.getchildren():
31
        if child.tag == '{%s}session-id' % ns:
32
            id = child.text
33
        elif child.tag == '{%s}capabilities' % ns:
34
            for cap in child.getiterator('{%s}capability' % ns):
35
                capabilities.add(cap.text)
29
    root = ElementTree.fromstring(raw)
30
    if root.tag == _('hello'):
31
        for child in hello.getchildren():
32
            if child.tag == _('session-id'):
33
                id = int(child.text)
34
            elif child.tag == _('capabilities'):
35
                for cap in child.getiterator(_('capability')):
36
                    capabilities.add(cap.text)
36 37
    return id, capabilities
37

  
38
#class HelloParser:
39
#    
40
#    'Fast parsing with expat'
41
#    
42
#    capability, sid = range(2)
43
#    
44
#    def __init__(self, raw):
45
#        self._sid = None
46
#        self._capabilities = Capabilities()
47
#        p = xml.parsers.expat.ParserCreate()
48
#        p.StartElementHandler = self._start_element
49
#        p.EndElementHandler = self._end_element
50
#        p.CharacterDataHandler = self._char_data
51
#        self._expect = None
52
#        p.parse(raw, True)
53
#    
54
#    def _start_element(self, name, attrs):
55
#        if name == 'capability':
56
#            self._expect = HelloParser.capability
57
#        elif name == 'session-id':
58
#            self._expect = HelloParser.sid
59
#    
60
#    def _end_element(self, name):
61
#        self._expect = None
62
#    
63
#    def _char_data(self, data):
64
#        if self._expect == HelloParser.capability:
65
#            self._capabilities.add(data)
66
#        elif self._expect == HelloParser.sid:
67
#            self._sid = int(data)
68
#    
69
#    @property
70
#    def sid(self): return self._sid
71
#    
72
#    @property
73
#    def capabilities(self): return self._capabilities
b/ncclient/content/rpc.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
from xml.etree import cElementTree as ElementTree
16

  
17
from . import NETCONF_NS
18
from .util import qualify as _
19

  
20
def make(id, op):
21
    return '<rpc message-id="%s" xmlns="%s">%s</rpc>' % (id, NETCONF_NS, op)
22

  
23
#def parse(raw):
24
#    
25
#    class RootElementParser:
26
#        
27
#        def __init__(self):
28
#            self.id = 0
29
#            self.is_notification = False
30
#            
31
#        def start(self, tag, attrib):
32
#            if tag == _('rpc'):
33
#                self.id = int(attrib['message-id'])
34
#            elif tag == _('notification'):
35
#                self.is_notification = True
36
#    
37
#    target = RootElementParser()
38
#    parser = ElementTree.XMLTreeBuilder(target=target)
39
#    parser.feed(raw)
40
#    return target.id, target.is_notification
41
#
b/ncclient/content/util.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
from . import NETCONF_NS
16

  
17
def qualify(tag, ns=NETCONF_NS):
18
    return '{%s}%s' % (ns, tag)
b/ncclient/listener.py
12 12
# See the License for the specific language governing permissions and
13 13
# limitations under the License.
14 14

  
15
from threading import Lock
15
from content import rpc
16 16

  
17
import logging
17
class SessionListener:
18 18

  
19
logger = logging.getLogger('ncclient.listener')
20

  
21
class Subject:
22
        
23
    def __init__(self, listeners=[]):
24
        self._listeners = listeners
25
        self._lock = Lock()
26
    
27
    def has_listener(self, listener):
28
        with self._lock:
29
            return (listener in self._listeners)
19
    def __init__(self):
20
        self._id2rpc = {}
21
        self._subscription_id = None # notifications are delivered to the rpc
22
                                    # that created the subscription
30 23
    
31
    def add_listener(self, listener):
32
        with self._lock:
33
            self._listeners.append(listener)
24
    def set_subscription(self, id):
25
        self._subscription = id
34 26
    
35
    def remove_listener(self, listener):
36
        with self._lock:
37
            try:
38
                self._listeners.remove(listener)
39
            except ValueError:
40
                pass
27
    def reply(self, raw):
28
        id, is_notification = rpc.parse(raw)
29
        if is_notification:
30
            self._id2rpc[self._subscription_id].event(raw)
31
        else:
32
            self._id2rpc[id]._deliver(raw)
33
            del self._id2rpc[id]
41 34
    
42
    def dispatch(self, event, *args, **kwds):
43
        with self._lock:
44
            listeners = list(self._listeners)
45
        for l in listeners:
46
            logger.debug('dispatching [%s] to [%s]' % (event, l.__class__))
47
            try:
48
                getattr(l, event)(*args, **kwds)
49
            except Exception as e:
50
                logger.warning(e)
35
    def error(self, buf):
36
        pass
b/ncclient/rpc.py
12 12
# See the License for the specific language governing permissions and
13 13
# limitations under the License.
14 14

  
15
from threading import Event
15
import content
16 16

  
17
from listener import Listener
17
from threading import Event
18 18

  
19
from content import MessageIDParser
19
from listener import RPCReplyListener
20 20

  
21 21
class RPC:
22 22
    
23
    cur_id = {}
23
    current_id = {}
24
    listeners = {}
24 25

  
25 26
    def __init__(self, session=None, async=False):
26 27
        self._session = None
......
33 34
        if self._event.isSet():
34 35
            return self._reply
35 36
    
36
    def do(self, session, async=False):
37
    def do(self, async=False):
37 38
        self._async = async
38 39
    
39
    def deliver(self, reply):
40
    def _deliver(self, reply):
40 41
        self._reply = reply
41 42
        self._event.set()
42 43

  
43 44
    @property
44
    def has_reply(self): return self._event.isSet()
45
    def has_reply(self):
46
        return self._event.isSet()
45 47
    
46 48
    @property
47
    def async(self): return self._async
49
    def is_async(self):
50
        return self._async
48 51
    
49 52
    @property
50
    def listener(self): return self._listener
53
    def listener(self):
54
        if RPC.listeners[self._sid] is None:
55
            RPC.listeners[self.sid] = listener.RPCReplyListener()
56
        return RPC.listeners[self._sid]
57
    
58
    @property
59
    def ok(self):
60
        pass
51 61
    
52 62
    def _next_id(self):
53
        cur_id[self._sid] = cur_id.get(self._sid, 0) + 1
54
        return cur_id[self._sid]
63
        RPC.current_id[self._session.id] = RPC.current_id.get(self._session.id, 0) + 1
64
        return RPC.current_id[self._sid]
55 65
    
56 66
class RPCReply:
57 67
    
58
    def __init__(self, raw):
68
    def __init__(self, id, raw):
69
        self._id = id
59 70
        self._raw = raw
60 71
    
61
    def get_id(self):
62
        return content.rpc.parse_msg_id(raw)
63

  
64
class RPCError(NETCONFError):
72
    @property
73
    def id(self):
74
        return self._id
65 75
    
76
class RPCError(NETCONFError):
66 77
    pass
67

  
68
class ReplyListener(Listener):
69
    
70
    def __init__(self):
71
        self._id2rpc = {}
72
    
73
    def reply(self, msg):
74
        reply = RPCReply(msg)
75
        id2rpc[reply.get_id()].deliver(reply)
76
    
77
    def error(self, buf):
78
        pass
b/ncclient/session.py
13 13
# limitations under the License.
14 14

  
15 15
import logging
16

  
17 16
from threading import Thread, Event
18 17
from Queue import Queue
19 18

  
20
from error import ClientError
21
from content import hello
22
from listener import Subject
23 19
from capability import CAPABILITIES
20
from content import hello
21
from error import ClientError
22
from subject import Subject
24 23

  
25 24
logger = logging.getLogger('ncclient.session')
26 25

  
27
class SessionError(ClientError): pass
26
class SessionError(ClientError):
27
    
28
    pass
28 29

  
29 30
class Session(Thread, Subject):
30 31
    
......
39 40
        self._init_event = Event()
40 41
        self._q = Queue()
41 42
    
42
    def _close(self):
43
        self._connected = False
44
    
45
    def _init(self):
46
        self._connected = True
47
        # start the subclass' main loop
48
        self.start()
49
        # queue client's hello message for sending
50
        self.send(hello.make(self._client_capabilities))
51
        # we expect server's hello message, wait for _init_event to be set by HelloListener
52
        self._init_event.wait()
53
        # there may have been an error
54
        if self._error:
55
            self._close()
56
            raise self._error
57

  
58 43
    def connect(self):
59 44
        raise NotImplementedError
60 45

  
......
69 54
    ### Properties
70 55

  
71 56
    @property
72
    def client_capabilities(self): return self._client_capabilities
57
    def client_capabilities(self):
58
        return self._client_capabilities
73 59
    
74 60
    @property
75
    def serve_capabilities(self): return self._server_capabilities
61
    def serve_capabilities(self):
62
        return self._server_capabilities
76 63
    
77 64
    @property
78
    def connected(self): return self._connected
65
    def connected(self):
66
        return self._connected
79 67
    
80 68
    @property
81
    def id(self): return self._id    
69
    def id(self):
70
        return self._id    
82 71

  
83 72
    class HelloListener:
84 73
        
......
106 95
        
107 96
        def close(self, err):
108 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
b/ncclient/ssh.py
62 62
        self._channel = transport.open_session()
63 63
        self._channel.invoke_subsystem('netconf')
64 64
        self._channel.set_name('netconf')
65
        self._init()
66

  
67
    def _close(self):
68
        self._channel.close()
69
        Session._close(self)
65
        self._connect()
70 66
    
71 67
    def run(self):
72 68
        
......
105 101
        
106 102
        logger.debug('** broke out of main loop **')
107 103
        self.dispatch('close', SessionCloseError(self._in_buf, self._out_buf))
104
    
105
    def _close(self):
106
        self._channel.close()
107
        Session._close(self)
108

  
108 109

  
109 110
class MissingHostKeyPolicy(paramiko.MissingHostKeyPolicy):
110 111
    
......
113 114
    
114 115
    def missing_host_key(self, client, hostname, key):
115 116
        if not self._cb(hostname, key):
116
            raise SSHError
117
            raise SSHError
b/ncclient/subject.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
from threading import Lock
16

  
17
import logging
18

  
19
logger = logging.getLogger('ncclient.listener')
20

  
21
class Subject:
22
        
23
    def __init__(self, listeners=[]):
24
        self._listeners = listeners
25
        self._lock = Lock()
26
    
27
    def has_listener(self, listener):
28
        with self._lock:
29
            return (listener in self._listeners)
30
    
31
    def add_listener(self, listener):
32
        with self._lock:
33
            self._listeners.append(listener)
34
    
35
    def remove_listener(self, listener):
36
        with self._lock:
37
            try:
38
                self._listeners.remove(listener)
39
            except ValueError:
40
                pass
41
    
42
    def dispatch(self, event, *args, **kwds):
43
        with self._lock:
44
            listeners = list(self._listeners)
45
        for l in listeners:
46
            logger.debug('dispatching [%s] to [%s]' % (event, l.__class__))
47
            try:
48
                getattr(l, event)(*args, **kwds)
49
            except Exception as e:
50
                logger.warning(e)
51

  
52

  
53
class SessionListener:
54

  
55
    def __init__(self):
56
        self._id2rpc = {}
57
        self._subscription = None
58

  
59
    def reply(self, raw):
60
        reply = RPCReply(msg)
61
        id2rpc[reply.id]._deliver(reply)
62
    
63
    def error(self, buf):
64
        pass

Also available in: Unified diff