ia64/xen-unstable

view tools/xm-test/lib/XmTestLib/Console.py @ 9786:a22ce69dd703

Fix the 15_create_smallmem_pos.py test, which was failing because the
set console.limit command in the test was never being run. The select in
Console.py was never timing out because there was always someting to read on
the fd, the OOM messages are constant. So the test would hang.

The fix includes:

1) Changing MEM in 15_create_smallmem_pos.py to 32MBs, which is the default
for the tools that should work.
2) Change the XmConsole init to add an argument to set the console limit
when it's created.
3) Set a default large limit for console so we won't hang in the future.
4) Added a new 16_create_smallmem_neg.py test to handle failure situation.
5) Added comment in README.

Signed-off-by: Daniel Stekloff <dsteklof@us.ibm.com>
author stekloff@dyn9047022152.beaverton.ibm.com
date Wed Apr 19 22:58:03 2006 +0100 (2006-04-19)
parents b67f9f21fd9c
children bef7f5fcf207
line source
1 #!/usr/bin/python
2 """
3 XmConsole.py - Interact with a xen console, getting return codes and
4 output from commands executed there.
6 Copyright (C) International Business Machines Corp., 2005
7 Author: Dan Smith <danms@us.ibm.com>
9 This program is free software; you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation; under version 2 of the License.
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
18 You should have received a copy of the GNU General Public License
19 along with this program; if not, write to the Free Software
20 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
22 NB: This requires the domU's prompt to be set to
23 a _very_ specific value, set in the PROMPT
24 variable of this script
25 """
26 import sys
27 import os
28 import pty
29 import tty
30 import termios
31 import fcntl
32 import select
34 from Test import *
36 TIMEDOUT = 1
37 RUNAWAY = 2
39 class ConsoleError(Exception):
40 def __init__(self, msg, reason=TIMEDOUT):
41 self.errMsg = msg
42 self.reason = reason
44 def __str__(self):
45 return str(self.errMsg)
47 class XmConsole:
49 def __init__(self, domain, historyLimit=256, historySaveAll=True, historySaveCmds=False, cLimit=131072):
50 """
51 Parameters:
52 historyLimit: specifies how many lines of history are maintained
53 historySaveAll: determines whether or not extra messages in
54 between commands are saved
55 historySaveCmds : determines whether or not the command echos
56 are included in the history buffer
57 """
59 self.TIMEOUT = 30
60 self.PROMPT = "@%@%> "
61 self.domain = domain
62 self.historyBuffer = []
63 self.historyLines = 0
64 self.historyLimit = historyLimit
65 self.historySaveAll = historySaveAll
66 self.historySaveCmds = historySaveCmds
67 self.debugMe = False
68 self.limit = cLimit
70 consoleCmd = ["/usr/sbin/xm", "xm", "console", domain]
72 if verbose:
73 print "Console executing: %s" % str(consoleCmd)
75 pid, fd = pty.fork()
77 if pid == 0:
78 os.execvp("/usr/sbin/xm", consoleCmd[1:])
80 self.consolePid = pid
81 self.consoleFd = fd
83 tty.setraw(self.consoleFd, termios.TCSANOW)
85 self.__chewall(self.consoleFd)
88 def __addToHistory(self, line):
89 self.historyBuffer.append(line)
90 self.historyLines += 1
91 if self.historyLines > self.historyLimit:
92 self.historyBuffer = self.historyBuffer[1:]
93 self.historyLines -= 1
96 def clearHistory(self):
97 """Clear the history buffer"""
98 self.historyBuffer = []
99 self.historyLines = 0
102 def getHistory(self):
103 """Returns a string containing the entire history buffer"""
104 output = ""
106 for line in self.historyBuffer:
107 output += line + "\n"
109 return output
112 def setTimeout(self, timeout):
113 """Sets the timeout used to determine if a remote command
114 has blocked"""
115 self.TIMEOUT = timeout
118 def setPrompt(self, prompt):
119 """Sets the string key used to delimit the end of command
120 output"""
121 self.PROMPT = prompt
124 def __chewall(self, fd):
125 timeout = 0
126 bytes = 0
128 while timeout < 3:
129 i, o, e = select.select([fd], [], [], 1)
130 if fd in i:
131 try:
132 foo = os.read(fd, 1)
133 if self.debugMe:
134 sys.stdout.write(foo)
135 bytes += 1
136 except Exception, exn:
137 raise ConsoleError(str(exn))
139 else:
140 timeout += 1
142 if self.limit and bytes >= self.limit:
143 raise ConsoleError("Console run-away (exceeded %i bytes)"
144 % self.limit, RUNAWAY)
146 if self.debugMe:
147 print "Ignored %i bytes of miscellaneous console output" % bytes
149 return bytes
152 def __runCmd(self, command, saveHistory=True):
153 output = ""
154 line = ""
155 lines = 0
156 bytes = 0
158 self.__chewall(self.consoleFd)
160 if verbose:
161 print "[%s] Sending `%s'" % (self.domain, command)
163 os.write(self.consoleFd, "%s\n" % command)
165 while True:
166 i, o, e = select.select([self.consoleFd], [], [], self.TIMEOUT)
168 if self.consoleFd in i:
169 try:
170 str = os.read(self.consoleFd, 1)
171 if self.debugMe:
172 sys.stdout.write(str)
173 bytes += 1
174 except Exception, exn:
175 raise ConsoleError(
176 "Failed to read from console (fd=%i): %s" %
177 (self.consoleFd, exn))
178 else:
179 raise ConsoleError("Timed out waiting for console")
181 if self.limit and bytes >= self.limit:
182 raise ConsoleError("Console run-away (exceeded %i bytes)"
183 % self.limit, RUNAWAY)
185 if str == "\n":
186 if lines > 0:
187 output += line + "\n"
188 if saveHistory:
189 self.__addToHistory(line)
190 elif self.historySaveCmds and saveHistory:
191 self.__addToHistory("*" + line)
192 lines += 1
193 line = ""
194 elif str == "\r":
195 pass # ignore \r's
196 else:
197 line += str
199 if line == self.PROMPT:
200 break
202 return output
205 def runCmd(self, command):
206 """Runs a command on the remote terminal and returns the output
207 as well as the return code. For example:
209 ret = c.runCmd("ls")
210 print ret["output"]
211 sys.exit(run["return"])
213 """
215 # Allow exceptions to bubble up
216 realOutput = self.__runCmd(command)
217 retOutput = self.__runCmd("echo $?", saveHistory=False)
219 try:
220 retCode = int(retOutput)
221 except:
222 retCode = 255
223 return {
224 "output": realOutput,
225 "return": retCode,
226 }
228 def sendInput(self, input):
229 """Sends input to the remote terminal, but doesn't check
230 for a return code"""
231 realOutput = self.__runCmd(input)
232 return {
233 "output": realOutput,
234 "return": 0,
235 }
237 def closeConsole(self):
238 """Closes the console connection and ensures that the console
239 process is killed"""
240 os.close(self.consoleFd)
241 os.kill(self.consolePid, 2)
244 def setLimit(self, limit):
245 """Sets a limit on the number of bytes that can be
246 read in an attempt to run a command. We need this when
247 running something that can run away"""
248 try:
249 self.limit = int(limit)
250 except Exception, e:
251 self.limit = None
254 if __name__ == "__main__":
255 """This is both an example of using the XmConsole class, as
256 well as a utility for command-line execution of single commands
257 on a domU console. Prints output to stdout. Exits with the same
258 code as the domU command.
259 """
261 verbose = True
263 try:
264 t = XmConsole(sys.argv[1])
265 except ConsoleError, e:
266 print "Failed to attach to console (%s)" % str(e)
267 sys.exit(255)
269 try:
270 run = t.runCmd(sys.argv[2])
271 except ConsoleError, e:
272 print "Console failed (%)" % str(e)
273 sys.exit(255)
275 t.closeConsole()
277 print run["output"],
278 sys.exit(run["return"])