ia64/xen-unstable

view tools/xm-test/lib/XmTestLib/Console.py @ 7757:139aabe357a4

Various chmod a+x.

Signed-off-by: Ewan Mellor <ewan@xensource.com>
author emellor@leeni.uk.xensource.com
date Thu Nov 10 12:00:09 2005 +0100 (2005-11-10)
parents 3920df5da25e
children 4bdcb7f8c3d7
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 class ConsoleError(Exception):
37 def __init__(self, msg):
38 self.errMsg = msg
40 def __str__(self):
41 return str(self.errMsg)
43 class XmConsole:
45 def __init__(self, domain, historyLimit=256, historySaveAll=True, historySaveCmds=False):
46 """
47 Parameters:
48 historyLimit: specifies how many lines of history are maintained
49 historySaveAll: determines whether or not extra messages in
50 between commands are saved
51 historySaveCmds : determines whether or not the command echos
52 are included in the history buffer
53 """
55 self.TIMEOUT = 30
56 self.PROMPT = "@%@%> "
57 self.domain = domain
58 self.historyBuffer = []
59 self.historyLines = 0
60 self.historyLimit = historyLimit
61 self.historySaveAll = historySaveAll
62 self.historySaveCmds = historySaveCmds
63 self.debugMe = False
64 self.limit = None
66 consoleCmd = ["/usr/sbin/xm", "xm", "console", domain]
68 if verbose:
69 print "Console executing: " + str(consoleCmd)
71 pid, fd = pty.fork()
73 if pid == 0:
74 os.execvp("/usr/sbin/xm", consoleCmd[1:])
76 self.consolePid = pid
77 self.consoleFd = fd
79 tty.setraw(self.consoleFd, termios.TCSANOW)
81 bytes = self.__chewall(self.consoleFd)
82 if bytes < 0:
83 raise ConsoleError("Console didn't respond")
85 def __addToHistory(self, line):
86 self.historyBuffer.append(line)
87 self.historyLines += 1
88 if self.historyLines > self.historyLimit:
89 self.historyBuffer = self.historyBuffer[1:]
90 self.historyLines -= 1
93 def clearHistory(self):
94 """Clear the history buffer"""
95 self.historyBuffer = []
96 self.historyLines = 0
99 def getHistory(self):
100 """Returns a string containing the entire history buffer"""
101 output = ""
103 for line in self.historyBuffer:
104 output += line + "\n"
106 return output
109 def setTimeout(self, timeout):
110 """Sets the timeout used to determine if a remote command
111 has blocked"""
112 self.TIMEOUT = timeout
115 def setPrompt(self, prompt):
116 """Sets the string key used to delimit the end of command
117 output"""
118 self.PROMPT = prompt
121 def __chewall(self, fd):
122 timeout = 0
123 bytes = 0
125 while timeout < 3:
126 i, o, e = select.select([fd], [], [], 1)
127 if fd in i:
128 try:
129 foo = os.read(fd, 1)
130 if self.debugMe:
131 sys.stdout.write(foo)
132 bytes += 1
133 except:
134 timeout += 1
136 else:
137 timeout += 1
139 if self.limit and bytes >= self.limit:
140 raise ConsoleError("Console run-away (exceeded %i bytes)"
141 % self.limit)
143 if self.debugMe:
144 print "Ignored %i bytes of miscellaneous console output" % bytes
146 return bytes
149 def __runCmd(self, command, saveHistory=True):
150 output = ""
151 line = ""
152 lines = 0
153 bytes = 0
155 self.__chewall(self.consoleFd)
157 if verbose:
158 print "[%s] Sending `%s'" % (self.domain, command)
160 os.write(self.consoleFd, "%s\n" % command)
162 while 1==1:
163 i, o, e = select.select([self.consoleFd], [], [], self.TIMEOUT)
165 if self.consoleFd in i:
166 try:
167 str = os.read(self.consoleFd, 1)
168 if self.debugMe:
169 sys.stdout.write(str)
170 bytes += 1
171 except:
172 raise ConsoleError("Failed to read from console (fd=%i)"
173 % self.consoleFd)
174 else:
175 raise ConsoleError("Timed out waiting for console")
177 if self.limit and bytes >= self.limit:
178 raise ConsoleError("Console run-away (exceeded %i bytes)"
179 % self.limit)
181 if str == "\n":
182 if lines > 0:
183 output += line + "\n"
184 if saveHistory:
185 self.__addToHistory(line)
186 elif self.historySaveCmds and saveHistory:
187 self.__addToHistory("*" + line)
188 lines += 1
189 line = ""
190 elif str == "\r":
191 pass # ignore \r's
192 else:
193 line += str
195 if line == self.PROMPT:
196 break
198 return output
201 def runCmd(self, command):
202 """Runs a command on the remote terminal and returns the output
203 as well as the return code. For example:
205 ret = c.runCmd("ls")
206 print ret["output"]
207 sys.exit(run["return"])
209 """
211 # Allow exceptions to bubble up
212 realOutput = self.__runCmd(command)
213 retOutput = self.__runCmd("echo $?", saveHistory=False)
215 try:
216 retCode = int(retOutput)
217 except:
218 retCode = 255
219 return {
220 "output": realOutput,
221 "return": retCode,
222 }
224 def sendInput(self, input):
225 """Sends input to the remote terminal, but doesn't check
226 for a return code"""
227 realOutput = self.__runCmd(input)
228 return {
229 "output": realOutput,
230 "return": 0,
231 }
233 def closeConsole(self):
234 """Closes the console connection and ensures that the console
235 process is killed"""
236 os.close(self.consoleFd)
237 os.kill(self.consolePid, 2)
240 def setLimit(self, limit):
241 """Sets a limit on the number of bytes that can be
242 read in an attempt to run a command. We need this when
243 running something that can run away"""
244 try:
245 self.limit = int(limit)
246 except Exception, e:
247 self.limit = None
250 if __name__ == "__main__":
251 """This is both an example of using the XmConsole class, as
252 well as a utility for command-line execution of single commands
253 on a domU console. Prints output to stdout. Exits with the same
254 code as the domU command.
255 """
257 verbose = True
259 try:
260 t = XmConsole(sys.argv[1])
261 except ConsoleError, e:
262 print "Failed to attach to console (%s)" % str(e)
263 sys.exit(255)
265 try:
266 run = t.runCmd(sys.argv[2])
267 except ConsoleError, e:
268 print "Console failed (%)" % str(e)
269 sys.exit(255)
271 t.closeConsole()
273 print run["output"],
274 sys.exit(run["return"])