41
|
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)
|
42
|
97 dev = hubDevice(usbId)
|
|
98 # sometimes the dev will exist but fail to open
|
|
99 open(dev, "r")
|
41
|
100 return True
|
42
|
101 except (ValueError, IOError):
|
41
|
102 return False
|
|
103
|
|
104 def resetDevice(dev):
|
|
105 """
|
|
106 send USBDEVFS_RESET to the given /dev address
|
|
107 """
|
|
108 log.debug("resetting %s" % dev)
|
|
109 f=os.open(dev, os.O_WRONLY)
|
|
110 ret = fcntl.ioctl(f, USBDEVFS_RESET, 0)
|
|
111 if ret != 0:
|
|
112 raise ValueError("ioctl failed with %s" % ret)
|
|
113 time.sleep(3)
|
|
114
|
|
115 def supervisorRestart(cmds, supervisor="http://localhost:9001"):
|
|
116 serv = xmlrpclib.ServerProxy(supervisor)
|
|
117 for c in cmds:
|
|
118 log.info("restarting %s", c)
|
|
119 try:
|
|
120 serv.supervisor.stopProcessGroup(c)
|
|
121 except xmlrpclib.ResponseError, e:
|
|
122 log.warn("supervisor.stopProcessGroup error %r, ignoring", e)
|
|
123 serv.supervisor.startProcess(c)
|
|
124
|
|
125 def resetModules(modules):
|
|
126 log.info("reloading modules: %s", modules)
|
|
127 for m in modules:
|
|
128 subprocess.call(['modprobe', '-r', m])
|
|
129 for m in modules:
|
|
130 subprocess.check_call(['modprobe', m])
|
|
131
|
|
132
|
|
133 class Background(object):
|
|
134 def __init__(self, config, period):
|
|
135 self.config = config
|
|
136 self.period = period
|
|
137 self.lastPollTime = 0
|
|
138
|
|
139 def assertIsCurrent(self):
|
|
140 """raise an error if the poll data is not fresh"""
|
|
141 dt = time.time() - self.lastPollTime
|
|
142 if dt > self.period * 2:
|
|
143 raise ValueError("last poll time was too old: %.1f sec ago" % dt)
|
|
144
|
|
145 @inlineCallbacks
|
|
146 def step(self):
|
|
147 now = time.time()
|
|
148 try:
|
|
149 if hostname == 'bang':
|
|
150 if (not haveDevice(Id.bedroomCam) or
|
|
151 not haveDevice(Id.bedroomArduino)):
|
|
152 if haveDevice(Id.bedroomHub3):
|
|
153 resetDevice(hubDevice(Id.bedroomHub3))
|
|
154 else:
|
|
155 if haveDevice(Id.bedroomHub2):
|
|
156 resetDevice(hubDevice(Id.bedroomHub2))
|
|
157 else:
|
|
158 if haveDevice(Id.bedroomHub1):
|
|
159 resetDevice(hubDevice(Id.bedroomHub1))
|
|
160 else:
|
|
161 if haveDevice(Id.bedroomHub0):
|
|
162 resetDevice(hubDevice(Id.bedroomHub0))
|
|
163 else:
|
|
164 raise ValueError(
|
|
165 "don't even have the first hub")
|
|
166 resetModules(['gspca_zc3xx'])
|
|
167 supervisorRestart(['webcam_9053'])
|
|
168 else:
|
|
169 log.debug("usb devices look ok")
|
|
170
|
|
171 elif hostname == 'slash':
|
|
172 haveFrontHub0 = haveDevice(Id.frontDoorHub0)
|
|
173 haveFrontHub1 = haveDevice(Id.frontDoorHub1)
|
|
174 haveFrontHub2 = haveDevice(Id.frontDoorHub2)
|
|
175 haveFrontHub3 = haveDevice(Id.frontDoorHub3)
|
|
176 haveGarageHub0 = haveDevice(Id.garageHub0)
|
|
177 haveGarageHub1 = haveDevice(Id.garageHub1)
|
|
178 haveFrontDoorCam = haveDevice(Id.frontDoorCam)
|
|
179 haveV4lDevice = os.path.exists(
|
|
180 "/dev/v4l/by-id/usb-Vimicro_Corp._PC_Camera-video-index0")
|
|
181 haveFrontArduinoServe = (yield getOk('http://slash:9080/'))
|
|
182 haveFrontWebcamImage = (yield getOk(
|
|
183 "http://slash:9023/frontDoor", timeout=10))
|
|
184
|
|
185 log.info(str(vars()))
|
|
186
|
|
187 if not haveDevice(Id.ftdi):
|
|
188 if haveFrontHub3:
|
42
|
189 resetDevice(hubDevice(Id.frontDoorHub3))
|
41
|
190 else:
|
|
191 if haveFrontHub2:
|
42
|
192 resetDevice(hubDevice(Id.frontDoorHub2))
|
41
|
193 else:
|
|
194 if haveFrontHub1:
|
42
|
195 resetDevice(hubDevice(Id.frontDoorHub1))
|
41
|
196 else:
|
|
197 if haveFrontHub0:
|
42
|
198 resetDevice(hubDevice(Id.frontDoorHub0))
|
41
|
199 else:
|
|
200 raise ValueError("don't have the first hub")
|
|
201 else:
|
|
202 log.debug("front door chain looks ok")
|
|
203
|
|
204 if not haveDevice(Id.garagePowerSerial):
|
|
205 if haveGarageHub1:
|
42
|
206 resetDevice(hubDevice(Id.garageHub1))
|
41
|
207 else:
|
|
208 if haveGarageHub0:
|
42
|
209 resetDevice(hubDevice(Id.garageHub0))
|
41
|
210 else:
|
|
211 raise ValueError("don't have the first hub")
|
|
212 else:
|
|
213 log.debug("garage chain looks ok")
|
|
214
|
|
215 if not haveDevice(Id.garageArduino):
|
|
216 if haveGarageHub1:
|
|
217 resetDevice(hubDevice(Id.garageHub1))
|
|
218 else:
|
|
219 raise ValueError("don't even have the first hub")
|
|
220 resetModules(['gspca_zc3xx'])
|
|
221 supervisorRestart(['frontDoorArduino_9080'])
|
|
222 else:
|
|
223 if not haveFrontArduinoServe:
|
|
224 resetDevice(hubDevice(Id.frontDoorHub3))
|
|
225 supervisorRestart(['frontDoorArduino_9080'])
|
|
226 time.sleep(10)
|
|
227 else:
|
|
228 log.debug("frontDoorArduino looks ok")
|
|
229
|
|
230 if not haveFrontWebcamImage:
|
|
231 supervisorRestart(['webcam_frontDoor_9023'])
|
|
232 else:
|
|
233 log.debug("front webcam looks ok")
|
|
234
|
|
235 elif hostname == 'dash':
|
|
236 if not os.path.exists("/dev/serial/by-id/usb-FTDI_FT232R_USB_UART_A900gbcG-if00-port0"):
|
42
|
237 resetDevice(hubDevice("/dev/bus/usb/003/001"))
|
41
|
238
|
|
239 else:
|
|
240 raise NotImplementedError
|
|
241
|
|
242 log.debug(" -- finished")
|
|
243 self.lastPollTime = now
|
|
244
|
|
245 except Exception, e:
|
|
246 print "poll error", e
|
|
247 traceback.print_exc()
|
|
248
|
|
249 class Index(PrettyErrorHandler, cyclone.web.RequestHandler):
|
|
250 def get(self):
|
|
251 self.settings.background.assertIsCurrent()
|
|
252
|
|
253 self.set_header("Content-Type", "application/xhtml+xml")
|
|
254 self.write("usbreset is ok")#open("index.html").read())
|
|
255
|
|
256 if __name__ == '__main__':
|
|
257 config = { # to be read from a file
|
|
258 'servePort' : 9100,
|
|
259 'checkPeriod' : 30,
|
|
260 }
|
|
261
|
|
262 from twisted.python import log as twlog
|
|
263 #twlog.startLogging(sys.stdout)
|
|
264
|
|
265 log.setLevel(logging.INFO)
|
|
266
|
|
267 p = Background(config, config['checkPeriod'])
|
|
268 task.LoopingCall(p.step).start(config['checkPeriod'])
|
|
269
|
|
270 reactor.listenTCP(config['servePort'], cyclone.web.Application([
|
|
271 (r"/", Index),
|
|
272 ], background=p))
|
|
273 reactor.run()
|