blob: 9043584f9c934a9d4ed214fb1cc1eb52670e356f [file] [log] [blame]
Khen Nursimulua7b842a2016-12-03 23:28:42 -05001#!/usr/bin/env python
Khen Nursimulu3869d8d2016-11-28 20:44:28 -05002#
Khen Nursimuluc9ef7c12017-01-04 20:40:53 -05003# Copyright 2017 the original author or authors.
Khen Nursimulu3869d8d2016-11-28 20:44:28 -05004#
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
Khen Nursimuluc9ef7c12017-01-04 20:40:53 -050026MAXSSHBUF = C.MAXSSHBUF
Khen Nursimulu3869d8d2016-11-28 20:44:28 -050027
28class NetconfConnection(protocol.Protocol):
29 def __init__(self, data=None, avatar=None, max_chunk=MAXSSHBUF):
30 self.avatar = avatar
31 self.nc_server = self.avatar.get_nc_server()
32 self.rx = MessageQueue()
33 self.max_chunk = max_chunk
34 self.connected = True
35 self.proto_handler = None
36 self.exiting = False
37
38 def connectionLost(self, reason):
39 log.info('connection-lost')
40 self.connected = False
41 if not self.exiting:
42 self.proto_handler.stop('Connection-Lost')
43
44 def connectionMade(self):
45 log.info('connection-made')
46 self.nc_server.client_connected(self)
47
48 def dataReceived(self, data):
49 log.debug('data-received', len=len(data),
50 received=hexdump(data, result='return'))
51 assert len(data)
52 self.rx.put(data)
53
54 def processEnded(self, reason=None):
55 log.info('process-ended', reason=reason)
56 self.connected = False
57
58 def chunkit(self, msg, maxsend):
59 sz = len(msg)
60 left = 0
61 for unused in range(0, sz // maxsend):
62 right = left + maxsend
63 chunk = msg[left:right]
64 left = right
65 yield chunk
66 msg = msg[left:]
67 yield msg
68
69 def send_msg(self, msg, new_framing):
70 assert self.connected
71 # Apparently ssh has a bug that requires minimum of 64 bytes?
72 # This may not be sufficient to fix this.
73 if new_framing:
Khen Nursimulua7b842a2016-12-03 23:28:42 -050074 msg = "#{}\n{}\n##\n".format(len(msg), msg)
Khen Nursimulu3869d8d2016-11-28 20:44:28 -050075 else:
Khen Nursimulua7b842a2016-12-03 23:28:42 -050076 msg += C.DELIMITER
Khen Nursimulu3869d8d2016-11-28 20:44:28 -050077 for chunk in self.chunkit(msg, self.max_chunk - 64):
Khen Nursimulua7b842a2016-12-03 23:28:42 -050078 log.info('sending', chunk=chunk,
Khen Nursimulu3869d8d2016-11-28 20:44:28 -050079 framing="1.1" if new_framing else "1.0")
80 # out = hexdump(chunk, result='return')
Khen Nursimulua7b842a2016-12-03 23:28:42 -050081 self.transport.write('{}\n'.format(chunk))
Khen Nursimulu3869d8d2016-11-28 20:44:28 -050082
83 @inlineCallbacks
84 def receive_msg_any(self, new_framing):
85 assert self.connected
86 msg = yield self.recv(lambda _: True)
87 if new_framing:
88 returnValue(self._receive_11(msg))
89 else:
90 returnValue(self._receive_10(msg))
91
92 def _receive_10(self, msg):
93 # search for message end indicator
94 searchfrom = 0
Khen Nursimulua7b842a2016-12-03 23:28:42 -050095 eomidx = msg.find(C.DELIMITER, searchfrom)
Khen Nursimulu3869d8d2016-11-28 20:44:28 -050096 if eomidx != -1:
97 log.info('received-msg', msg=msg[:eomidx])
98 return msg[:eomidx]
99 else:
100 log.error('no-message-end-indicators', msg=msg)
101 return msg
102
103 def _receive_11(self, msg):
104 # Message is received in the format "\n#{len}\n{msg}\n##\n"
Khen Nursimulua7b842a2016-12-03 23:28:42 -0500105 # A message may have return characters within it
Khen Nursimulu3869d8d2016-11-28 20:44:28 -0500106 if msg:
Khen Nursimulua7b842a2016-12-03 23:28:42 -0500107 log.info('received-msg-full', msg=msg)
Khen Nursimulu3869d8d2016-11-28 20:44:28 -0500108 msg = msg.split('\n')
109 if len(msg) > 2:
Khen Nursimulua7b842a2016-12-03 23:28:42 -0500110 msg = ''.join(msg[2:(len(msg)-2)])
111 log.info('parsed-msg\n', msg=msg)
112 return msg
Khen Nursimulu3869d8d2016-11-28 20:44:28 -0500113 return None
114
115 def close_connection(self):
116 log.info('closing-connection')
117 self.exiting = True
118 self.transport.loseConnection()
119
120 def recv(self, predicate):
121 assert self.connected
122 return self.rx.get(predicate)
123
124 def recv_any(self, new_framing):
125 return self.recv(lambda _: True)
126
127 def recv_xid(self, xid):
128 return self.recv(lambda msg: msg.xid == xid)