3 from twisted
.python
.failure
import Failure
4 from twisted
.internet
import task
, defer
, reactor
5 from twisted
.protocols
.basic
import LineOnlyReceiver
10 class BuildmasterTimeoutError(Exception):
12 class BuildslaveTimeoutError(Exception):
14 class ReconfigError(Exception):
16 class BuildSlaveDetectedError(Exception):
19 class LogWatcher(LineOnlyReceiver
):
22 delimiter
= os
.linesep
24 def __init__(self
, logfile
):
25 self
.logfile
= logfile
26 self
.in_reconfig
= False
27 self
.transport
= FakeTransport()
29 self
.processtype
= "buildmaster"
32 # return a Deferred that fires when the reconfig process has
33 # finished. It errbacks with TimeoutError if the finish line has not
34 # been seen within 10 seconds, and with ReconfigError if the error
35 # line was seen. If the logfile could not be opened, it errbacks with
38 d
= defer
.maybeDeferred(self
._start
)
42 self
.d
= defer
.Deferred()
44 self
.f
= open(self
.logfile
, "rb")
45 self
.f
.seek(0, 2) # start watching from the end
48 reactor
.callLater(self
.TIMEOUT_DELAY
, self
.timeout
)
49 self
.poller
= task
.LoopingCall(self
.poll
)
50 self
.poller
.start(self
.POLL_INTERVAL
)
54 if self
.processtype
== "buildmaster":
55 self
.d
.errback(BuildmasterTimeoutError())
57 self
.d
.errback(BuildslaveTimeoutError())
59 def finished(self
, results
):
61 self
.in_reconfig
= False
62 self
.d
.callback(results
)
64 def lineReceived(self
, line
):
67 if "Log opened." in line
:
68 self
.in_reconfig
= True
69 if "loading configuration from" in line
:
70 self
.in_reconfig
= True
71 if "Creating BuildSlave" in line
:
72 self
.processtype
= "buildslave"
77 if "message from master: attached" in line
:
78 return self
.finished("buildslave")
79 if "I will keep using the previous config file" in line
:
80 return self
.finished(Failure(ReconfigError()))
81 if "configuration update complete" in line
:
82 return self
.finished("buildmaster")
87 self
.f
= open(self
.logfile
, "rb")
91 data
= self
.f
.read(1000)
94 self
.dataReceived(data
)