Bug 611348 - adding functionality to devicemanager.py to handle slave rebooting r=jmaher a=NPOTB

This commit is contained in:
Clint Talbert 2010-11-18 13:03:44 -08:00
parent a792acda7d
commit 663acccd84
2 changed files with 182 additions and 41 deletions

View File

@ -37,6 +37,7 @@
# ***** END LICENSE BLOCK *****
import socket
import SocketServer
import time, datetime
import os
import re
@ -58,14 +59,14 @@ class FileError(Exception):
class DeviceManager:
host = ''
port = 0
debug = 2
debug = 2
_redo = False
deviceRoot = None
tempRoot = os.getcwd()
base_prompt = '\$\>'
prompt_sep = '\x00'
prompt_regex = '.*' + base_prompt + prompt_sep
agentErrorRE = re.compile('^##AGENT-ERROR##.*')
agentErrorRE = re.compile('^##AGENT-WARNING##.*')
def __init__(self, host, port = 20701):
self.host = host
@ -272,7 +273,10 @@ class DeviceManager:
return None
def mkDir(self, name):
return self.sendCMD(['mkdr ' + name])
if (self.dirExists(name)):
return name
else:
return self.sendCMD(['mkdr ' + name])
# make directory structure on the device
def mkDirs(self, filename):
@ -306,7 +310,9 @@ class DeviceManager:
match = ".*" + dirname + "$"
dirre = re.compile(match)
data = self.sendCMD(['cd ' + dirname, 'cwd'])
if (data == None):
# Because this is a compound command, cd can fail while cwd can succeed,
# we should check for agent error directly
if (data == None or self.agentErrorRE.match(data) ):
return None
retVal = self.stripPrompt(data)
data = retVal.split('\n')
@ -549,11 +555,12 @@ class DeviceManager:
# /reftest
# /mochitest
def getDeviceRoot(self):
if (not self.deviceRoot):
data = self.sendCMD(['testroot'])
if (data == None):
return '/tests'
self.deviceRoot = self.stripPrompt(data).strip('\n') + '/tests'
# This caching of deviceRoot is causing issues if things fail
# if (not self.deviceRoot):
data = self.sendCMD(['testroot'])
if (data == None):
return '/tests'
self.deviceRoot = self.stripPrompt(data).strip('\n') + '/tests'
if (not self.dirExists(self.deviceRoot)):
self.mkDir(self.deviceRoot)
@ -690,8 +697,8 @@ class DeviceManager:
Application bundle - path to the application bundle on the device
Destination - destination directory of where application should be
installed to (optional)
Returns True or False depending on what we get back
TODO: we need a real way to know if this works or not
Returns None for success, or output if known failure
TODO: we need a better way to know if this works or not
"""
def installApp(self, appBundlePath, destPath=None):
cmd = 'inst ' + appBundlePath
@ -699,9 +706,13 @@ class DeviceManager:
cmd += ' ' + destPath
data = self.sendCMD([cmd])
if (data is None):
return False
else:
return True
return None
f = re.compile('Failure')
for line in data.split():
if (f.match(line)):
return data
return None
"""
Uninstalls the named application from device and causes a reboot.
@ -714,9 +725,53 @@ class DeviceManager:
cmd = 'uninst ' + appName
if installPath:
cmd += ' ' + installPath
self.sendCMD([cmd])
data = self.sendCMD([cmd])
if (self.debug > 3): print "uninstallAppAndReboot: " + str(data)
return True
"""
Updates the application on the device.
Application bundle - path to the application bundle on the device
Process name of application - used to end the process if the applicaiton is
currently running
Destination - Destination directory to where the application should be
installed (optional)
ipAddr - IP address to await a callback ping to let us know that the device has updated
properly - defaults to current IP.
port - port to await a callback ping to let us know that the device has updated properly
defaults to 30000, and counts up from there if it finds a conflict
Returns True if succeeds, False if not
NOTE: We have no real way to know if the device gets updated or not due to the
reboot that the udpate call forces on us. We can't install our own heartbeat
listener here because we run the risk of racing with other heartbeat listeners.
"""
def updateApp(self, appBundlePath, processName=None, destPath=None, ipAddr=None, port=None):
status = None
cmd = 'updt '
if (processName == None):
# Then we pass '' for processName
cmd += "'' " + appBundlePath
else:
cmd += processName + ' ' + appBundlePath
if (destPath):
cmd += " " + destPath
ip, port = self.getCallbackIpAndPort(ipAddr, 30000)
cmd += " %s %s" % (ip, port)
if (self.debug > 3): print "updateApp using command: " + str(cmd)
# Set up our callback server
callbacksvr = callbackServer(ip, port, self.debug)
data = self.sendCMD([cmd])
status = callbacksvr.disconnect()
if (self.debug > 3): print "got status back: " + str(status)
return status
"""
return the current time on the device
"""
@ -726,3 +781,112 @@ class DeviceManager:
return None
return self.stripPrompt(data).strip('\n')
"""
Connect the ipaddress and port for a callback ping. Defaults to current IP address
And ports starting at 30000.
NOTE: the detection for current IP address only works on Linux!
"""
def getCallbackIpAndPort(self, aIp, aPort):
ip = aIp
nettools = NetworkTools()
if (ip == None):
ip = nettools.getLanIp()
if (aPort != None):
port = nettools.findOpenPort(ip, aPort)
else:
port = nettools.findOpenPort(ip, 30000)
return ip, port
gCallbackData = ''
class callbackServer():
def __init__(self, ip, port, debuglevel):
self.ip = ip
self.port = port
self.connected = False
self.debug = debuglevel
if (self.debug > 3) : print "Creating server with " + str(ip) + ":" + str(port)
self.server = SocketServer.TCPServer((ip, port), self.myhandler)
self.server_thread = Thread(target=self.server.serve_forever)
self.server_thread.setDaemon(True)
self.server_thread.start()
def disconnect(self, step = 60, timeout = 600):
t = 0
if (self.debug > 3): print "Calling disconnect on callback server"
while t < timeout:
if (gCallbackData):
# Got the data back
if (self.debug > 3): print "Got data back from agent: " + str(gCallbackData)
break
time.sleep(step)
t += step
try:
if (self.debug > 3): print "Shutting down server now"
self.server.shutdown()
except:
print "Unable to shutdown callback server - check for a connection on port: " + str(self.port)
return gCallbackData
class myhandler(SocketServer.BaseRequestHandler):
def handle(self):
global gCallbackData
gCallbackData = self.request.recv(1024)
#print "Callback Handler got data: " + str(gCallbackData)
self.request.send("OK")
class NetworkTools:
def __init__(self):
pass
# Utilities to get the local ip address
def getInterfaceIp(self, ifname):
if os.name != "nt":
import fcntl
import struct
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
return socket.inet_ntoa(fcntl.ioctl(
s.fileno(),
0x8915, # SIOCGIFADDR
struct.pack('256s', ifname[:15])
)[20:24])
else:
return None
def getLanIp(self):
ip = socket.gethostbyname(socket.gethostname())
if ip.startswith("127.") and os.name != "nt":
interfaces = ["eth0","eth1","eth2","wlan0","wlan1","wifi0","ath0","ath1","ppp0"]
for ifname in interfaces:
try:
ip = self.getInterfaceIp(ifname)
break;
except IOError:
pass
return ip
# Gets an open port starting with the seed by incrementing by 1 each time
def findOpenPort(self, ip, seed):
try:
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
connected = False
if isinstance(seed, basestring):
seed = int(seed)
maxportnum = seed + 5000 # We will try at most 5000 ports to find an open one
while not connected:
try:
s.bind((ip, seed))
connected = True
s.close()
except:
if seed > maxportnum:
print "Could not find open port after checking 5000 ports"
raise
seed += 1
except:
print "Socket error trying to find open port"
return seed

View File

@ -42,7 +42,7 @@ import os
import socket
from automation import Automation
from devicemanager import DeviceManager
from devicemanager import DeviceManager, NetworkTools
class RemoteAutomation(Automation):
_devicemanager = None
@ -99,31 +99,9 @@ class RemoteAutomation(Automation):
# return app, ['--environ:NO_EM_RESTART=1'] + args
return app, args
# Utilities to get the local ip address
def getInterfaceIp(self, ifname):
if os.name != "nt":
import fcntl
import struct
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
return socket.inet_ntoa(fcntl.ioctl(
s.fileno(),
0x8915, # SIOCGIFADDR
struct.pack('256s', ifname[:15])
)[20:24])
else:
return None
def getLanIp(self):
ip = socket.gethostbyname(socket.gethostname())
if ip.startswith("127.") and os.name != "nt":
interfaces = ["eth0","eth1","eth2","wlan0","wlan1","wifi0","ath0","ath1","ppp0"]
for ifname in interfaces:
try:
ip = self.getInterfaceIp(ifname)
break;
except IOError:
pass
return ip
nettools = NetworkTools()
return nettools.getLanIp()
def Process(self, cmd, stdout = None, stderr = None, env = None, cwd = '.'):
if stdout == None or stdout == -1:
@ -176,4 +154,3 @@ class RemoteAutomation(Automation):
def kill(self):
self.dm.killProcess(self.procName)