blob: d8a2afe54da6e67b3c5eda60d54848a033b668a3 [file] [log] [blame]
Khen Nursimulua7b842a2016-12-03 23:28:42 -05001#!/usr/bin/env python
Khen Nursimulu3869d8d2016-11-28 20:44:28 -05002#
3# Copyright 2016 the original author or authors.
4#
5# Licensed under the Apache License, Version 2.0 (the "License");
6# you may not use this file except in compliance with the License.
7# You may obtain a copy of the License at
8#
9# http://www.apache.org/licenses/LICENSE-2.0
10#
11# Unless required by applicable law or agreed to in writing, software
12# distributed under the License is distributed on an "AS IS" BASIS,
13# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14# See the License for the specific language governing permissions and
15# limitations under the License.
16#
17import structlog
18from hexdump import hexdump
19from twisted.internet import protocol
20from twisted.internet.defer import inlineCallbacks, returnValue
21from common.utils.message_queue import MessageQueue
Khen Nursimulua7b842a2016-12-03 23:28:42 -050022from netconf.constants import Constants as C
Khen Nursimulu3869d8d2016-11-28 20:44:28 -050023
24log = structlog.get_logger()
25
26from netconf import MAXSSHBUF
27
28
29class NetconfConnection(protocol.Protocol):
30 def __init__(self, data=None, avatar=None, max_chunk=MAXSSHBUF):
31 self.avatar = avatar
32 self.nc_server = self.avatar.get_nc_server()
33 self.rx = MessageQueue()
34 self.max_chunk = max_chunk
35 self.connected = True
36 self.proto_handler = None
37 self.exiting = False
38
39 def connectionLost(self, reason):
40 log.info('connection-lost')
41 self.connected = False
42 if not self.exiting:
43 self.proto_handler.stop('Connection-Lost')
44
45 def connectionMade(self):
46 log.info('connection-made')
47 self.nc_server.client_connected(self)
48
49 def dataReceived(self, data):
50 log.debug('data-received', len=len(data),
51 received=hexdump(data, result='return'))
52 assert len(data)
53 self.rx.put(data)
54
55 def processEnded(self, reason=None):
56 log.info('process-ended', reason=reason)
57 self.connected = False
58
59 def chunkit(self, msg, maxsend):
60 sz = len(msg)
61 left = 0
62 for unused in range(0, sz // maxsend):
63 right = left + maxsend
64 chunk = msg[left:right]
65 left = right
66 yield chunk
67 msg = msg[left:]
68 yield msg
69
70 def send_msg(self, msg, new_framing):
71 assert self.connected
72 # Apparently ssh has a bug that requires minimum of 64 bytes?
73 # This may not be sufficient to fix this.
74 if new_framing:
Khen Nursimulua7b842a2016-12-03 23:28:42 -050075 msg = "#{}\n{}\n##\n".format(len(msg), msg)
Khen Nursimulu3869d8d2016-11-28 20:44:28 -050076 else:
Khen Nursimulua7b842a2016-12-03 23:28:42 -050077 msg += C.DELIMITER
Khen Nursimulu3869d8d2016-11-28 20:44:28 -050078 for chunk in self.chunkit(msg, self.max_chunk - 64):
Khen Nursimulua7b842a2016-12-03 23:28:42 -050079 log.info('sending', chunk=chunk,
Khen Nursimulu3869d8d2016-11-28 20:44:28 -050080 framing="1.1" if new_framing else "1.0")
81 # out = hexdump(chunk, result='return')
Khen Nursimulua7b842a2016-12-03 23:28:42 -050082 self.transport.write('{}\n'.format(chunk))
Khen Nursimulu3869d8d2016-11-28 20:44:28 -050083
84 @inlineCallbacks
85 def receive_msg_any(self, new_framing):
86 assert self.connected
87 msg = yield self.recv(lambda _: True)
88 if new_framing:
89 returnValue(self._receive_11(msg))
90 else:
91 returnValue(self._receive_10(msg))
92
93 def _receive_10(self, msg):
94 # search for message end indicator
95 searchfrom = 0
Khen Nursimulua7b842a2016-12-03 23:28:42 -050096 eomidx = msg.find(C.DELIMITER, searchfrom)
Khen Nursimulu3869d8d2016-11-28 20:44:28 -050097 if eomidx != -1:
98 log.info('received-msg', msg=msg[:eomidx])
99 return msg[:eomidx]
100 else:
101 log.error('no-message-end-indicators', msg=msg)
102 return msg
103
104 def _receive_11(self, msg):
105 # Message is received in the format "\n#{len}\n{msg}\n##\n"
Khen Nursimulua7b842a2016-12-03 23:28:42 -0500106 # A message may have return characters within it
Khen Nursimulu3869d8d2016-11-28 20:44:28 -0500107 if msg:
Khen Nursimulua7b842a2016-12-03 23:28:42 -0500108 log.info('received-msg-full', msg=msg)
Khen Nursimulu3869d8d2016-11-28 20:44:28 -0500109 msg = msg.split('\n')
110 if len(msg) > 2:
Khen Nursimulua7b842a2016-12-03 23:28:42 -0500111 msg = ''.join(msg[2:(len(msg)-2)])
112 log.info('parsed-msg\n', msg=msg)
113 return msg
Khen Nursimulu3869d8d2016-11-28 20:44:28 -0500114 return None
115
116 def close_connection(self):
117 log.info('closing-connection')
118 self.exiting = True
119 self.transport.loseConnection()
120
121 def recv(self, predicate):
122 assert self.connected
123 return self.rx.get(predicate)
124
125 def recv_any(self, new_framing):
126 return self.recv(lambda _: True)
127
128 def recv_xid(self, xid):
129 return self.recv(lambda msg: msg.xid == xid)