Revision 195254ce

b/ncclient.kpf
1
<?xml version="1.0" encoding="UTF-8"?>
2
<!-- Komodo Project File - DO NOT EDIT -->
3
<project id="f5560a11-15e2-4c25-b6c2-84cc72e939fb" kpf_version="4" name="ncclient.kpf">
4
<preference-set idref="a7a1f1aa-d22c-4dc4-8cc7-a8cd8ce97f8a">
5
<preference-set id="Invocations">
6
<preference-set id="default">
7
  <string id="cookieparams"></string>
8
  <string id="cwd"></string>
9
  <string id="documentRoot"></string>
10
  <string id="executable-params"></string>
11
  <string relative="path" id="filename">src/session/ssh.py</string>
12
  <string id="getparams"></string>
13
  <string id="language">Python</string>
14
  <string id="mpostparams"></string>
15
  <string id="params"></string>
16
  <string id="postparams"></string>
17
  <string id="posttype">application/x-www-form-urlencoded</string>
18
  <string id="request-method">GET</string>
19
  <boolean id="show-dialog">1</boolean>
20
  <boolean id="sim-cgi">0</boolean>
21
  <boolean id="use-console">0</boolean>
22
  <string id="userCGIEnvironment"></string>
23
  <string id="userEnvironment"></string>
24
</preference-set>
25
</preference-set>
26
  <string id="lastInvocation">default</string>
27
</preference-set>
28
<preference-set idref="f5560a11-15e2-4c25-b6c2-84cc72e939fb">
29
  <string id="donotask_action_remotedebug_mapped_uri"></string>
30
  <boolean id="donotask_remotedebug_mapped_uri">0</boolean>
31
  <string id="import_exclude_matches">*.*~;*.bak;*.tmp;CVS;.#*;*.pyo;*.pyc;.svn;*%*;tmp*.html;.DS_Store</string>
32
  <string id="import_include_matches"></string>
33
  <boolean id="import_live">1</boolean>
34
  <boolean id="import_recursive">1</boolean>
35
  <string id="import_type">useFolders</string>
36
  <string id="javascriptExtraPaths"></string>
37
  <string id="lastTestPlanName_pref">New test plan #1</string>
38
  <string id="mappedPaths"></string>
39
  <string id="pythonExtraPaths"></string>
40
  <string id="tclExtraPaths"></string>
41
<preference-set id="testPlans">
42
<preference-set id="New test plan #1">
43
  <string id="command_line"></string>
44
  <string relative="url" id="directory"></string>
45
  <string id="language">Perl - TAP (*.t)</string>
46
</preference-set>
47
</preference-set>
48
</preference-set>
49
</project>
/dev/null
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
__version__ = "0.01"
16

  
17

  
18
class ncclientError(Exception): pass
19

  
20
class NETCONFError(ncclientError): pass
/dev/null
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 ncclientError, NETCONFError
16

  
17
class ContentError(ncclientError): pass
18

  
/dev/null
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
    'Thread-safe abstact class for event-dispatching subjects'
24
    
25
    def __init__(self, listeners=[]):
26
        self._listeners = listeners
27
        self._lock = Lock()
28
    
29
    def has_listener(self, listener):
30
        with self._lock:
31
            return (listener in self._listeners)
32
    
33
    def add_listener(self, listener):
34
        with self._lock:
35
            self._listeners.append(listener)
36
    
37
    def remove_listener(self, listener):
38
        with self._lock:
39
            try:
40
                self._listeners.remove(listener)
41
            except ValueError:
42
                pass
43
    
44
    def dispatch(self, event, *args, **kwds):
45
        with self._lock:
46
            for l in self._listeners:
47
                try:
48
                    getattr(l, event)(*args, **kwds)
49
                except Exception as e:
50
                    logger.warning(e)
51

  
52
if __name__=="__main__":
53
    
54
    logging.basicConfig(level=logging.DEBUG)
55
    
56
    class Listener:
57
        def reply(self, data):
58
            print data
59
        def error(self, err_info):
60
            print err_info
61
    
62
    subject = Subject()        
63
    subject.add_listener(Listener())
64
    
65
    subject.dispatch('reply', 'hello world')
66
    subject.dispatch('error', 'bye world')
67
    subject.dispatch('undefined', 'happy deliverin')
/dev/null
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

  
16
from threading import Event
17

  
18
class OperationError(NETCONFError): pass
19

  
20

  
21
class Operation(RPCRequest):
22
    pass
23

  
/dev/null
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 listener import Listener
16

  
17
class RPC:
18
    pass
19

  
20
class RPCRequest(RPC):
21
    
22
    cur_msg_id = {}
23

  
24
    @cls
25
    def next_id(cls, session_id):
26
        cur_msg_id[session_id] = cur_msg_id.get(session_id, 0) + 1
27
        return cur_msg_id[session_id]
28

  
29
    def __init__(self):
30
        self._reply = None
31
        self._event = Event()
32
        self._async = None
33

  
34
    def get_reply(self, timeout=2.0):
35
        self._event.wait(timeout)
36
        if self._event.isSet():
37
            return self._reply
38

  
39
    def do(self, session, async=False):
40
        self._async = async
41
        
42
    @property
43
    def async(self):
44
        return self._async
45
    
46
class RPCReply(RPC):
47
    pass
48

  
49
class RPCError(OperationError):
50
    pass
51

  
52
class ReplyListener(Listener):
53
    
54
    def __init__(self):
55
        self._id2rpc = {}
56
    
57
    def reply(self, msg):
58
        # if all good:
59
        op = id2op[id]
60
        op._reply = parsed_msg
61
        # else:
62
        self._error = True
63
        
64
        op._event.set()
65
        pass
66
    
67
    def error(self, buf):
68
        pass
69

  
/dev/null
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 content import Creator, Parser
16

  
17
from threading import Thread
18
from listener import Subject, Listener
19

  
20
class SessionError(ncclientError): pass
21

  
22
class Session(Thread, Subject, Listener):
23
    
24
    CLIENT, SERVER = range(2)
25
    
26
    def __init__(self, capabilities=None, listeners=[]):
27
        Thread.__init__(self, name='session')
28
        listeners.append(self)
29
        Subject.__init__(self, listeners=listeners)
30
        Thread.setDaemon(True)
31
        self._capabilities = {
32
            CLIENT: capabilities,
33
            SERVER: None # yet
34
        }
35
        self._id = None # session-id
36
        self._connected = False
37
        self._initialised = False
38
        self._error = False
39
        self._q = Queue.Queue()
40
        
41
    def _init(self, id, capabilities):
42
        self.id = id
43
        self.capabilities[SERVER] = capabilities
44
        self.initialised = True
45
    
46
    def _greet(self):
47
        hello = Creator()
48
        # ...
49
        self._q.add(hello)
50
    
51
    def _close(self):
52
        raise NotImplementedError
53
    
54
    def connect(self):
55
        raise NotImplementedError
56

  
57
    def send(self, message):
58
        'Blocks if session not initialised yet'
59
        while not (self.ready or self._error):
60
            time.sleep(0.1)
61
        if self._error:
62
            raise SessionError
63
        else:
64
            self._q.add(message)
65

  
66
    def run(self):
67
        raise NotImplementedError
68
    
69
    ### Listener methods - relevant for the initial greeting
70
    
71
    def reply(self, data, *args, **kwds):
72
        id, capabilities = None, None
73
        try:
74
            p = Parser()
75
            # ...
76
            self._init(id, capabilities)
77
        except:
78
            self._error = True
79
        finally:
80
            self.remove_listener(self)
81
    
82
    def error(self, data, *args, **kwds):
83
        self._close()
84
        self.remove_listener(self)
85
        self._error = True
86
    
87
    ### Properties
88

  
89
    @property
90
    def client_capabilities(self): return self._capabilities[CLIENT]
91
    
92
    @property
93
    def serve_capabilities(self): return self._capabilities[SERVER]
94
    
95
    @property
96
    def ready(self): return (self._connected and self._initialised)
97
    
98
    @property
99
    def id(self): return self._id
100
    
/dev/null
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
import paramiko
17

  
18
from select import select as select
19

  
20
from session import Session, SessionError
21

  
22
logger = logging.getLogger('ncclient.ssh')
23

  
24
class SSHError(SessionError): pass
25

  
26
class SSHSession(Session):
27
    
28
    BUF_SIZE = 4096
29
    MSG_DELIM = ']]>>]]>'
30
    MSG_DELIM_LEN = len(MSG_DELIM)
31
    
32
    def __init__(self, capabilities, load_known_hosts=True,
33
                 missing_host_key_policy=paramiko.RejectPolicy):
34
        Session.__init__(self, capabilities)
35
        self._inBuf = ''
36
        self._outBuf = ''
37
        self._client = SSHClient()
38
        if load_known_hosts:
39
            self._client.load_system_host_keys()
40
        self._client.set_missing_host_key_policy(missing_host_key_policy)
41
    
42
    def load_host_keys(self, filename):
43
        self._client.load_host_keys(filename)
44
    
45
    def set_missing_host_key_policy(self, policy):
46
        self._client.set_missing_host_key_policy(policy)
47
    
48
    def connect(self, hostname, port=830, username=None, password=None,
49
                key_filename=None, timeout=None, allow_agent=True,
50
                look_for_keys=True):
51
        self._client.connect(hostname, port=port, username=username,
52
                             password=password, key_filename=key_filename,
53
                             timeout=timeout, allow_agent=allow_agent,
54
                             look_for_keys=look_for_keys)
55
        transport = self._client.get_transport()
56
        self._channel = transport.open_session()
57
        self._channel.invoke_subsystem('netconf')
58
        self.connected = True
59
        self._greet()
60
        self.start()
61

  
62
    def _close(self):
63
        self._channel.shutdown(2)
64
    
65
    def run(self):
66
        sock = self._channel
67
        sock.setblocking(0)
68
        q = self._q
69
        while True:
70
            (r, w, e) = select([sock], [sock], [], 60)
71
            if w:
72
                if not q.empty():
73
                   self._outBuffer += ( q.get() + MSG_DELIM )
74
                if self._outBuffer:
75
                    n = sock.send(self._outBuffer)
76
                    self._outBuffer = self._outBuffer[n:]
77
            if r:
78
                data = sock.recv(BUF_SIZE)
79
                if data:
80
                    self._inBuf += data
81
                    (before, _, after) = self._inBuf.partition(MSG_DELIM)
82
                    if after:
83
                        self.dispatch('reply', before)
84
                        self._inBuf = after
85
                else:
86
                    self.dispatch('error', self._inBuf)
87

  
88

  
89
class CallbackPolicy(paramiko.MissingHostKeyPolicy):
90
    
91
    def __init__(self, cb):
92
        self._cb = cb
93
    
94
    def missing_host_key(self, client, hostname, key):
95
        if not self._cb(hostname, key):
96
            raise SSHError

Also available in: Unified diff