1
2
3 from twisted.spread import pb
4 from twisted.python import components, log as twlog
5 from twisted.internet import reactor
6 from twisted.application import strports
7 from twisted.cred import portal, checkers
8
9 from buildbot import interfaces
10 from zope.interface import Interface, implements
11 from buildbot.status import builder, base
12 from buildbot.changes import changes
13
16
18
19
20 if obj is None:
21 return None
22 return IRemote(obj)
23
24
28
31
34
37
40
46
49
54
59
62
63 components.registerAdapter(RemoteBuildSet,
64 interfaces.IBuildSetStatus, IRemote)
65
66
70
73
76
78 state, builds = self.b.getState()
79 return (state,
80 None,
81 [makeRemote(b) for b in builds])
82
85
88
91
94
97
98 components.registerAdapter(RemoteBuilder,
99 interfaces.IBuilderStatus, IRemote)
100
101
104 self.b = buildreq
105 self.observers = []
106
109
112
114 """The observer's remote_newbuild method will be called (with two
115 arguments: the RemoteBuild object, and our builderName) for each new
116 Build that is created to handle this BuildRequest."""
117 self.observers.append(observer)
118 def send(bs):
119 d = observer.callRemote("newbuild",
120 IRemote(bs), self.b.getBuilderName())
121 d.addErrback(lambda err: None)
122 reactor.callLater(0, self.b.subscribe, send)
123
125
126
127
128
129
130 for o in self.observers:
131 if o == observer:
132 self.observers.remove(o)
133
134 components.registerAdapter(RemoteBuildRequest,
135 interfaces.IBuildRequestStatus, IRemote)
136
139 self.b = build
140 self.observers = []
141
144
147
150
153
156
159
162
165
172
175
178
179 - def remote_getText(self):
180 return self.b.getText()
181
184
190
192 """The observer will have remote_stepStarted(buildername, build,
193 stepname, step), remote_stepFinished(buildername, build, stepname,
194 step, results), and maybe remote_buildETAUpdate(buildername, build,
195 eta)) messages sent to it."""
196 self.observers.append(observer)
197 s = BuildSubscriber(observer)
198 self.b.subscribe(s, updateInterval)
199
201
202
203
204 for o in self.observers:
205 if o == observer:
206 self.observers.remove(o)
207
208
209 components.registerAdapter(RemoteBuild,
210 interfaces.IBuildStatus, IRemote)
211
214 self.observer = observer
215
221
228
235
236
273
274 components.registerAdapter(RemoteBuildStep,
275 interfaces.IBuildStepStatus, IRemote)
276
289
290 components.registerAdapter(RemoteSlave,
291 interfaces.ISlaveStatus, IRemote)
292
301
302 components.registerAdapter(RemoteEvent,
303 interfaces.IStatusEvent, IRemote)
304
325
326
327 components.registerAdapter(RemoteLog, builder.LogFile, IRemote)
328
329
340
341 components.registerAdapter(RemoteChange, changes.Change, IRemote)
342
343
345
346 subscribed = None
347 client = None
348
350 self.status = status
351 self.subscribed_to_builders = []
352 self.subscribed_to = []
353
355 d = self.__dict__.copy()
356 d['client'] = None
357 return d
358
362
373
375 """The remote client wishes to subscribe to some set of events.
376 'target' will be sent remote messages when these events happen.
377 'mode' indicates which events are desired: it is a string with one
378 of the following values:
379
380 'builders': builderAdded, builderRemoved
381 'builds': those plus builderChangedState, buildStarted, buildFinished
382 'steps': all those plus buildETAUpdate, stepStarted, stepFinished
383 'logs': all those plus stepETAUpdate, logStarted, logFinished
384 'full': all those plus logChunk (with the log contents)
385
386
387 Messages are defined by buildbot.interfaces.IStatusReceiver .
388 'interval' is used to specify how frequently ETAUpdate messages
389 should be sent.
390
391 Raising or lowering the subscription level will take effect starting
392 with the next build or step."""
393
394 assert mode in ("builders", "builds", "steps", "logs", "full")
395 assert target
396 twlog.msg("PB subscribe(%s)" % mode)
397
398 self.client = target
399 self.subscribed = mode
400 self.interval = interval
401 self.subscribed_to.append(self.status)
402
403
404 reactor.callLater(0, self.status.subscribe, self)
405 return None
406
412
414 """This returns tuples of (buildset, bsid), because that is much more
415 convenient for tryclient."""
416 return [(IRemote(s), s.getID()) for s in self.status.getBuildSets()]
417
420
424
428
430 """Ping method to allow pb clients to validate their connections."""
431 return "pong"
432
433
434
435
437 self.client.callRemote("builderAdded", name, IRemote(builder))
438 if self.subscribed in ("builds", "steps", "logs", "full"):
439 self.subscribed_to_builders.append(name)
440 return self
441 return None
442
444 self.client.callRemote("builderChangedState", name, state, None)
445
446
448 if name in self.subscribed_to_builders:
449 self.subscribed_to_builders.remove(name)
450 self.client.callRemote("builderRemoved", name)
451
455
456
458 self.client.callRemote("buildStarted", name, IRemote(build))
459 if self.subscribed in ("steps", "logs", "full"):
460 self.subscribed_to.append(build)
461 return (self, self.interval)
462 return None
463
470
471
476
487
497
498
504
506
507 rlog = IRemote(log, None)
508 if not rlog:
509 print "hey, couldn't adapt %s to IRemote" % log
510 self.client.callRemote("logStarted",
511 build.getBuilder().getName(), IRemote(build),
512 step.getName(), IRemote(step),
513 log.getName(), IRemote(log, None))
514 if self.subscribed in ("full",):
515 self.subscribed_to.append(log)
516 return self
517 return None
518
526
527
528 - def logChunk(self, build, step, log, channel, text):
534
535
536 -class PBListener(base.StatusReceiverMultiService):
537 """I am a listener for PB-based status clients."""
538
539 compare_attrs = ["port", "cred"]
540 implements(portal.IRealm)
541
542 - def __init__(self, port, user="statusClient", passwd="clientpw"):
543 base.StatusReceiverMultiService.__init__(self)
544 if type(port) is int:
545 port = "tcp:%d" % port
546 self.port = port
547 self.cred = (user, passwd)
548 p = portal.Portal(self)
549 c = checkers.InMemoryUsernamePasswordDatabaseDontUse()
550 c.addUser(user, passwd)
551 p.registerChecker(c)
552 f = pb.PBServerFactory(p)
553 s = strports.service(port, f)
554 s.setServiceParent(self)
555
559
562
569