1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 from twisted.spread import pb
18 from twisted.python import components, log as twlog
19 from twisted.internet import reactor
20 from twisted.application import strports
21 from twisted.cred import portal, checkers
22
23 from buildbot import interfaces
24 from zope.interface import Interface, implements
25 from buildbot.status import logfile, base
26 from buildbot.changes import changes
27
30
32
33
34 if obj is None:
35 return None
36 return IRemote(obj)
37
38
42
45
48
51
54
62 d.addCallback(add_remote)
63 return d
64
67
72
75
76 components.registerAdapter(RemoteBuildSet,
77 interfaces.IBuildSetStatus, IRemote)
78
79
83
86
89
95
98
101
104
107
110
111 components.registerAdapter(RemoteBuilder,
112 interfaces.IBuilderStatus, IRemote)
113
114
117 self.b = buildreq
118
119
120 self.observers = []
121
125
128
130 """The observer's remote_newbuild method will be called (with two
131 arguments: the RemoteBuild object, and our builderName) for each new
132 Build that is created to handle this BuildRequest."""
133 def send(bs):
134 d = observer.callRemote("newbuild",
135 IRemote(bs), self.b.getBuilderName())
136 d.addErrback(twlog.err,
137 "while calling client-side remote_newbuild")
138 self.observers.append((observer, send))
139 self.b.subscribe(send)
140
142 for i, (obs, send) in enumerate(self.observers):
143 if obs == observer:
144 del self.observers[i]
145 self.b.unsubscribe(send)
146 break
147
148 components.registerAdapter(RemoteBuildRequest,
149 interfaces.IBuildRequestStatus, IRemote)
150
153 self.b = build
154 self.observers = []
155
158
161
164
167
170
173
176
179
182
184
185
186 d = self.b.waitUntilFinished()
187 d.addCallback(lambda res: self)
188 return d
189
192
195
196 - def remote_getText(self):
197 return self.b.getText()
198
201
207
209 """The observer will have remote_stepStarted(buildername, build,
210 stepname, step), remote_stepFinished(buildername, build, stepname,
211 step, results), and maybe remote_buildETAUpdate(buildername, build,
212 eta)) messages sent to it."""
213 self.observers.append(observer)
214 s = BuildSubscriber(observer)
215 self.b.subscribe(s, updateInterval)
216
218
219
220
221 for o in self.observers:
222 if o == observer:
223 self.observers.remove(o)
224
225
226 components.registerAdapter(RemoteBuild,
227 interfaces.IBuildStatus, IRemote)
228
231 self.observer = observer
232
238
245
252
253
290
291 components.registerAdapter(RemoteBuildStep,
292 interfaces.IBuildStepStatus, IRemote)
293
306
307 components.registerAdapter(RemoteSlave,
308 interfaces.ISlaveStatus, IRemote)
309
318
319 components.registerAdapter(RemoteEvent,
320 interfaces.IStatusEvent, IRemote)
321
342
343
344 components.registerAdapter(RemoteLog, logfile.LogFile, IRemote)
345
346
357
358 components.registerAdapter(RemoteChange, changes.Change, IRemote)
359
360
362
363 subscribed = None
364 client = None
365
367 self.status = status
368 self.subscribed_to_builders = []
369 self.subscribed_to = []
370
372 d = self.__dict__.copy()
373 d['client'] = None
374 return d
375
379
390
392 """The remote client wishes to subscribe to some set of events.
393 'target' will be sent remote messages when these events happen.
394 'mode' indicates which events are desired: it is a string with one
395 of the following values:
396
397 'builders': builderAdded, builderRemoved
398 'builds': those plus builderChangedState, buildStarted, buildFinished
399 'steps': all those plus buildETAUpdate, stepStarted, stepFinished
400 'logs': all those plus stepETAUpdate, logStarted, logFinished
401 'full': all those plus logChunk (with the log contents)
402
403
404 Messages are defined by buildbot.interfaces.IStatusReceiver .
405 'interval' is used to specify how frequently ETAUpdate messages
406 should be sent.
407
408 Raising or lowering the subscription level will take effect starting
409 with the next build or step."""
410
411 assert mode in ("builders", "builds", "steps", "logs", "full")
412 assert target
413 twlog.msg("PB subscribe(%s)" % mode)
414
415 self.client = target
416 self.subscribed = mode
417 self.interval = interval
418 self.subscribed_to.append(self.status)
419
420
421 reactor.callLater(0, self.status.subscribe, self)
422 return None
423
429
431 """This returns tuples of (buildset, bsid), because that is much more
432 convenient for tryclient."""
433 d = self.status.getBuildSets()
434 def make_remotes(buildsets):
435 return [(IRemote(s), s.id) for s in buildsets]
436 d.addCallback(make_remotes)
437 return d
438
441
445
449
451 """Ping method to allow pb clients to validate their connections."""
452 return "pong"
453
454
455
456
463
466
467
469 if name in self.subscribed_to_builders:
470 self.subscribed_to_builders.remove(name)
471 self.client.callRemote("builderRemoved", name)
472
476
477
484
491
492
497
508
518
519
525
527
528 rlog = IRemote(log, None)
529 if not rlog:
530 print "hey, couldn't adapt %s to IRemote" % log
531 self.client.callRemote("logStarted",
532 build.getBuilder().getName(), IRemote(build),
533 step.getName(), IRemote(step),
534 log.getName(), IRemote(log, None))
535 if self.subscribed in ("full",):
536 self.subscribed_to.append(log)
537 return self
538 return None
539
547
548
549 - def logChunk(self, build, step, log, channel, text):
555
556
557 -class PBListener(base.StatusReceiverMultiService):
558 """I am a listener for PB-based status clients."""
559
560 compare_attrs = ["port", "cred"]
561 implements(portal.IRealm)
562
563 - def __init__(self, port, user="statusClient", passwd="clientpw"):
564 base.StatusReceiverMultiService.__init__(self)
565 if type(port) is int:
566 port = "tcp:%d" % port
567 self.port = port
568 self.cred = (user, passwd)
569 p = portal.Portal(self)
570 c = checkers.InMemoryUsernamePasswordDatabaseDontUse()
571 c.addUser(user, passwd)
572 p.registerChecker(c)
573 f = pb.PBServerFactory(p)
574 s = strports.service(port, f)
575 s.setServiceParent(self)
576
580
587