comparison service/usbReset/usbReset.py @ 846:5c6133c227d0

usb reset mess Ignore-this: 66107d6e9c5740a91d11a6ece7efc433 darcs-hash:20120827055524-312f9-ffe0e119c519786ace0b3ff8765e9c2c7a7bdb81
author drewp <drewp@bigasterisk.com>
date Sun, 26 Aug 2012 22:55:24 -0700
parents
children 022921fdafd0
comparison
equal deleted inserted replaced
845:edd2486d6e2c 846:5c6133c227d0
1 #!bin/python
2 """
3 resets usb ports and restarts other services. Must run as root for the usb part.
4
5 Other systems that might be able to do conditional tests and commands like this:
6 https://github.com/azkaban/azkaban
7 nagios
8
9
10
11 run this as root to reset a whole usb device. Ported from
12 http://marc.info/?l=linux-usb&m=121459435621262&w=2
13
14 how to learn what device to reset? lsusb or lsusb -t
15
16 how to learn the ioctl number? cpp on this file:
17
18 #include "/usr/include/linux/usbdevice_fs.h"
19 #include "/usr/include/asm-generic/ioctl.h"
20 USBDEVFS_RESET
21
22 last line comes out like this:
23 (((0U) << (((0 +8)+8)+14)) | ((('U')) << (0 +8)) | (((20)) << 0) | ((0) << ((0 +8)+8)))
24
25 this is a py expression:
26 (((0) << (((0 +8)+8)+14)) | ((ord('U')) << (0 +8)) | (((20)) << 0) | ((0) << ((0 +8)+8)))
27
28
29 """
30
31 from __future__ import division
32
33 import cyclone.web, json, traceback, os, sys, time, logging
34 import os, fcntl, commands, socket, logging, time, xmlrpclib, subprocess
35
36 from twisted.internet import reactor, task
37 from twisted.internet.defer import inlineCallbacks, returnValue
38 from twisted.web.client import getPage
39 sys.path.append("/my/proj/house/frontdoor")
40 from loggingserial import LoggingSerial
41 sys.path.append("/my/proj/homeauto/lib")
42 from cycloneerr import PrettyErrorHandler
43 from logsetup import log
44
45 USBDEVFS_RESET = 21780
46
47 class Id(object):
48 ftdi = "0403:6001"
49 frontDoorHub0 = "8087:0024" # bus2 dev 2
50 frontDoorHub1 = "0451:2046" # bus2 dev 3
51 frontDoorHub2 = "1a40:0101" # bus2 dev 7
52 frontDoorHub3 = "0409:0058" # bus2 dev 62
53 frontDoorCam = "0ac8:307b"
54
55 bedroomHub0 = "8087:0020"
56 bedroomHub1 = "05e3:0608"
57 bedroomHub2 = "058f:6254"
58 bedroomHub3 = "0409:005a"
59 bedroomCam = "046d:08aa"
60 bedroomSba = "04d8:000a"
61 bedroomArduino = "0403:6001"
62
63 garageHub0 = "1d6b:0002" # bus2 dev1
64 garageHub1 = "05e3:0604" # bus2 dev4
65 garageArduino = "2341:0001"
66 garagePowerSerial = "0557:2008"
67
68 blueHub = "1a40:0101"
69
70 hostname = socket.gethostname()
71
72 @inlineCallbacks
73 def getOk(url, timeout=1):
74 """can we get a successful status from this url in a short time?"""
75 log.debug("testing %s" % url)
76 try:
77 resp = yield getPage(url, timeout=timeout)
78 except Exception, e:
79 log.warn("getPage %s", e)
80 returnValue(False)
81
82 returnValue(True)
83
84 def hubDevice(usbId="1a40:0101"):
85 """
86 what's the /dev path to the device with this usb id
87 """
88 for line in commands.getoutput("lsusb").splitlines():
89 if 'ID '+usbId in line:
90 words = line.split()
91 return "/dev/bus/usb/%s/%s" % (words[1], words[3].rstrip(':'))
92 raise ValueError("no usb device found with id %r" % usbId)
93
94 def haveDevice(usbId):
95 try:
96 log.debug("checking for %s", usbId)
97 hubDevice(usbId)
98 return True
99 except ValueError:
100 return False
101
102 def resetDevice(dev):
103 """
104 send USBDEVFS_RESET to the given /dev address
105 """
106 log.debug("resetting %s" % dev)
107 f=os.open(dev, os.O_WRONLY)
108 ret = fcntl.ioctl(f, USBDEVFS_RESET, 0)
109 if ret != 0:
110 raise ValueError("ioctl failed with %s" % ret)
111 time.sleep(3)
112
113 def supervisorRestart(cmds, supervisor="http://localhost:9001"):
114 serv = xmlrpclib.ServerProxy(supervisor)
115 for c in cmds:
116 log.info("restarting %s", c)
117 try:
118 serv.supervisor.stopProcessGroup(c)
119 except xmlrpclib.ResponseError, e:
120 log.warn("supervisor.stopProcessGroup error %r, ignoring", e)
121 serv.supervisor.startProcess(c)
122
123 def resetModules(modules):
124 log.info("reloading modules: %s", modules)
125 for m in modules:
126 subprocess.call(['modprobe', '-r', m])
127 for m in modules:
128 subprocess.check_call(['modprobe', m])
129
130
131 class Background(object):
132 def __init__(self, config, period):
133 self.config = config
134 self.period = period
135 self.lastPollTime = 0
136
137 def assertIsCurrent(self):
138 """raise an error if the poll data is not fresh"""
139 dt = time.time() - self.lastPollTime
140 if dt > self.period * 2:
141 raise ValueError("last poll time was too old: %.1f sec ago" % dt)
142
143 @inlineCallbacks
144 def step(self):
145 now = time.time()
146 try:
147 if hostname == 'bang':
148 if (not haveDevice(Id.bedroomCam) or
149 not haveDevice(Id.bedroomArduino)):
150 if haveDevice(Id.bedroomHub3):
151 resetDevice(hubDevice(Id.bedroomHub3))
152 else:
153 if haveDevice(Id.bedroomHub2):
154 resetDevice(hubDevice(Id.bedroomHub2))
155 else:
156 if haveDevice(Id.bedroomHub1):
157 resetDevice(hubDevice(Id.bedroomHub1))
158 else:
159 if haveDevice(Id.bedroomHub0):
160 resetDevice(hubDevice(Id.bedroomHub0))
161 else:
162 raise ValueError(
163 "don't even have the first hub")
164 resetModules(['gspca_zc3xx'])
165 supervisorRestart(['webcam_9053'])
166 else:
167 log.debug("usb devices look ok")
168
169 elif hostname == 'slash':
170 haveFrontHub0 = haveDevice(Id.frontDoorHub0)
171 haveFrontHub1 = haveDevice(Id.frontDoorHub1)
172 haveFrontHub2 = haveDevice(Id.frontDoorHub2)
173 haveFrontHub3 = haveDevice(Id.frontDoorHub3)
174 haveGarageHub0 = haveDevice(Id.garageHub0)
175 haveGarageHub1 = haveDevice(Id.garageHub1)
176 haveFrontDoorCam = haveDevice(Id.frontDoorCam)
177 haveV4lDevice = os.path.exists(
178 "/dev/v4l/by-id/usb-Vimicro_Corp._PC_Camera-video-index0")
179 haveFrontArduinoServe = (yield getOk('http://slash:9080/'))
180 haveFrontWebcamImage = (yield getOk(
181 "http://slash:9023/frontDoor", timeout=10))
182
183 log.info(str(vars()))
184
185 if not haveDevice(Id.ftdi):
186 if haveFrontHub3:
187 resetDevice(Id.frontDoorHub3)
188 else:
189 if haveFrontHub2:
190 resetDevice(Id.frontDoorHub2)
191 else:
192 if haveFrontHub1:
193 resetDevice(Id.frontDoorHub1)
194 else:
195 if haveFrontHub0:
196 resetDevice(Id.frontDoorHub0)
197 else:
198 raise ValueError("don't have the first hub")
199 else:
200 log.debug("front door chain looks ok")
201
202 if not haveDevice(Id.garagePowerSerial):
203 if haveGarageHub1:
204 resetDevice(Id.garageHub1)
205 else:
206 if haveGarageHub0:
207 resetDevice(Id.garageHub0)
208 else:
209 raise ValueError("don't have the first hub")
210 else:
211 log.debug("garage chain looks ok")
212
213 if not haveDevice(Id.garageArduino):
214 if haveGarageHub1:
215 resetDevice(hubDevice(Id.garageHub1))
216 else:
217 raise ValueError("don't even have the first hub")
218 resetModules(['gspca_zc3xx'])
219 supervisorRestart(['frontDoorArduino_9080'])
220 else:
221 if not haveFrontArduinoServe:
222 resetDevice(hubDevice(Id.frontDoorHub3))
223 supervisorRestart(['frontDoorArduino_9080'])
224 time.sleep(10)
225 else:
226 log.debug("frontDoorArduino looks ok")
227
228 if not haveFrontWebcamImage:
229 supervisorRestart(['webcam_frontDoor_9023'])
230 else:
231 log.debug("front webcam looks ok")
232
233 elif hostname == 'dash':
234 if not os.path.exists("/dev/serial/by-id/usb-FTDI_FT232R_USB_UART_A900gbcG-if00-port0"):
235 resetDevice("/dev/bus/usb/003/001")
236
237 else:
238 raise NotImplementedError
239
240 log.debug(" -- finished")
241 self.lastPollTime = now
242
243 except Exception, e:
244 print "poll error", e
245 traceback.print_exc()
246
247 class Index(PrettyErrorHandler, cyclone.web.RequestHandler):
248 def get(self):
249 self.settings.background.assertIsCurrent()
250
251 self.set_header("Content-Type", "application/xhtml+xml")
252 self.write("usbreset is ok")#open("index.html").read())
253
254 if __name__ == '__main__':
255 config = { # to be read from a file
256 'servePort' : 9100,
257 'checkPeriod' : 30,
258 }
259
260 from twisted.python import log as twlog
261 #twlog.startLogging(sys.stdout)
262
263 log.setLevel(logging.INFO)
264
265 p = Background(config, config['checkPeriod'])
266 task.LoopingCall(p.step).start(config['checkPeriod'])
267
268 reactor.listenTCP(config['servePort'], cyclone.web.Application([
269 (r"/", Index),
270 ], background=p))
271 reactor.run()