blob: 33b9ab1fef25e734a8449402a50c29b9c696e16f [file] [log] [blame]
Kata7185c62013-04-04 17:31:13 +02001# Copyright (C) 2012 Holger Hans Peter Freyther
2# Copyright (C) 2013 Katerina Barone-Adesi
3# This program is free software: you can redistribute it and/or modify
4# it under the terms of the GNU General Public License as published by
5# the Free Software Foundation, either version 2 of the License, or
6# (at your option) any later version.
7#
8# This program is distributed in the hope that it will be useful,
9# but WITHOUT ANY WARRANTY; without even the implied warranty of
10# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11# GNU General Public License for more details.
12#
13# You should have received a copy of the GNU General Public License
14# along with this program. If not, see <http://www.gnu.org/licenses/>.
15
16#
17# VTY helper code for OpenBSC
18#
19import socket
20
Kata8ee6bb2013-04-05 17:06:30 +020021"""VTYInteract: interact with an osmocom vty
22
23Specify a VTY to connect to, and run commands on it.
24Connections will be reestablished as necessary.
25Methods: __init__, command, enabled_command, verify, w_verify"""
26
Kata7185c62013-04-04 17:31:13 +020027
28class VTYInteract(object):
Kata8ee6bb2013-04-05 17:06:30 +020029 """__init__(self, name, host, port):
30
31 name is the name the vty prints for commands, ie OpenBSC
32 host is the hostname to connect to
33 port is the port to connect on"""
Kata7185c62013-04-04 17:31:13 +020034 def __init__(self, name, host, port):
35 self.name = name
36 self.host = host
37 self.port = port
38
39 self.socket = None
40 self.norm_end = '\r\n%s> ' % self.name
41 self.priv_end = '\r\n%s# ' % self.name
42
43 def _close_socket(self):
44 self.socket.close()
45 self.socket = None
46
47 def _is_end(self, text, ends):
48 for end in ends:
49 if text.endswith(end):
50 return end
51 return ""
52
53 def _common_command(self, request, close=False, ends=None):
54 if not ends:
55 ends = [self.norm_end, self.priv_end]
56 if not self.socket:
57 self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
58 self.socket.setblocking(1)
59 self.socket.connect((self.host, self.port))
60 self.socket.recv(4096)
61
62 # Now send the command
63 self.socket.send("%s\r" % request)
64 res = ""
65 end = ""
66
67 # Unfortunately, timeout and recv don't always play nicely
68 while True:
69 data = self.socket.recv(4096)
70 res = "%s%s" % (res, data)
71 if not res: # yes, this is ugly
72 raise IOError("Failed to read data (did the app crash?)")
73 end = self._is_end(res, ends)
74 if end:
75 break
76
77 if close:
78 self._close_socket()
79 return res[len(request) + 2: -len(end)]
80
81 # There's no close parameter, as close=True makes this useless
82 def enable(self):
83 self.command("enable")
84
85 """Run a command on the vty"""
Kata8ee6bb2013-04-05 17:06:30 +020086
Kata7185c62013-04-04 17:31:13 +020087 def command(self, request, close=False):
88 return self._common_command(request, close)
89
90 """Run enable, followed by another command"""
91 def enabled_command(self, request, close=False):
92 self.enable()
93 return self._common_command(request, close)
94
95 """Verify, ignoring leading/trailing whitespace"""
96 # inspired by diff -w, though not identical
97 def w_verify(self, command, results, close=False, loud=True):
98 return self.verify(command, results, close, loud, lambda x: x.strip())
99
100 """Verify that a command has the expected results
101
102 command = the command to verify
103 results = the expected results [line1, line2, ...]
104 close = True to close the socket after running the verify
105 loud = True to show what was expected and what actually happend, stdout
106 f = A function to run over the expected and actual results, before compare
107
108 Returns True iff the expected and actual results match"""
109 def verify(self, command, results, close=False, loud=True, f=None):
110 res = self.command(command, close).split('\r\n')
111 if f:
112 res = map(f, res)
113 results = map(f, results)
114
115 if loud:
116 if res != results:
117 print "Rec: %s\nExp: %s" % (res, results)
118
119 return res == results