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
181
182
183 d = self.b.waitUntilFinished()
184 d.addCallback(lambda res: self)
185 return d
186
189
192
193 - def remote_getText(self):
194 return self.b.getText()
195
198
204
206 """The observer will have remote_stepStarted(buildername, build,
207 stepname, step), remote_stepFinished(buildername, build, stepname,
208 step, results), and maybe remote_buildETAUpdate(buildername, build,
209 eta)) messages sent to it."""
210 self.observers.append(observer)
211 s = BuildSubscriber(observer)
212 self.b.subscribe(s, updateInterval)
213
215
216
217
218 for o in self.observers:
219 if o == observer:
220 self.observers.remove(o)
221
222
223 components.registerAdapter(RemoteBuild,
224 interfaces.IBuildStatus, IRemote)
225
228 self.observer = observer
229
235
242
249
250
287
288 components.registerAdapter(RemoteBuildStep,
289 interfaces.IBuildStepStatus, IRemote)
290
303
304 components.registerAdapter(RemoteSlave,
305 interfaces.ISlaveStatus, IRemote)
306
315
316 components.registerAdapter(RemoteEvent,
317 interfaces.IStatusEvent, IRemote)
318
339
340
341 components.registerAdapter(RemoteLog, logfile.LogFile, IRemote)
342
343
354
355 components.registerAdapter(RemoteChange, changes.Change, IRemote)
356
357
359
360 subscribed = None
361 client = None
362
364 self.status = status
365 self.subscribed_to_builders = []
366 self.subscribed_to = []
367
369 d = self.__dict__.copy()
370 d['client'] = None
371 return d
372
376
387
389 """The remote client wishes to subscribe to some set of events.
390 'target' will be sent remote messages when these events happen.
391 'mode' indicates which events are desired: it is a string with one
392 of the following values:
393
394 'builders': builderAdded, builderRemoved
395 'builds': those plus builderChangedState, buildStarted, buildFinished
396 'steps': all those plus buildETAUpdate, stepStarted, stepFinished
397 'logs': all those plus stepETAUpdate, logStarted, logFinished
398 'full': all those plus logChunk (with the log contents)
399
400
401 Messages are defined by buildbot.interfaces.IStatusReceiver .
402 'interval' is used to specify how frequently ETAUpdate messages
403 should be sent.
404
405 Raising or lowering the subscription level will take effect starting
406 with the next build or step."""
407
408 assert mode in ("builders", "builds", "steps", "logs", "full")
409 assert target
410 twlog.msg("PB subscribe(%s)" % mode)
411
412 self.client = target
413 self.subscribed = mode
414 self.interval = interval
415 self.subscribed_to.append(self.status)
416
417
418 reactor.callLater(0, self.status.subscribe, self)
419 return None
420
426
428 """This returns tuples of (buildset, bsid), because that is much more
429 convenient for tryclient."""
430 d = self.status.getBuildSets()
431 def make_remotes(buildsets):
432 return [(IRemote(s), s.id) for s in buildsets]
433 d.addCallback(make_remotes)
434 return d
435
438
442
446
448 """Ping method to allow pb clients to validate their connections."""
449 return "pong"
450
451
452
453
460
463
464
466 if name in self.subscribed_to_builders:
467 self.subscribed_to_builders.remove(name)
468 self.client.callRemote("builderRemoved", name)
469
473
474
481
488
489
494
505
515
516
522
524
525 rlog = IRemote(log, None)
526 if not rlog:
527 print "hey, couldn't adapt %s to IRemote" % log
528 self.client.callRemote("logStarted",
529 build.getBuilder().getName(), IRemote(build),
530 step.getName(), IRemote(step),
531 log.getName(), IRemote(log, None))
532 if self.subscribed in ("full",):
533 self.subscribed_to.append(log)
534 return self
535 return None
536
544
545
546 - def logChunk(self, build, step, log, channel, text):
552
553
554 -class PBListener(base.StatusReceiverMultiService):
555 """I am a listener for PB-based status clients."""
556
557 compare_attrs = ["port", "cred"]
558 implements(portal.IRealm)
559
560 - def __init__(self, port, user="statusClient", passwd="clientpw"):
561 base.StatusReceiverMultiService.__init__(self)
562 if type(port) is int:
563 port = "tcp:%d" % port
564 self.port = port
565 self.cred = (user, passwd)
566 p = portal.Portal(self)
567 c = checkers.InMemoryUsernamePasswordDatabaseDontUse()
568 c.addUser(user, passwd)
569 p.registerChecker(c)
570 f = pb.PBServerFactory(p)
571 s = strports.service(port, f)
572 s.setServiceParent(self)
573
577
580
587