1c7f1593f9af3ea1b9264b37628c36f3a70e1749aMike Frysinger#!/usr/bin/python 2a0c25297ded2a0b9e54bec22b9f544b3170ab0eekbaclawski# 3a0c25297ded2a0b9e54bec22b9f544b3170ab0eekbaclawski# Copyright 2011 Google Inc. All Rights Reserved. 4a0c25297ded2a0b9e54bec22b9f544b3170ab0eekbaclawski# 5a0c25297ded2a0b9e54bec22b9f544b3170ab0eekbaclawski 6a0c25297ded2a0b9e54bec22b9f544b3170ab0eekbaclawski__author__ = 'kbaclawski@google.com (Krystian Baclawski)' 7a0c25297ded2a0b9e54bec22b9f544b3170ab0eekbaclawski 8a0c25297ded2a0b9e54bec22b9f544b3170ab0eekbaclawskiimport cStringIO 9a0c25297ded2a0b9e54bec22b9f544b3170ab0eekbaclawskiimport logging 10a0c25297ded2a0b9e54bec22b9f544b3170ab0eekbaclawskiimport os 11a0c25297ded2a0b9e54bec22b9f544b3170ab0eekbaclawskiimport signal 12a0c25297ded2a0b9e54bec22b9f544b3170ab0eekbaclawskiimport socket 13a0c25297ded2a0b9e54bec22b9f544b3170ab0eekbaclawskiimport sys 14a0c25297ded2a0b9e54bec22b9f544b3170ab0eekbaclawskiimport time 15a0c25297ded2a0b9e54bec22b9f544b3170ab0eekbaclawskiimport unittest 16a0c25297ded2a0b9e54bec22b9f544b3170ab0eekbaclawski 17a0c25297ded2a0b9e54bec22b9f544b3170ab0eekbaclawski 18a0c25297ded2a0b9e54bec22b9f544b3170ab0eekbaclawskidef AddScriptDirToPath(): 19a0c25297ded2a0b9e54bec22b9f544b3170ab0eekbaclawski """Required for remote python script execution.""" 20a0c25297ded2a0b9e54bec22b9f544b3170ab0eekbaclawski path = os.path.abspath(__file__) 21a0c25297ded2a0b9e54bec22b9f544b3170ab0eekbaclawski 22a0c25297ded2a0b9e54bec22b9f544b3170ab0eekbaclawski for _ in range(3): 23a0c25297ded2a0b9e54bec22b9f544b3170ab0eekbaclawski path, _ = os.path.split(path) 24a0c25297ded2a0b9e54bec22b9f544b3170ab0eekbaclawski 25a0c25297ded2a0b9e54bec22b9f544b3170ab0eekbaclawski if not path in sys.path: 26a0c25297ded2a0b9e54bec22b9f544b3170ab0eekbaclawski sys.path.append(path) 27a0c25297ded2a0b9e54bec22b9f544b3170ab0eekbaclawski 28a0c25297ded2a0b9e54bec22b9f544b3170ab0eekbaclawski 29a0c25297ded2a0b9e54bec22b9f544b3170ab0eekbaclawskiAddScriptDirToPath() 30a0c25297ded2a0b9e54bec22b9f544b3170ab0eekbaclawski 31a0c25297ded2a0b9e54bec22b9f544b3170ab0eekbaclawskifrom automation.common.command_executer import CommandExecuter 32a0c25297ded2a0b9e54bec22b9f544b3170ab0eekbaclawski 33a0c25297ded2a0b9e54bec22b9f544b3170ab0eekbaclawski 34a0c25297ded2a0b9e54bec22b9f544b3170ab0eekbaclawskiclass LoggerMock(object): 35f2a3ef46f75d2196a93d3ed27f4d1fcf22b54fbeLuis Lozano 36a0c25297ded2a0b9e54bec22b9f544b3170ab0eekbaclawski def LogCmd(self, cmd, machine='', user=''): 37a0c25297ded2a0b9e54bec22b9f544b3170ab0eekbaclawski if machine: 38a0c25297ded2a0b9e54bec22b9f544b3170ab0eekbaclawski logging.info('[%s] Executing: %s', machine, cmd) 39a0c25297ded2a0b9e54bec22b9f544b3170ab0eekbaclawski else: 40a0c25297ded2a0b9e54bec22b9f544b3170ab0eekbaclawski logging.info('Executing: %s', cmd) 41a0c25297ded2a0b9e54bec22b9f544b3170ab0eekbaclawski 42a0c25297ded2a0b9e54bec22b9f544b3170ab0eekbaclawski def LogError(self, msg): 43a0c25297ded2a0b9e54bec22b9f544b3170ab0eekbaclawski logging.error(msg) 44a0c25297ded2a0b9e54bec22b9f544b3170ab0eekbaclawski 45a0c25297ded2a0b9e54bec22b9f544b3170ab0eekbaclawski def LogWarning(self, msg): 46a0c25297ded2a0b9e54bec22b9f544b3170ab0eekbaclawski logging.warning(msg) 47a0c25297ded2a0b9e54bec22b9f544b3170ab0eekbaclawski 48a0c25297ded2a0b9e54bec22b9f544b3170ab0eekbaclawski def LogOutput(self, msg): 49a0c25297ded2a0b9e54bec22b9f544b3170ab0eekbaclawski logging.info(msg) 50a0c25297ded2a0b9e54bec22b9f544b3170ab0eekbaclawski 51a0c25297ded2a0b9e54bec22b9f544b3170ab0eekbaclawski 52a0c25297ded2a0b9e54bec22b9f544b3170ab0eekbaclawskiclass CommandExecuterUnderTest(CommandExecuter): 53f2a3ef46f75d2196a93d3ed27f4d1fcf22b54fbeLuis Lozano 54a0c25297ded2a0b9e54bec22b9f544b3170ab0eekbaclawski def __init__(self): 55a0c25297ded2a0b9e54bec22b9f544b3170ab0eekbaclawski CommandExecuter.__init__(self, logger_to_set=LoggerMock()) 56a0c25297ded2a0b9e54bec22b9f544b3170ab0eekbaclawski 57a0c25297ded2a0b9e54bec22b9f544b3170ab0eekbaclawski # We will record stdout and stderr. 58a0c25297ded2a0b9e54bec22b9f544b3170ab0eekbaclawski self._stderr = cStringIO.StringIO() 59a0c25297ded2a0b9e54bec22b9f544b3170ab0eekbaclawski self._stdout = cStringIO.StringIO() 60a0c25297ded2a0b9e54bec22b9f544b3170ab0eekbaclawski 61a0c25297ded2a0b9e54bec22b9f544b3170ab0eekbaclawski @property 62a0c25297ded2a0b9e54bec22b9f544b3170ab0eekbaclawski def stdout(self): 63a0c25297ded2a0b9e54bec22b9f544b3170ab0eekbaclawski return self._stdout.getvalue() 64a0c25297ded2a0b9e54bec22b9f544b3170ab0eekbaclawski 65a0c25297ded2a0b9e54bec22b9f544b3170ab0eekbaclawski @property 66a0c25297ded2a0b9e54bec22b9f544b3170ab0eekbaclawski def stderr(self): 67a0c25297ded2a0b9e54bec22b9f544b3170ab0eekbaclawski return self._stderr.getvalue() 68a0c25297ded2a0b9e54bec22b9f544b3170ab0eekbaclawski 69a0c25297ded2a0b9e54bec22b9f544b3170ab0eekbaclawski def DataReceivedOnOutput(self, data): 70a0c25297ded2a0b9e54bec22b9f544b3170ab0eekbaclawski self._stdout.write(data) 71a0c25297ded2a0b9e54bec22b9f544b3170ab0eekbaclawski 72a0c25297ded2a0b9e54bec22b9f544b3170ab0eekbaclawski def DataReceivedOnError(self, data): 73a0c25297ded2a0b9e54bec22b9f544b3170ab0eekbaclawski self._stderr.write(data) 74a0c25297ded2a0b9e54bec22b9f544b3170ab0eekbaclawski 75a0c25297ded2a0b9e54bec22b9f544b3170ab0eekbaclawski 76a0c25297ded2a0b9e54bec22b9f544b3170ab0eekbaclawskiclass CommandExecuterLocalTests(unittest.TestCase): 77a0c25297ded2a0b9e54bec22b9f544b3170ab0eekbaclawski HOSTNAME = None 78a0c25297ded2a0b9e54bec22b9f544b3170ab0eekbaclawski 79a0c25297ded2a0b9e54bec22b9f544b3170ab0eekbaclawski def setUp(self): 80a0c25297ded2a0b9e54bec22b9f544b3170ab0eekbaclawski self._executer = CommandExecuterUnderTest() 81a0c25297ded2a0b9e54bec22b9f544b3170ab0eekbaclawski 82a0c25297ded2a0b9e54bec22b9f544b3170ab0eekbaclawski def tearDown(self): 83a0c25297ded2a0b9e54bec22b9f544b3170ab0eekbaclawski pass 84a0c25297ded2a0b9e54bec22b9f544b3170ab0eekbaclawski 85a0c25297ded2a0b9e54bec22b9f544b3170ab0eekbaclawski def RunCommand(self, method, **kwargs): 86a0c25297ded2a0b9e54bec22b9f544b3170ab0eekbaclawski program = os.path.abspath(sys.argv[0]) 87a0c25297ded2a0b9e54bec22b9f544b3170ab0eekbaclawski 88f2a3ef46f75d2196a93d3ed27f4d1fcf22b54fbeLuis Lozano return self._executer.RunCommand('%s runHelper %s' % (program, method), 89f2a3ef46f75d2196a93d3ed27f4d1fcf22b54fbeLuis Lozano machine=self.HOSTNAME, 90f2a3ef46f75d2196a93d3ed27f4d1fcf22b54fbeLuis Lozano **kwargs) 91a0c25297ded2a0b9e54bec22b9f544b3170ab0eekbaclawski 92a0c25297ded2a0b9e54bec22b9f544b3170ab0eekbaclawski def testCommandTimeout(self): 93a0c25297ded2a0b9e54bec22b9f544b3170ab0eekbaclawski exit_code = self.RunCommand('SleepForMinute', command_timeout=3) 94a0c25297ded2a0b9e54bec22b9f544b3170ab0eekbaclawski 95a0c25297ded2a0b9e54bec22b9f544b3170ab0eekbaclawski self.assertTrue(-exit_code in [signal.SIGTERM, signal.SIGKILL], 96a0c25297ded2a0b9e54bec22b9f544b3170ab0eekbaclawski 'Invalid exit code: %d' % exit_code) 97a0c25297ded2a0b9e54bec22b9f544b3170ab0eekbaclawski 98a0c25297ded2a0b9e54bec22b9f544b3170ab0eekbaclawski def testCommandTimeoutIfSigTermIgnored(self): 99a0c25297ded2a0b9e54bec22b9f544b3170ab0eekbaclawski exit_code = self.RunCommand('IgnoreSigTerm', command_timeout=3) 100a0c25297ded2a0b9e54bec22b9f544b3170ab0eekbaclawski 101a0c25297ded2a0b9e54bec22b9f544b3170ab0eekbaclawski self.assertTrue(-exit_code in [signal.SIGTERM, signal.SIGKILL]) 102a0c25297ded2a0b9e54bec22b9f544b3170ab0eekbaclawski 103a0c25297ded2a0b9e54bec22b9f544b3170ab0eekbaclawski def testCommandSucceeded(self): 104a0c25297ded2a0b9e54bec22b9f544b3170ab0eekbaclawski self.assertFalse(self.RunCommand('ReturnTrue')) 105a0c25297ded2a0b9e54bec22b9f544b3170ab0eekbaclawski 106a0c25297ded2a0b9e54bec22b9f544b3170ab0eekbaclawski def testCommandFailed(self): 107a0c25297ded2a0b9e54bec22b9f544b3170ab0eekbaclawski self.assertTrue(self.RunCommand('ReturnFalse')) 108a0c25297ded2a0b9e54bec22b9f544b3170ab0eekbaclawski 109a0c25297ded2a0b9e54bec22b9f544b3170ab0eekbaclawski def testStringOnOutputStream(self): 110a0c25297ded2a0b9e54bec22b9f544b3170ab0eekbaclawski self.assertFalse(self.RunCommand('EchoToOutputStream')) 111a0c25297ded2a0b9e54bec22b9f544b3170ab0eekbaclawski self.assertEquals(self._executer.stderr, '') 112a0c25297ded2a0b9e54bec22b9f544b3170ab0eekbaclawski self.assertEquals(self._executer.stdout, 'test') 113a0c25297ded2a0b9e54bec22b9f544b3170ab0eekbaclawski 114a0c25297ded2a0b9e54bec22b9f544b3170ab0eekbaclawski def testStringOnErrorStream(self): 115a0c25297ded2a0b9e54bec22b9f544b3170ab0eekbaclawski self.assertFalse(self.RunCommand('EchoToErrorStream')) 116a0c25297ded2a0b9e54bec22b9f544b3170ab0eekbaclawski self.assertEquals(self._executer.stderr, 'test') 117a0c25297ded2a0b9e54bec22b9f544b3170ab0eekbaclawski self.assertEquals(self._executer.stdout, '') 118a0c25297ded2a0b9e54bec22b9f544b3170ab0eekbaclawski 119a0c25297ded2a0b9e54bec22b9f544b3170ab0eekbaclawski def testOutputStreamNonInteractive(self): 120f2a3ef46f75d2196a93d3ed27f4d1fcf22b54fbeLuis Lozano self.assertFalse( 121f2a3ef46f75d2196a93d3ed27f4d1fcf22b54fbeLuis Lozano self.RunCommand('IsOutputStreamInteractive'), 122f2a3ef46f75d2196a93d3ed27f4d1fcf22b54fbeLuis Lozano 'stdout stream is a terminal!') 123a0c25297ded2a0b9e54bec22b9f544b3170ab0eekbaclawski 124a0c25297ded2a0b9e54bec22b9f544b3170ab0eekbaclawski def testErrorStreamNonInteractive(self): 125f2a3ef46f75d2196a93d3ed27f4d1fcf22b54fbeLuis Lozano self.assertFalse( 126f2a3ef46f75d2196a93d3ed27f4d1fcf22b54fbeLuis Lozano self.RunCommand('IsErrorStreamInteractive'), 127f2a3ef46f75d2196a93d3ed27f4d1fcf22b54fbeLuis Lozano 'stderr stream is a terminal!') 128a0c25297ded2a0b9e54bec22b9f544b3170ab0eekbaclawski 129a0c25297ded2a0b9e54bec22b9f544b3170ab0eekbaclawski def testAttemptToRead(self): 130a0c25297ded2a0b9e54bec22b9f544b3170ab0eekbaclawski self.assertFalse(self.RunCommand('WaitForInput', command_timeout=3)) 131a0c25297ded2a0b9e54bec22b9f544b3170ab0eekbaclawski 132a0c25297ded2a0b9e54bec22b9f544b3170ab0eekbaclawski def testInterruptedProcess(self): 133a0c25297ded2a0b9e54bec22b9f544b3170ab0eekbaclawski self.assertEquals(self.RunCommand('TerminateBySigAbrt'), -signal.SIGABRT) 134a0c25297ded2a0b9e54bec22b9f544b3170ab0eekbaclawski 135a0c25297ded2a0b9e54bec22b9f544b3170ab0eekbaclawski 136a0c25297ded2a0b9e54bec22b9f544b3170ab0eekbaclawskiclass CommandExecuterRemoteTests(CommandExecuterLocalTests): 137a0c25297ded2a0b9e54bec22b9f544b3170ab0eekbaclawski HOSTNAME = socket.gethostname() 138a0c25297ded2a0b9e54bec22b9f544b3170ab0eekbaclawski 139a0c25297ded2a0b9e54bec22b9f544b3170ab0eekbaclawski def testCommandTimeoutIfSigTermIgnored(self): 140a0c25297ded2a0b9e54bec22b9f544b3170ab0eekbaclawski exit_code = self.RunCommand('IgnoreSigTerm', command_timeout=6) 141a0c25297ded2a0b9e54bec22b9f544b3170ab0eekbaclawski 142a0c25297ded2a0b9e54bec22b9f544b3170ab0eekbaclawski self.assertEquals(exit_code, 255) 143a0c25297ded2a0b9e54bec22b9f544b3170ab0eekbaclawski 144a0c25297ded2a0b9e54bec22b9f544b3170ab0eekbaclawski lines = self._executer.stdout.splitlines() 145a0c25297ded2a0b9e54bec22b9f544b3170ab0eekbaclawski pid = int(lines[0]) 146a0c25297ded2a0b9e54bec22b9f544b3170ab0eekbaclawski 147a0c25297ded2a0b9e54bec22b9f544b3170ab0eekbaclawski try: 148a0c25297ded2a0b9e54bec22b9f544b3170ab0eekbaclawski with open('/proc/%d/cmdline' % pid) as f: 149a0c25297ded2a0b9e54bec22b9f544b3170ab0eekbaclawski cmdline = f.read() 150a0c25297ded2a0b9e54bec22b9f544b3170ab0eekbaclawski except IOError: 151a0c25297ded2a0b9e54bec22b9f544b3170ab0eekbaclawski cmdline = '' 152a0c25297ded2a0b9e54bec22b9f544b3170ab0eekbaclawski 153a0c25297ded2a0b9e54bec22b9f544b3170ab0eekbaclawski self.assertFalse('IgnoreSigTerm' in cmdline, 'Process is still alive.') 154a0c25297ded2a0b9e54bec22b9f544b3170ab0eekbaclawski 155a0c25297ded2a0b9e54bec22b9f544b3170ab0eekbaclawski 156a0c25297ded2a0b9e54bec22b9f544b3170ab0eekbaclawskiclass CommandExecuterTestHelpers(object): 157f2a3ef46f75d2196a93d3ed27f4d1fcf22b54fbeLuis Lozano 158a0c25297ded2a0b9e54bec22b9f544b3170ab0eekbaclawski def SleepForMinute(self): 159a0c25297ded2a0b9e54bec22b9f544b3170ab0eekbaclawski time.sleep(60) 160a0c25297ded2a0b9e54bec22b9f544b3170ab0eekbaclawski return 1 161a0c25297ded2a0b9e54bec22b9f544b3170ab0eekbaclawski 162a0c25297ded2a0b9e54bec22b9f544b3170ab0eekbaclawski def ReturnTrue(self): 163a0c25297ded2a0b9e54bec22b9f544b3170ab0eekbaclawski return 0 164a0c25297ded2a0b9e54bec22b9f544b3170ab0eekbaclawski 165a0c25297ded2a0b9e54bec22b9f544b3170ab0eekbaclawski def ReturnFalse(self): 166a0c25297ded2a0b9e54bec22b9f544b3170ab0eekbaclawski return 1 167a0c25297ded2a0b9e54bec22b9f544b3170ab0eekbaclawski 168a0c25297ded2a0b9e54bec22b9f544b3170ab0eekbaclawski def EchoToOutputStream(self): 169a0c25297ded2a0b9e54bec22b9f544b3170ab0eekbaclawski sys.stdout.write('test') 170a0c25297ded2a0b9e54bec22b9f544b3170ab0eekbaclawski return 0 171a0c25297ded2a0b9e54bec22b9f544b3170ab0eekbaclawski 172a0c25297ded2a0b9e54bec22b9f544b3170ab0eekbaclawski def EchoToErrorStream(self): 173a0c25297ded2a0b9e54bec22b9f544b3170ab0eekbaclawski sys.stderr.write('test') 174a0c25297ded2a0b9e54bec22b9f544b3170ab0eekbaclawski return 0 175a0c25297ded2a0b9e54bec22b9f544b3170ab0eekbaclawski 176a0c25297ded2a0b9e54bec22b9f544b3170ab0eekbaclawski def IsOutputStreamInteractive(self): 177a0c25297ded2a0b9e54bec22b9f544b3170ab0eekbaclawski return sys.stdout.isatty() 178a0c25297ded2a0b9e54bec22b9f544b3170ab0eekbaclawski 179a0c25297ded2a0b9e54bec22b9f544b3170ab0eekbaclawski def IsErrorStreamInteractive(self): 180a0c25297ded2a0b9e54bec22b9f544b3170ab0eekbaclawski return sys.stderr.isatty() 181a0c25297ded2a0b9e54bec22b9f544b3170ab0eekbaclawski 182a0c25297ded2a0b9e54bec22b9f544b3170ab0eekbaclawski def IgnoreSigTerm(self): 183a0c25297ded2a0b9e54bec22b9f544b3170ab0eekbaclawski os.write(1, '%d' % os.getpid()) 184a0c25297ded2a0b9e54bec22b9f544b3170ab0eekbaclawski signal.signal(signal.SIGTERM, signal.SIG_IGN) 185a0c25297ded2a0b9e54bec22b9f544b3170ab0eekbaclawski time.sleep(30) 186a0c25297ded2a0b9e54bec22b9f544b3170ab0eekbaclawski return 0 187a0c25297ded2a0b9e54bec22b9f544b3170ab0eekbaclawski 188a0c25297ded2a0b9e54bec22b9f544b3170ab0eekbaclawski def WaitForInput(self): 189a0c25297ded2a0b9e54bec22b9f544b3170ab0eekbaclawski try: 190a0c25297ded2a0b9e54bec22b9f544b3170ab0eekbaclawski # can only read end-of-file marker 191a0c25297ded2a0b9e54bec22b9f544b3170ab0eekbaclawski return os.read(0, 1) != '' 192a0c25297ded2a0b9e54bec22b9f544b3170ab0eekbaclawski except OSError: 193a0c25297ded2a0b9e54bec22b9f544b3170ab0eekbaclawski # that means that stdin descriptor is closed 194a0c25297ded2a0b9e54bec22b9f544b3170ab0eekbaclawski return 0 195a0c25297ded2a0b9e54bec22b9f544b3170ab0eekbaclawski 196a0c25297ded2a0b9e54bec22b9f544b3170ab0eekbaclawski def TerminateBySigAbrt(self): 197a0c25297ded2a0b9e54bec22b9f544b3170ab0eekbaclawski os.kill(os.getpid(), signal.SIGABRT) 198a0c25297ded2a0b9e54bec22b9f544b3170ab0eekbaclawski return 0 199a0c25297ded2a0b9e54bec22b9f544b3170ab0eekbaclawski 200a0c25297ded2a0b9e54bec22b9f544b3170ab0eekbaclawski 201a0c25297ded2a0b9e54bec22b9f544b3170ab0eekbaclawskiif __name__ == '__main__': 202a0c25297ded2a0b9e54bec22b9f544b3170ab0eekbaclawski FORMAT = '%(asctime)-15s %(levelname)s %(message)s' 203a0c25297ded2a0b9e54bec22b9f544b3170ab0eekbaclawski logging.basicConfig(format=FORMAT, level=logging.DEBUG) 204a0c25297ded2a0b9e54bec22b9f544b3170ab0eekbaclawski 205a0c25297ded2a0b9e54bec22b9f544b3170ab0eekbaclawski if len(sys.argv) > 1: 206a0c25297ded2a0b9e54bec22b9f544b3170ab0eekbaclawski if sys.argv[1] == 'runHelper': 207a0c25297ded2a0b9e54bec22b9f544b3170ab0eekbaclawski helpers = CommandExecuterTestHelpers() 208a0c25297ded2a0b9e54bec22b9f544b3170ab0eekbaclawski sys.exit(getattr(helpers, sys.argv[2])()) 209a0c25297ded2a0b9e54bec22b9f544b3170ab0eekbaclawski 210a0c25297ded2a0b9e54bec22b9f544b3170ab0eekbaclawski unittest.main() 211