Home | Trees | Indices | Help |
|
---|
|
1 2 # code to deliver build status through twisted.words (instant messaging 3 # protocols: irc, etc) 4 5 import re, shlex 6 from string import join, capitalize, lower 7 8 from zope.interface import Interface, implements 9 from twisted.internet import protocol, reactor 10 from twisted.words.protocols import irc 11 from twisted.python import log, failure 12 from twisted.application import internet 13 14 from buildbot import interfaces, util 15 from buildbot import version 16 from buildbot.interfaces import IStatusReceiver 17 from buildbot.sourcestamp import SourceStamp 18 from buildbot.status import base 19 from buildbot.status.builder import SUCCESS, WARNINGS, FAILURE, EXCEPTION 20 from buildbot.scripts.runner import ForceOptions 21 22 # twisted.internet.ssl requires PyOpenSSL, so be resilient if it's missing 23 try: 24 from twisted.internet import ssl 25 have_ssl = True 26 except ImportError: 27 have_ssl = False 283231 ValueError.__init__(self, string, *more)34 hasStarted = False 35 timer = None 36 406042 del self.timer 43 if not self.hasStarted: 44 self.parent.send("The build has been queued, I'll give a shout" 45 " when it starts")4648 self.hasStarted = True 49 if self.timer: 50 self.timer.cancel() 51 del self.timer 52 eta = s.getETA() 53 response = "build #%d forced" % s.getNumber() 54 if eta is not None: 55 response = "build forced [ETA %s]" % self.parent.convertTime(eta) 56 self.parent.send(response) 57 self.parent.send("I'll give a shout when the build finishes") 58 d = s.waitUntilFinished() 59 d.addCallback(self.parent.watchedBuildFinished)62 implements(IStatusReceiver) 63 """I hold the state for a single user's interaction with the buildbot. 64 65 This base class provides all the basic behavior (the queries and 66 responses). Subclasses for each channel type (IRC, different IM 67 protocols) are expected to provide the lower-level send/receive methods. 68 69 There will be one instance of me for each user who interacts personally 70 with the buildbot. There will be an additional instance for each 71 'broadcast contact' (chat rooms, IRC channels as a whole). 72 """ 7358475 #StatusReceiver.__init__(self) doesn't exist 76 self.channel = channel 77 self.notify_events = {} 78 self.subscribed = 0 79 self.add_notification_events(channel.notify_events)80 81 silly = { 82 "What happen ?": "Somebody set up us the bomb.", 83 "It's You !!": ["How are you gentlemen !!", 84 "All your base are belong to us.", 85 "You are on the way to destruction."], 86 "What you say !!": ["You have no chance to survive make your time.", 87 "HA HA HA HA ...."], 88 } 89 9395 try: 96 b = self.channel.status.getBuilder(which) 97 except KeyError: 98 raise UsageError, "no such builder '%s'" % which 99 return b100102 if not self.channel.control: 103 raise UsageError("builder control is not enabled") 104 try: 105 bc = self.channel.control.getBuilder(which) 106 except KeyError: 107 raise UsageError("no such builder '%s'" % which) 108 return bc109111 """ 112 @rtype: list of L{buildbot.process.builder.Builder} 113 """ 114 names = self.channel.status.getBuilderNames(categories=self.channel.categories) 115 names.sort() 116 builders = [self.channel.status.getBuilder(n) for n in names] 117 return builders118120 if seconds < 60: 121 return "%d seconds" % seconds 122 minutes = int(seconds / 60) 123 seconds = seconds - 60*minutes 124 if minutes < 60: 125 return "%dm%02ds" % (minutes, seconds) 126 hours = int(minutes / 60) 127 minutes = minutes - 60*hours 128 return "%dh%02dm%02ds" % (hours, minutes, seconds)129131 response = self.silly[message] 132 if type(response) != type([]): 133 response = [response] 134 when = 0.5 135 for r in response: 136 reactor.callLater(when, self.send, r) 137 when += 2.5138140 self.send("yes?")141 144146 args = shlex.split(args) 147 if len(args) == 0: 148 raise UsageError, "try 'list builders'" 149 if args[0] == 'builders': 150 builders = self.getAllBuilders() 151 str = "Configured builders: " 152 for b in builders: 153 str += b.name 154 state = b.getState()[0] 155 if state == 'offline': 156 str += "[offline]" 157 str += " " 158 str.rstrip() 159 self.send(str) 160 return161 command_LIST.usage = "list builders - List configured builders" 162164 args = shlex.split(args) 165 if len(args) == 0: 166 which = "all" 167 elif len(args) == 1: 168 which = args[0] 169 else: 170 raise UsageError, "try 'status <builder>'" 171 if which == "all": 172 builders = self.getAllBuilders() 173 for b in builders: 174 self.emit_status(b.name) 175 return 176 self.emit_status(which)177 command_STATUS.usage = "status [<which>] - List status of a builder (or all builders)" 178180 if not re.compile("^(started|finished|success|failure|exception|warnings|(success|warnings|exception|failure)To(Failure|Success|Warnings|Exception))$").match(event): 181 raise UsageError("try 'notify on|off <EVENT>'")182184 self.send( "The following events are being notified: %r" % self.notify_events.keys() )185 191 195 199201 for event in events: 202 self.validate_notification_event(event) 203 self.notify_events[event] = 1 204 205 if not self.subscribed: 206 self.subscribe_to_build_events()207209 for event in events: 210 self.validate_notification_event(event) 211 del self.notify_events[event] 212 213 if len(self.notify_events) == 0 and self.subscribed: 214 self.unsubscribe_from_build_events()215 221223 args = shlex.split(args) 224 225 if not args: 226 raise UsageError("try 'notify on|off|list <EVENT>'") 227 action = args.pop(0) 228 events = args 229 230 if action == "on": 231 if not events: events = ('started','finished') 232 self.add_notification_events(events) 233 234 self.list_notified_events() 235 236 elif action == "off": 237 if events: 238 self.remove_notification_events(events) 239 else: 240 self.remove_all_notification_events() 241 242 self.list_notified_events() 243 244 elif action == "list": 245 self.list_notified_events() 246 return 247 248 else: 249 raise UsageError("try 'notify on|off <EVENT>'")250 251 command_NOTIFY.usage = "notify on|off|list [<EVENT>] ... - Notify me about build events. event should be one or more of: 'started', 'finished', 'failure', 'success', 'exception' or 'xToY' (where x and Y are one of success, warnings, failure, exception, but Y is capitalized)" 252254 args = shlex.split(args) 255 if len(args) != 1: 256 raise UsageError("try 'watch <builder>'") 257 which = args[0] 258 b = self.getBuilder(which) 259 builds = b.getCurrentBuilds() 260 if not builds: 261 self.send("there are no builds currently running") 262 return 263 for build in builds: 264 assert not build.isFinished() 265 d = build.waitUntilFinished() 266 d.addCallback(self.watchedBuildFinished) 267 r = "watching build %s #%d until it finishes" \ 268 % (which, build.getNumber()) 269 eta = build.getETA() 270 if eta is not None: 271 r += " [%s]" % self.convertTime(eta) 272 r += ".." 273 self.send(r)274 command_WATCH.usage = "watch <which> - announce the completion of an active build" 275277 log.msg('[Contact] Buildset %s added' % (buildset))278 282284 log.msg('[Contact] Builder %s changed state to %s' % (builderName, state))285287 log.msg('[Contact] BuildRequest for %s submitted to Builder %s' % 288 (brstatus.getSourceStamp(), brstatus.builderName))289291 log.msg('[Contact] Builder %s removed' % (builderName))292294 builder = build.getBuilder() 295 log.msg('[Contact] Builder %r in category %s started' % (builder, builder.category)) 296 297 # only notify about builders we are interested in 298 299 if (self.channel.categories != None and 300 builder.category not in self.channel.categories): 301 log.msg('Not notifying for a build in the wrong category') 302 return 303 304 if not self.notify_for('started'): 305 log.msg('Not notifying for a build when started-notification disabled') 306 return 307 308 r = "build #%d of %s started, including [%s]" % \ 309 (build.getNumber(), 310 builder.getName(), 311 ", ".join([str(c.revision) for c in build.getChanges()]) 312 ) 313 314 self.send(r)315 316 results_descriptions = { 317 SUCCESS: "Success", 318 WARNINGS: "Warnings", 319 FAILURE: "Failure", 320 EXCEPTION: "Exception", 321 } 322324 builder = build.getBuilder() 325 326 # only notify about builders we are interested in 327 log.msg('[Contact] builder %r in category %s finished' % (builder, builder.category)) 328 329 if (self.channel.categories != None and 330 builder.category not in self.channel.categories): 331 return 332 333 if not self.notify_for_finished(build): 334 return 335 336 r = "build #%d of %s is complete: %s" % \ 337 (build.getNumber(), 338 builder.getName(), 339 self.results_descriptions.get(build.getResults(), "??")) 340 r += " [%s]" % " ".join(build.getText()) 341 buildurl = self.channel.status.getURLForThing(build) 342 if buildurl: 343 r += " Build details are at %s" % buildurl 344 345 if self.channel.showBlameList and build.getResults() != SUCCESS and len(build.changes) != 0: 346 r += ' blamelist: ' + ', '.join([c.who for c in build.changes]) 347 348 self.send(r)349351 results = build.getResults() 352 353 if self.notify_for('finished'): 354 return True 355 356 if self.notify_for(lower(self.results_descriptions.get(results))): 357 return True 358 359 prevBuild = build.getPreviousBuild() 360 if prevBuild: 361 prevResult = prevBuild.getResults() 362 363 required_notification_control_string = join((lower(self.results_descriptions.get(prevResult)), \ 364 'To', \ 365 capitalize(self.results_descriptions.get(results))), \ 366 '') 367 368 if (self.notify_for(required_notification_control_string)): 369 return True 370 371 return False372374 375 # only notify about builders we are interested in 376 builder = b.getBuilder() 377 log.msg('builder %r in category %s finished' % (builder, 378 builder.category)) 379 if (self.channel.categories != None and 380 builder.category not in self.channel.categories): 381 return 382 383 r = "Hey! build %s #%d is complete: %s" % \ 384 (b.getBuilder().getName(), 385 b.getNumber(), 386 self.results_descriptions.get(b.getResults(), "??")) 387 r += " [%s]" % " ".join(b.getText()) 388 self.send(r) 389 buildurl = self.channel.status.getURLForThing(b) 390 if buildurl: 391 self.send("Build details are at %s" % buildurl)392394 args = shlex.split(args) 395 if not args: 396 raise UsageError("try 'force build WHICH <REASON>'") 397 what = args.pop(0) 398 if what != "build": 399 raise UsageError("try 'force build WHICH <REASON>'") 400 opts = ForceOptions() 401 opts.parseOptions(args) 402 403 which = opts['builder'] 404 branch = opts['branch'] 405 revision = opts['revision'] 406 reason = opts['reason'] 407 408 if which is None: 409 raise UsageError("you must provide a Builder, " 410 "try 'force build WHICH <REASON>'") 411 412 # keep weird stuff out of the branch and revision strings. 413 # TODO: centralize this somewhere. 414 if branch and not re.match(r'^[\w\.\-\/]*$', branch): 415 log.msg("bad branch '%s'" % branch) 416 self.send("sorry, bad branch '%s'" % branch) 417 return 418 if revision and not re.match(r'^[\w\.\-\/]*$', revision): 419 log.msg("bad revision '%s'" % revision) 420 self.send("sorry, bad revision '%s'" % revision) 421 return 422 423 bc = self.getControl(which) 424 425 # TODO: maybe give certain users the ability to request builds of 426 # certain branches 427 reason = "forced: by %s: %s" % (self.describeUser(who), reason) 428 ss = SourceStamp(branch=branch, revision=revision) 429 try: 430 brs = bc.submitBuildRequest(ss, reason, now=True) 431 except interfaces.NoSlaveError: 432 self.send("sorry, I can't force a build: all slaves are offline") 433 return 434 ireq = IrcBuildRequest(self) 435 brs.subscribe(ireq.started)436 437 438 command_FORCE.usage = "force build <which> <reason> - Force a build" 439441 args = shlex.split(args) 442 if len(args) < 3 or args[0] != 'build': 443 raise UsageError, "try 'stop build WHICH <REASON>'" 444 which = args[1] 445 reason = args[2] 446 447 buildercontrol = self.getControl(which) 448 449 r = "stopped: by %s: %s" % (self.describeUser(who), reason) 450 451 # find an in-progress build 452 builderstatus = self.getBuilder(which) 453 builds = builderstatus.getCurrentBuilds() 454 if not builds: 455 self.send("sorry, no build is currently running") 456 return 457 for build in builds: 458 num = build.getNumber() 459 460 # obtain the BuildControl object 461 buildcontrol = buildercontrol.getBuild(num) 462 463 # make it stop 464 buildcontrol.stopBuild(r) 465 466 self.send("build %d interrupted" % num)467 468 command_STOP.usage = "stop build <which> <reason> - Stop a running build" 469471 b = self.getBuilder(which) 472 str = "%s: " % which 473 state, builds = b.getState() 474 str += state 475 if state == "idle": 476 last = b.getLastFinishedBuild() 477 if last: 478 start,finished = last.getTimes() 479 str += ", last build %s ago: %s" % \ 480 (self.convertTime(int(util.now() - finished)), " ".join(last.getText())) 481 if state == "building": 482 t = [] 483 for build in builds: 484 step = build.getCurrentStep() 485 if step: 486 s = "(%s)" % " ".join(step.getText()) 487 else: 488 s = "(no current step)" 489 ETA = build.getETA() 490 if ETA is not None: 491 s += " [ETA %s]" % self.convertTime(ETA) 492 t.append(s) 493 str += ", ".join(t) 494 self.send(str)495497 last = self.getBuilder(which).getLastFinishedBuild() 498 if not last: 499 str = "(no builds run since last restart)" 500 else: 501 start,finish = last.getTimes() 502 str = "%s ago: " % (self.convertTime(int(util.now() - finish))) 503 str += " ".join(last.getText()) 504 self.send("last build [%s]: %s" % (which, str))505507 args = shlex.split(args) 508 if len(args) == 0: 509 which = "all" 510 elif len(args) == 1: 511 which = args[0] 512 else: 513 raise UsageError, "try 'last <builder>'" 514 if which == "all": 515 builders = self.getAllBuilders() 516 for b in builders: 517 self.emit_last(b.name) 518 return 519 self.emit_last(which)520 command_LAST.usage = "last <which> - list last build status for builder <which>" 521523 commands = [] 524 for k in dir(self): 525 if k.startswith('command_'): 526 commands.append(k[8:].lower()) 527 commands.sort() 528 return commands529531 args = shlex.split(args) 532 if len(args) == 0: 533 self.send("Get help on what? (try 'help <foo>', or 'commands' for a command list)") 534 return 535 command = args[0] 536 meth = self.getCommandMethod(command) 537 if not meth: 538 raise UsageError, "no such command '%s'" % command 539 usage = getattr(meth, 'usage', None) 540 if usage: 541 self.send("Usage: %s" % usage) 542 else: 543 self.send("No usage info for '%s'" % command)544 command_HELP.usage = "help <command> - Give help for <command>" 545 549551 commands = self.build_commands() 552 str = "buildbot commands: " + ", ".join(commands) 553 self.send(str)554 command_COMMANDS.usage = "commands - List available commands" 555557 self.act("readies phasers")558560 reactor.callLater(1.0, self.send, "0-<") 561 reactor.callLater(3.0, self.send, "0-/") 562 reactor.callLater(3.5, self.send, "0-\\")563 567569 # this is sent when somebody performs an action that mentions the 570 # buildbot (like '/me kicks buildbot'). 'user' is the name/nick/id of 571 # the person who performed the action, so if their action provokes a 572 # response, they can be named. 573 if not data.endswith("s buildbot"): 574 return 575 words = data.split() 576 verb = words[-2] 577 timeout = 4 578 if verb == "kicks": 579 response = "%s back" % verb 580 timeout = 1 581 else: 582 response = "%s %s too" % (verb, user) 583 reactor.callLater(timeout, self.act, response)586 implements(IStatusReceiver) 587 588 # this is the IRC-specific subclass of Contact 589667591 Contact.__init__(self, channel) 592 # when people send us public messages ("buildbot: command"), 593 # self.dest is the name of the channel ("#twisted"). When they send 594 # us private messages (/msg buildbot command), self.dest is their 595 # username. 596 self.dest = dest597599 if self.dest[0] == '#': 600 return "IRC user <%s> on channel %s" % (user, self.dest) 601 return "IRC user <%s> (privmsg)" % user602 603 # userJoined(self, user, channel) 604606 self.channel.msgOrNotice(self.dest, message.encode("ascii", "replace"))607609 self.channel.me(self.dest, action.encode("ascii", "replace"))610612 args = shlex.split(args) 613 to_join = args[0] 614 self.channel.join(to_join) 615 self.send("Joined %s" % to_join)616 command_JOIN.usage = "join channel - Join another channel" 617619 args = shlex.split(args) 620 to_leave = args[0] 621 self.send("Buildbot has been told to leave %s" % to_leave) 622 self.channel.part(to_leave)623 command_LEAVE.usage = "leave channel - Leave a channel" 624 625627 # a message has arrived from 'who'. For broadcast contacts (i.e. when 628 # people do an irc 'buildbot: command'), this will be a string 629 # describing the sender of the message in some useful-to-log way, and 630 # a single Contact may see messages from a variety of users. For 631 # unicast contacts (i.e. when people do an irc '/msg buildbot 632 # command'), a single Contact will only ever see messages from a 633 # single user. 634 message = message.lstrip() 635 if self.silly.has_key(message): 636 return self.doSilly(message) 637 638 parts = message.split(' ', 1) 639 if len(parts) == 1: 640 parts = parts + [''] 641 cmd, args = parts 642 log.msg("irc command", cmd) 643 644 meth = self.getCommandMethod(cmd) 645 if not meth and message[-1] == '!': 646 meth = self.command_EXCITED 647 648 error = None 649 try: 650 if meth: 651 meth(args.strip(), who) 652 except UsageError, e: 653 self.send(str(e)) 654 except: 655 f = failure.Failure() 656 log.err(f) 657 error = "Something bad happened (see logs): %s" % f.type 658 659 if error: 660 try: 661 self.send(error) 662 except: 663 log.err() 664 665 #self.say(channel, "count %d" % self.counter) 666 self.channel.counter += 1669 """I represent the buildbot's presence in a particular IM scheme. 670 671 This provides the connection to the IRC server, or represents the 672 buildbot's account with an IM service. Each Channel will have zero or 673 more Contacts associated with it. 674 """675677 """I represent the buildbot to an IRC server. 678 """ 679 implements(IChannel) 680 contactClass = IRCContact 681779 780 # we can using the following irc.IRCClient methods to send output. Most 781 # of these are used by the IRCContact class. 782 # 783 # self.say(channel, message) # broadcast 784 # self.msg(user, message) # unicast 785 # self.me(channel, action) # send action 786 # self.away(message='') 787 # self.quit(message='') 788 796682 - def __init__(self, nickname, password, channels, status, categories, notify_events, noticeOnChannel = False, showBlameList = False):683 """ 684 @type nickname: string 685 @param nickname: the nickname by which this bot should be known 686 @type password: string 687 @param password: the password to use for identifying with Nickserv 688 @type channels: list of strings 689 @param channels: the bot will maintain a presence in these channels 690 @type status: L{buildbot.status.builder.Status} 691 @param status: the build master's Status object, through which the 692 bot retrieves all status information 693 """ 694 self.nickname = nickname 695 self.channels = channels 696 self.password = password 697 self.status = status 698 self.categories = categories 699 self.notify_events = notify_events 700 self.counter = 0 701 self.hasQuit = 0 702 self.contacts = {} 703 self.noticeOnChannel = noticeOnChannel 704 self.showBlameList = showBlameList705707 if self.noticeOnChannel and dest[0] == '#': 708 self.notice(dest, message) 709 else: 710 self.msg(dest, message)711713 self.contacts[name] = contact714716 if name in self.contacts: 717 return self.contacts[name] 718 new_contact = self.contactClass(self, name) 719 self.contacts[name] = new_contact 720 return new_contact721723 name = contact.getName() 724 if name in self.contacts: 725 assert self.contacts[name] == contact 726 del self.contacts[name]727729 log.msg("%s: %s" % (self, msg))730 731 732 # the following irc.IRCClient methods are called when we have input 733735 user = user.split('!', 1)[0] # rest is ~user@hostname 736 # channel is '#twisted' or 'buildbot' (for private messages) 737 channel = channel.lower() 738 #print "privmsg:", user, channel, message 739 if channel == self.nickname: 740 # private message 741 contact = self.getContact(user) 742 contact.handleMessage(message, user) 743 return 744 # else it's a broadcast message, maybe for us, maybe not. 'channel' 745 # is '#twisted' or the like. 746 contact = self.getContact(channel) 747 if message.startswith("%s:" % self.nickname) or message.startswith("%s," % self.nickname): 748 message = message[len("%s:" % self.nickname):] 749 contact.handleMessage(message, user)750 # to track users comings and goings, add code here 751753 #log.msg("action: %s,%s,%s" % (user, channel, data)) 754 user = user.split('!', 1)[0] # rest is ~user@hostname 755 # somebody did an action (/me actions) in the broadcast channel 756 contact = self.getContact(channel) 757 if "buildbot" in data: 758 contact.handleAction(data, user)759 760 761763 if self.password: 764 self.msg("Nickserv", "IDENTIFY " + self.password) 765 for c in self.channels: 766 self.join(c)767769 self.log("I have joined %s" % (channel,)) 770 # trigger contact contructor, which in turn subscribes to notify events 771 self.getContact(channel)772774 self.log("I have left %s" % (channel,))798 protocol = IrcStatusBot 799 800 status = None 801 control = None 802 shuttingDown = False 803 p = None 804852 853805 - def __init__(self, nickname, password, channels, categories, notify_events, noticeOnChannel = False, showBlameList = False):806 #ThrottledClientFactory.__init__(self) # doesn't exist 807 self.status = None 808 self.nickname = nickname 809 self.password = password 810 self.channels = channels 811 self.categories = categories 812 self.notify_events = notify_events 813 self.noticeOnChannel = noticeOnChannel 814 self.showBlameList = showBlameList815 820822 self.shuttingDown = True 823 if self.p: 824 self.p.quit("buildmaster reconfigured: bot disconnecting")825827 p = self.protocol(self.nickname, self.password, 828 self.channels, self.status, 829 self.categories, self.notify_events, 830 noticeOnChannel = self.noticeOnChannel, 831 showBlameList = self.showBlameList) 832 p.factory = self 833 p.status = self.status 834 p.control = self.control 835 self.p = p 836 return p837 838 # TODO: I think a shutdown that occurs while the connection is being 839 # established will make this explode 840842 if self.shuttingDown: 843 log.msg("not scheduling reconnection attempt") 844 return 845 ThrottledClientFactory.clientConnectionLost(self, connector, reason)846848 if self.shuttingDown: 849 log.msg("not scheduling reconnection attempt") 850 return 851 ThrottledClientFactory.clientConnectionFailed(self, connector, reason)855 implements(IStatusReceiver) 856 """I am an IRC bot which can be queried for status information. I 857 connect to a single IRC server and am known by a single nickname on that 858 server, however I can join multiple channels.""" 859 860 in_test_harness = False 861 862 compare_attrs = ["host", "port", "nick", "password", 863 "channels", "allowForce", "useSSL", 864 "categories"] 865914 915 916 ## buildbot: list builders 917 # buildbot: watch quick 918 # print notification when current build in 'quick' finishes 919 ## buildbot: status 920 ## buildbot: status full-2.3 921 ## building, not, % complete, ETA 922 ## buildbot: force build full-2.3 "reason" 923866 - def __init__(self, host, nick, channels, port=6667, allowForce=True, 867 categories=None, password=None, notify_events={}, 868 noticeOnChannel = False, showBlameList = True, 869 useSSL=False):870 base.StatusReceiverMultiService.__init__(self) 871 872 assert allowForce in (True, False) # TODO: implement others 873 874 # need to stash these so we can detect changes later 875 self.host = host 876 self.port = port 877 self.nick = nick 878 self.channels = channels 879 self.password = password 880 self.allowForce = allowForce 881 self.categories = categories 882 self.notify_events = notify_events 883 log.msg('Notify events %s' % notify_events) 884 self.f = IrcStatusFactory(self.nick, self.password, 885 self.channels, self.categories, self.notify_events, 886 noticeOnChannel = noticeOnChannel, 887 showBlameList = showBlameList) 888 889 # don't set up an actual ClientContextFactory if we're running tests. 890 if self.in_test_harness: 891 return 892 893 if useSSL: 894 # SSL client needs a ClientContextFactory for some SSL mumbo-jumbo 895 if not have_ssl: 896 raise RuntimeError("useSSL requires PyOpenSSL") 897 cf = ssl.ClientContextFactory() 898 c = internet.SSLClient(self.host, self.port, self.f, cf) 899 else: 900 c = internet.TCPClient(self.host, self.port, self.f) 901 902 c.setServiceParent(self)903905 base.StatusReceiverMultiService.setServiceParent(self, parent) 906 self.f.status = parent.getStatus() 907 if self.allowForce: 908 self.f.control = interfaces.IControl(parent)909911 # make sure the factory will stop reconnecting 912 self.f.shutdown() 913 return base.StatusReceiverMultiService.stopService(self)
Home | Trees | Indices | Help |
|
---|
Generated by Epydoc 3.0.1 on Tue May 25 17:53:42 2010 | http://epydoc.sourceforge.net |