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 """ 7360975 #StatusReceiver.__init__(self) doesn't exist 76 self.channel = channel 77 self.notify_events = {} 78 self.subscribed = 0 79 self.reported_builds = [] # tuples (when, buildername, buildnum) 80 self.add_notification_events(channel.notify_events)81 82 silly = { 83 "What happen ?": "Somebody set up us the bomb.", 84 "It's You !!": ["How are you gentlemen !!", 85 "All your base are belong to us.", 86 "You are on the way to destruction."], 87 "What you say !!": ["You have no chance to survive make your time.", 88 "HA HA HA HA ...."], 89 } 90 9496 try: 97 b = self.channel.status.getBuilder(which) 98 except KeyError: 99 raise UsageError, "no such builder '%s'" % which 100 return b101103 if not self.channel.control: 104 raise UsageError("builder control is not enabled") 105 try: 106 bc = self.channel.control.getBuilder(which) 107 except KeyError: 108 raise UsageError("no such builder '%s'" % which) 109 return bc110112 """ 113 @rtype: list of L{buildbot.process.builder.Builder} 114 """ 115 names = self.channel.status.getBuilderNames(categories=self.channel.categories) 116 names.sort() 117 builders = [self.channel.status.getBuilder(n) for n in names] 118 return builders119121 if seconds < 60: 122 return "%d seconds" % seconds 123 minutes = int(seconds / 60) 124 seconds = seconds - 60*minutes 125 if minutes < 60: 126 return "%dm%02ds" % (minutes, seconds) 127 hours = int(minutes / 60) 128 minutes = minutes - 60*hours 129 return "%dh%02dm%02ds" % (hours, minutes, seconds)130132 """Returns True if this build should be reported for this contact 133 (eliminating duplicates), and also records the report for later""" 134 for w, b, n in self.reported_builds: 135 if b == builder and n == buildnum: 136 return False 137 self.reported_builds.append([util.now(), builder, buildnum]) 138 139 # clean the reported builds 140 horizon = util.now() - 60 141 while self.reported_builds and self.reported_builds[0][0] < horizon: 142 self.reported_builds.pop(0) 143 144 # and return True, since this is a new one 145 return True146148 response = self.silly[message] 149 if type(response) != type([]): 150 response = [response] 151 when = 0.5 152 for r in response: 153 reactor.callLater(when, self.send, r) 154 when += 2.5155157 self.send("yes?")158 161163 args = shlex.split(args) 164 if len(args) == 0: 165 raise UsageError, "try 'list builders'" 166 if args[0] == 'builders': 167 builders = self.getAllBuilders() 168 str = "Configured builders: " 169 for b in builders: 170 str += b.name 171 state = b.getState()[0] 172 if state == 'offline': 173 str += "[offline]" 174 str += " " 175 str.rstrip() 176 self.send(str) 177 return178 command_LIST.usage = "list builders - List configured builders" 179181 args = shlex.split(args) 182 if len(args) == 0: 183 which = "all" 184 elif len(args) == 1: 185 which = args[0] 186 else: 187 raise UsageError, "try 'status <builder>'" 188 if which == "all": 189 builders = self.getAllBuilders() 190 for b in builders: 191 self.emit_status(b.name) 192 return 193 self.emit_status(which)194 command_STATUS.usage = "status [<which>] - List status of a builder (or all builders)" 195197 if not re.compile("^(started|finished|success|failure|exception|warnings|(success|warnings|exception|failure)To(Failure|Success|Warnings|Exception))$").match(event): 198 raise UsageError("try 'notify on|off <EVENT>'")199201 self.send( "The following events are being notified: %r" % self.notify_events.keys() )202 208 212 216218 for event in events: 219 self.validate_notification_event(event) 220 self.notify_events[event] = 1 221 222 if not self.subscribed: 223 self.subscribe_to_build_events()224226 for event in events: 227 self.validate_notification_event(event) 228 del self.notify_events[event] 229 230 if len(self.notify_events) == 0 and self.subscribed: 231 self.unsubscribe_from_build_events()232 238240 args = shlex.split(args) 241 242 if not args: 243 raise UsageError("try 'notify on|off|list <EVENT>'") 244 action = args.pop(0) 245 events = args 246 247 if action == "on": 248 if not events: events = ('started','finished') 249 self.add_notification_events(events) 250 251 self.list_notified_events() 252 253 elif action == "off": 254 if events: 255 self.remove_notification_events(events) 256 else: 257 self.remove_all_notification_events() 258 259 self.list_notified_events() 260 261 elif action == "list": 262 self.list_notified_events() 263 return 264 265 else: 266 raise UsageError("try 'notify on|off <EVENT>'")267 268 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)" 269271 args = shlex.split(args) 272 if len(args) != 1: 273 raise UsageError("try 'watch <builder>'") 274 which = args[0] 275 b = self.getBuilder(which) 276 builds = b.getCurrentBuilds() 277 if not builds: 278 self.send("there are no builds currently running") 279 return 280 for build in builds: 281 assert not build.isFinished() 282 d = build.waitUntilFinished() 283 d.addCallback(self.watchedBuildFinished) 284 r = "watching build %s #%d until it finishes" \ 285 % (which, build.getNumber()) 286 eta = build.getETA() 287 if eta is not None: 288 r += " [%s]" % self.convertTime(eta) 289 r += ".." 290 self.send(r)291 command_WATCH.usage = "watch <which> - announce the completion of an active build" 292294 log.msg('[Contact] Buildset %s added' % (buildset))295 299301 log.msg('[Contact] Builder %s changed state to %s' % (builderName, state))302304 log.msg('[Contact] BuildRequest %d for %s submitted' % 305 (brstatus.brid, brstatus.getSourceStamp()))306308 log.msg('[Contact] Builder %s removed' % (builderName))309311 builder = build.getBuilder() 312 log.msg('[Contact] Builder %r in category %s started' % (builder, builder.category)) 313 314 # only notify about builders we are interested in 315 316 if (self.channel.categories != None and 317 builder.category not in self.channel.categories): 318 log.msg('Not notifying for a build in the wrong category') 319 return 320 321 if not self.notify_for('started'): 322 log.msg('Not notifying for a build when started-notification disabled') 323 return 324 325 r = "build #%d of %s started, including [%s]" % \ 326 (build.getNumber(), 327 builder.getName(), 328 ", ".join([str(c.revision) for c in build.getChanges()]) 329 ) 330 331 self.send(r)332 333 results_descriptions = { 334 SUCCESS: "Success", 335 WARNINGS: "Warnings", 336 FAILURE: "Failure", 337 EXCEPTION: "Exception", 338 } 339341 builder = build.getBuilder() 342 343 # only notify about builders we are interested in 344 log.msg('[Contact] builder %r in category %s finished' % (builder, builder.category)) 345 346 if (self.channel.categories != None and 347 builder.category not in self.channel.categories): 348 return 349 350 if not self.notify_for_finished(build): 351 return 352 353 builder_name = builder.getName() 354 buildnum = build.getNumber() 355 356 if self.reportBuild(builder_name, buildnum): 357 r = "build #%d of %s is complete: %s" % \ 358 (buildnum, 359 builder_name, 360 self.results_descriptions.get(build.getResults(), "??")) 361 r += " [%s]" % " ".join(build.getText()) 362 buildurl = self.channel.status.getURLForThing(build) 363 if buildurl: 364 r += " Build details are at %s" % buildurl 365 366 if self.channel.showBlameList and build.getResults() != SUCCESS and len(build.changes) != 0: 367 r += ' blamelist: ' + ', '.join(list(set([c.who for c in build.changes]))) 368 369 self.send(r)370372 results = build.getResults() 373 374 if self.notify_for('finished'): 375 return True 376 377 if self.notify_for(lower(self.results_descriptions.get(results))): 378 return True 379 380 prevBuild = build.getPreviousBuild() 381 if prevBuild: 382 prevResult = prevBuild.getResults() 383 384 required_notification_control_string = join((lower(self.results_descriptions.get(prevResult)), \ 385 'To', \ 386 capitalize(self.results_descriptions.get(results))), \ 387 '') 388 389 if (self.notify_for(required_notification_control_string)): 390 return True 391 392 return False393395 396 # only notify about builders we are interested in 397 builder = b.getBuilder() 398 log.msg('builder %r in category %s finished' % (builder, 399 builder.category)) 400 if (self.channel.categories != None and 401 builder.category not in self.channel.categories): 402 return 403 404 builder_name = b.getBuilder().getName() 405 buildnum = b.getNumber() 406 407 if self.reportBuild(builder_name, buildnum): 408 r = "Hey! build %s #%d is complete: %s" % \ 409 (builder_name, 410 buildnum, 411 self.results_descriptions.get(b.getResults(), "??")) 412 r += " [%s]" % " ".join(b.getText()) 413 self.send(r) 414 buildurl = self.channel.status.getURLForThing(b) 415 if buildurl: 416 self.send("Build details are at %s" % buildurl)417419 args = shlex.split(args) 420 if not args: 421 raise UsageError("try 'force build WHICH <REASON>'") 422 what = args.pop(0) 423 if what != "build": 424 raise UsageError("try 'force build WHICH <REASON>'") 425 opts = ForceOptions() 426 opts.parseOptions(args) 427 428 which = opts['builder'] 429 branch = opts['branch'] 430 revision = opts['revision'] 431 reason = opts['reason'] 432 433 if which is None: 434 raise UsageError("you must provide a Builder, " 435 "try 'force build WHICH <REASON>'") 436 437 # keep weird stuff out of the branch and revision strings. 438 # TODO: centralize this somewhere. 439 if branch and not re.match(r'^[\w\.\-\/]*$', branch): 440 log.msg("bad branch '%s'" % branch) 441 self.send("sorry, bad branch '%s'" % branch) 442 return 443 if revision and not re.match(r'^[\w\.\-\/]*$', revision): 444 log.msg("bad revision '%s'" % revision) 445 self.send("sorry, bad revision '%s'" % revision) 446 return 447 448 bc = self.getControl(which) 449 450 # TODO: maybe give certain users the ability to request builds of 451 # certain branches 452 reason = "forced: by %s: %s" % (self.describeUser(who), reason) 453 ss = SourceStamp(branch=branch, revision=revision) 454 try: 455 brs = bc.submitBuildRequest(ss, reason, now=True) 456 except interfaces.NoSlaveError: 457 self.send("sorry, I can't force a build: all slaves are offline") 458 return 459 ireq = IrcBuildRequest(self) 460 brs.subscribe(ireq.started)461 462 463 command_FORCE.usage = "force build <which> <reason> - Force a build" 464466 args = shlex.split(args) 467 if len(args) < 3 or args[0] != 'build': 468 raise UsageError, "try 'stop build WHICH <REASON>'" 469 which = args[1] 470 reason = args[2] 471 472 buildercontrol = self.getControl(which) 473 474 r = "stopped: by %s: %s" % (self.describeUser(who), reason) 475 476 # find an in-progress build 477 builderstatus = self.getBuilder(which) 478 builds = builderstatus.getCurrentBuilds() 479 if not builds: 480 self.send("sorry, no build is currently running") 481 return 482 for build in builds: 483 num = build.getNumber() 484 485 # obtain the BuildControl object 486 buildcontrol = buildercontrol.getBuild(num) 487 488 # make it stop 489 buildcontrol.stopBuild(r) 490 491 self.send("build %d interrupted" % num)492 493 command_STOP.usage = "stop build <which> <reason> - Stop a running build" 494496 b = self.getBuilder(which) 497 str = "%s: " % which 498 state, builds = b.getState() 499 str += state 500 if state == "idle": 501 last = b.getLastFinishedBuild() 502 if last: 503 start,finished = last.getTimes() 504 str += ", last build %s ago: %s" % \ 505 (self.convertTime(int(util.now() - finished)), " ".join(last.getText())) 506 if state == "building": 507 t = [] 508 for build in builds: 509 step = build.getCurrentStep() 510 if step: 511 s = "(%s)" % " ".join(step.getText()) 512 else: 513 s = "(no current step)" 514 ETA = build.getETA() 515 if ETA is not None: 516 s += " [ETA %s]" % self.convertTime(ETA) 517 t.append(s) 518 str += ", ".join(t) 519 self.send(str)520522 last = self.getBuilder(which).getLastFinishedBuild() 523 if not last: 524 str = "(no builds run since last restart)" 525 else: 526 start,finish = last.getTimes() 527 str = "%s ago: " % (self.convertTime(int(util.now() - finish))) 528 str += " ".join(last.getText()) 529 self.send("last build [%s]: %s" % (which, str))530532 args = shlex.split(args) 533 if len(args) == 0: 534 which = "all" 535 elif len(args) == 1: 536 which = args[0] 537 else: 538 raise UsageError, "try 'last <builder>'" 539 if which == "all": 540 builders = self.getAllBuilders() 541 for b in builders: 542 self.emit_last(b.name) 543 return 544 self.emit_last(which)545 command_LAST.usage = "last <which> - list last build status for builder <which>" 546548 commands = [] 549 for k in dir(self): 550 if k.startswith('command_'): 551 commands.append(k[8:].lower()) 552 commands.sort() 553 return commands554556 args = shlex.split(args) 557 if len(args) == 0: 558 self.send("Get help on what? (try 'help <foo>', or 'commands' for a command list)") 559 return 560 command = args[0] 561 meth = self.getCommandMethod(command) 562 if not meth: 563 raise UsageError, "no such command '%s'" % command 564 usage = getattr(meth, 'usage', None) 565 if usage: 566 self.send("Usage: %s" % usage) 567 else: 568 self.send("No usage info for '%s'" % command)569 command_HELP.usage = "help <command> - Give help for <command>" 570 574576 commands = self.build_commands() 577 str = "buildbot commands: " + ", ".join(commands) 578 self.send(str)579 command_COMMANDS.usage = "commands - List available commands" 580582 self.act("readies phasers")583585 reactor.callLater(1.0, self.send, "0-<") 586 reactor.callLater(3.0, self.send, "0-/") 587 reactor.callLater(3.5, self.send, "0-\\")588 592594 # this is sent when somebody performs an action that mentions the 595 # buildbot (like '/me kicks buildbot'). 'user' is the name/nick/id of 596 # the person who performed the action, so if their action provokes a 597 # response, they can be named. 598 if not data.endswith("s buildbot"): 599 return 600 words = data.split() 601 verb = words[-2] 602 timeout = 4 603 if verb == "kicks": 604 response = "%s back" % verb 605 timeout = 1 606 else: 607 response = "%s %s too" % (verb, user) 608 reactor.callLater(timeout, self.act, response)611 implements(IStatusReceiver) 612 613 # this is the IRC-specific subclass of Contact 614677616 Contact.__init__(self, channel) 617 # when people send us public messages ("buildbot: command"), 618 # self.dest is the name of the channel ("#twisted"). When they send 619 # us private messages (/msg buildbot command), self.dest is their 620 # username. 621 self.dest = dest622624 if self.dest[0] == '#': 625 return "IRC user <%s> on channel %s" % (user, self.dest) 626 return "IRC user <%s> (privmsg)" % user627 628 # userJoined(self, user, channel) 629631 self.channel.msgOrNotice(self.dest, message.encode("ascii", "replace"))632634 self.channel.me(self.dest, action.encode("ascii", "replace"))635637 # a message has arrived from 'who'. For broadcast contacts (i.e. when 638 # people do an irc 'buildbot: command'), this will be a string 639 # describing the sender of the message in some useful-to-log way, and 640 # a single Contact may see messages from a variety of users. For 641 # unicast contacts (i.e. when people do an irc '/msg buildbot 642 # command'), a single Contact will only ever see messages from a 643 # single user. 644 message = message.lstrip() 645 if self.silly.has_key(message): 646 return self.doSilly(message) 647 648 parts = message.split(' ', 1) 649 if len(parts) == 1: 650 parts = parts + [''] 651 cmd, args = parts 652 log.msg("irc command", cmd) 653 654 meth = self.getCommandMethod(cmd) 655 if not meth and message[-1] == '!': 656 meth = self.command_EXCITED 657 658 error = None 659 try: 660 if meth: 661 meth(args.strip(), who) 662 except UsageError, e: 663 self.send(str(e)) 664 except: 665 f = failure.Failure() 666 log.err(f) 667 error = "Something bad happened (see logs): %s" % f.type 668 669 if error: 670 try: 671 self.send(error) 672 except: 673 log.err() 674 675 #self.say(channel, "count %d" % self.counter) 676 self.channel.counter += 1679 """I represent the buildbot's presence in a particular IM scheme. 680 681 This provides the connection to the IRC server, or represents the 682 buildbot's account with an IM service. Each Channel will have zero or 683 more Contacts associated with it. 684 """685687 """I represent the buildbot to an IRC server. 688 """ 689 implements(IChannel) 690 contactClass = IRCContact 691789 790 # we can using the following irc.IRCClient methods to send output. Most 791 # of these are used by the IRCContact class. 792 # 793 # self.say(channel, message) # broadcast 794 # self.msg(user, message) # unicast 795 # self.me(channel, action) # send action 796 # self.away(message='') 797 # self.quit(message='') 798 806692 - def __init__(self, nickname, password, channels, status, categories, notify_events, noticeOnChannel = False, showBlameList = False):693 """ 694 @type nickname: string 695 @param nickname: the nickname by which this bot should be known 696 @type password: string 697 @param password: the password to use for identifying with Nickserv 698 @type channels: list of strings 699 @param channels: the bot will maintain a presence in these channels 700 @type status: L{buildbot.status.builder.Status} 701 @param status: the build master's Status object, through which the 702 bot retrieves all status information 703 """ 704 self.nickname = nickname 705 self.channels = channels 706 self.password = password 707 self.status = status 708 self.categories = categories 709 self.notify_events = notify_events 710 self.counter = 0 711 self.hasQuit = 0 712 self.contacts = {} 713 self.noticeOnChannel = noticeOnChannel 714 self.showBlameList = showBlameList715717 if self.noticeOnChannel and dest[0] == '#': 718 self.notice(dest, message) 719 else: 720 self.msg(dest, message)721723 self.contacts[name] = contact724726 if name in self.contacts: 727 return self.contacts[name] 728 new_contact = self.contactClass(self, name) 729 self.contacts[name] = new_contact 730 return new_contact731733 name = contact.getName() 734 if name in self.contacts: 735 assert self.contacts[name] == contact 736 del self.contacts[name]737739 log.msg("%s: %s" % (self, msg))740 741 742 # the following irc.IRCClient methods are called when we have input 743745 user = user.split('!', 1)[0] # rest is ~user@hostname 746 # channel is '#twisted' or 'buildbot' (for private messages) 747 channel = channel.lower() 748 #print "privmsg:", user, channel, message 749 if channel == self.nickname: 750 # private message 751 contact = self.getContact(user) 752 contact.handleMessage(message, user) 753 return 754 # else it's a broadcast message, maybe for us, maybe not. 'channel' 755 # is '#twisted' or the like. 756 contact = self.getContact(channel) 757 if message.startswith("%s:" % self.nickname) or message.startswith("%s," % self.nickname): 758 message = message[len("%s:" % self.nickname):] 759 contact.handleMessage(message, user)760 # to track users comings and goings, add code here 761763 #log.msg("action: %s,%s,%s" % (user, channel, data)) 764 user = user.split('!', 1)[0] # rest is ~user@hostname 765 # somebody did an action (/me actions) in the broadcast channel 766 contact = self.getContact(channel) 767 if "buildbot" in data: 768 contact.handleAction(data, user)769 770 771773 if self.password: 774 self.msg("Nickserv", "IDENTIFY " + self.password) 775 for c in self.channels: 776 self.join(c)777779 self.log("I have joined %s" % (channel,)) 780 # trigger contact contructor, which in turn subscribes to notify events 781 self.getContact(channel)782784 self.log("I have left %s" % (channel,))808 protocol = IrcStatusBot 809 810 status = None 811 control = None 812 shuttingDown = False 813 p = None 814862 863815 - def __init__(self, nickname, password, channels, categories, notify_events, noticeOnChannel = False, showBlameList = False):816 #ThrottledClientFactory.__init__(self) # doesn't exist 817 self.status = None 818 self.nickname = nickname 819 self.password = password 820 self.channels = channels 821 self.categories = categories 822 self.notify_events = notify_events 823 self.noticeOnChannel = noticeOnChannel 824 self.showBlameList = showBlameList825 830832 self.shuttingDown = True 833 if self.p: 834 self.p.quit("buildmaster reconfigured: bot disconnecting")835837 p = self.protocol(self.nickname, self.password, 838 self.channels, self.status, 839 self.categories, self.notify_events, 840 noticeOnChannel = self.noticeOnChannel, 841 showBlameList = self.showBlameList) 842 p.factory = self 843 p.status = self.status 844 p.control = self.control 845 self.p = p 846 return p847 848 # TODO: I think a shutdown that occurs while the connection is being 849 # established will make this explode 850852 if self.shuttingDown: 853 log.msg("not scheduling reconnection attempt") 854 return 855 ThrottledClientFactory.clientConnectionLost(self, connector, reason)856858 if self.shuttingDown: 859 log.msg("not scheduling reconnection attempt") 860 return 861 ThrottledClientFactory.clientConnectionFailed(self, connector, reason)865 implements(IStatusReceiver) 866 """I am an IRC bot which can be queried for status information. I 867 connect to a single IRC server and am known by a single nickname on that 868 server, however I can join multiple channels.""" 869 870 in_test_harness = False 871 872 compare_attrs = ["host", "port", "nick", "password", 873 "channels", "allowForce", "useSSL", 874 "categories"] 875924 925 926 ## buildbot: list builders 927 # buildbot: watch quick 928 # print notification when current build in 'quick' finishes 929 ## buildbot: status 930 ## buildbot: status full-2.3 931 ## building, not, % complete, ETA 932 ## buildbot: force build full-2.3 "reason" 933876 - def __init__(self, host, nick, channels, port=6667, allowForce=True, 877 categories=None, password=None, notify_events={}, 878 noticeOnChannel = False, showBlameList = True, 879 useSSL=False):880 base.StatusReceiverMultiService.__init__(self) 881 882 assert allowForce in (True, False) # TODO: implement others 883 884 # need to stash these so we can detect changes later 885 self.host = host 886 self.port = port 887 self.nick = nick 888 self.channels = channels 889 self.password = password 890 self.allowForce = allowForce 891 self.categories = categories 892 self.notify_events = notify_events 893 log.msg('Notify events %s' % notify_events) 894 self.f = IrcStatusFactory(self.nick, self.password, 895 self.channels, self.categories, self.notify_events, 896 noticeOnChannel = noticeOnChannel, 897 showBlameList = showBlameList) 898 899 # don't set up an actual ClientContextFactory if we're running tests. 900 if self.in_test_harness: 901 return 902 903 if useSSL: 904 # SSL client needs a ClientContextFactory for some SSL mumbo-jumbo 905 if not have_ssl: 906 raise RuntimeError("useSSL requires PyOpenSSL") 907 cf = ssl.ClientContextFactory() 908 c = internet.SSLClient(self.host, self.port, self.f, cf) 909 else: 910 c = internet.TCPClient(self.host, self.port, self.f) 911 912 c.setServiceParent(self)913915 base.StatusReceiverMultiService.setServiceParent(self, parent) 916 self.f.status = parent.getStatus() 917 if self.allowForce: 918 self.f.control = interfaces.IControl(parent)919921 # make sure the factory will stop reconnecting 922 self.f.shutdown() 923 return base.StatusReceiverMultiService.stopService(self)
Trees | Indices | Help |
|
---|
Generated by Epydoc 3.0.1 on Fri Oct 29 10:00:50 2010 | http://epydoc.sourceforge.net |