예제 #1
0
def getPorts(app, config):
    ports = []
    if config.telnet:
        from twisted.protocols import telnet
        factory = telnet.ShellFactory()
        ports.append((int(config.telnet), factory))
    try:
        root = config.root
        config.root.indexName = config.index
    except AttributeError:
        # This really ought to be web.Admin or something
        root = test.Test()

    site = server.Site(root)

    if config.personal:
        import pwd, os

        pw_name, pw_passwd, pw_uid, pw_gid, pw_gecos, pw_dir, pw_shell \
                 = pwd.getpwuid(os.getuid())
        ports.append((os.path.join(pw_dir,
                                   distrib.UserDirectory.userSocketName),
                      pb.BrokerFactory(distrib.ResourcePublisher(site))))
    else:
        ports.append((int(config.port), site))
    return ports
예제 #2
0
    def _setupDistribServer(self, child):
        """
        Set up a resource on a distrib site using L{ResourcePublisher}.

        @param child: The resource to publish using distrib.

        @return: A tuple consisting of the host and port on which to contact
            the created site.
        """
        distribRoot = resource.Resource()
        distribRoot.putChild("child", child)
        distribSite = server.Site(distribRoot)
        self.f1 = distribFactory = PBServerFactory(
            distrib.ResourcePublisher(distribSite))
        distribPort = reactor.listenTCP(
            0, distribFactory, interface="127.0.0.1")
        self.addCleanup(distribPort.stopListening)
        addr = distribPort.getHost()

        self.sub = mainRoot = distrib.ResourceSubscription(
            addr.host, addr.port)
        mainSite = server.Site(mainRoot)
        mainPort = reactor.listenTCP(0, mainSite, interface="127.0.0.1")
        self.addCleanup(mainPort.stopListening)
        mainAddr = mainPort.getHost()

        return mainPort, mainAddr
예제 #3
0
    def _requestTest(self, child, **kwargs):
        """
        Set up a resource on a distrib site using L{ResourcePublisher} and
        then retrieve it from a L{ResourceSubscription} via an HTTP client.

        @param child: The resource to publish using distrib.
        @param **kwargs: Extra keyword arguments to pass to L{getPage} when
            requesting the resource.

        @return: A L{Deferred} which fires with the result of the request.
        """
        distribRoot = resource.Resource()
        distribRoot.putChild("child", child)
        distribSite = server.Site(distribRoot)
        self.f1 = distribFactory = PBServerFactory(
            distrib.ResourcePublisher(distribSite))
        distribPort = reactor.listenTCP(0,
                                        distribFactory,
                                        interface="127.0.0.1")
        self.addCleanup(distribPort.stopListening)
        addr = distribPort.getHost()

        self.sub = mainRoot = distrib.ResourceSubscription(
            addr.host, addr.port)
        mainSite = server.Site(mainRoot)
        mainPort = reactor.listenTCP(0, mainSite, interface="127.0.0.1")
        self.addCleanup(mainPort.stopListening)
        mainAddr = mainPort.getHost()

        return client.getPage(
            "http://%s:%s/child" % (mainAddr.host, mainAddr.port), **kwargs)
예제 #4
0
    def testDistrib(self):
        # site1 is the publisher
        r1 = resource.Resource()
        r1.putChild("there", static.Data("root", "text/plain"))
        site1 = server.Site(r1)
        f1 = pb.PBServerFactory(distrib.ResourcePublisher(site1))
        self.port1 = reactor.listenTCP(0, f1)

        util.spinUntil(lambda: self.port1.connected)

        # site2 is the subscriber
        sub = distrib.ResourceSubscription("127.0.0.1",
                                           self.port1.getHost().port)
        r2 = resource.Resource()
        r2.putChild("here", sub)
        f2 = MySite(r2)
        self.port2 = reactor.listenTCP(0, f2)

        util.spinUntil(lambda: self.port2.connected)

        # then we hit site2 with a client
        d = client.getPage("http://127.0.0.1:%d/here/there" % \
                           self.port2.getHost().port)
        res = util.wait(d, timeout=1.0)
        self.failUnlessEqual(res, "root")

        # A bit of a hack: force the pb client to disconnect, for cleanup
        # purposes.
        sub.publisher.broker.transport.loseConnection()
예제 #5
0
def makePersonalServerFactory(site):
    """
    Create and return a factory which will respond to I{distrib} requests
    against the given site.

    @type site: L{twisted.web.server.Site}
    @rtype: L{twisted.internet.protocol.Factory}
    """
    return pb.PBServerFactory(distrib.ResourcePublisher(site))
예제 #6
0
 def testDistrib(self):
     # site1 is the publisher
     r1 = resource.Resource()
     r1.putChild("there", static.Data("root", "text/plain"))
     site1 = server.Site(r1)
     self.f1 = PBServerFactory(distrib.ResourcePublisher(site1))
     self.port1 = reactor.listenTCP(0, self.f1)
     self.sub = distrib.ResourceSubscription("127.0.0.1",
                                             self.port1.getHost().port)
     r2 = resource.Resource()
     r2.putChild("here", self.sub)
     f2 = MySite(r2)
     self.port2 = reactor.listenTCP(0, f2)
     d = client.getPage("http://127.0.0.1:%d/here/there" % \
                        self.port2.getHost().port)
     d.addCallback(self.assertEqual, 'root')
     return d
예제 #7
0
def makeService(config):
    s = service.MultiService()
    if config['root']:
        root = config['root']
        if config['indexes']:
            config['root'].indexNames = config['indexes']
    else:
        # This really ought to be web.Admin or something
        root = demo.Test()

    if isinstance(root, static.File):
        root.registry.setComponent(interfaces.IServiceCollection, s)

    if config['logfile']:
        site = server.Site(root, logPath=config['logfile'])
    else:
        site = server.Site(root)

    site.displayTracebacks = not config["notracebacks"]

    if config['personal']:
        import pwd, os

        pw_name, pw_passwd, pw_uid, pw_gid, pw_gecos, pw_dir, pw_shell \
                 = pwd.getpwuid(os.getuid())
        i = internet.UNIXServer(
            os.path.join(pw_dir, distrib.UserDirectory.userSocketName),
            pb.BrokerFactory(distrib.ResourcePublisher(site)))
        i.setServiceParent(s)
    else:
        if config['https']:
            from twisted.internet.ssl import DefaultOpenSSLContextFactory
            i = internet.SSLServer(
                int(config['https']), site,
                DefaultOpenSSLContextFactory(config['privkey'],
                                             config['certificate']))
            i.setServiceParent(s)
        strports.service(config['port'], site).setServiceParent(s)

    flashport = config.get('flashconduit', None)
    if flashport:
        from twisted.web.woven.flashconduit import FlashConduitFactory
        i = internet.TCPServer(int(flashport), FlashConduitFactory(site))
        i.setServiceParent(s)
    return s
예제 #8
0
 def testDistrib(self):
     # site1 is the publisher
     r1 = resource.Resource()
     r1.putChild(b"there", static.Data(b"root", "text/plain"))
     site1 = server.Site(r1)
     self.f1 = PBServerFactory(distrib.ResourcePublisher(site1))
     self.port1 = reactor.listenTCP(0, self.f1)
     self.sub = distrib.ResourceSubscription("127.0.0.1", self.port1.getHost().port)
     r2 = resource.Resource()
     r2.putChild(b"here", self.sub)
     f2 = MySite(r2)
     self.port2 = reactor.listenTCP(0, f2)
     agent = client.Agent(reactor)
     url = f"http://127.0.0.1:{self.port2.getHost().port}/here/there"
     url = url.encode("ascii")
     d = agent.request(b"GET", url)
     d.addCallback(client.readBody)
     d.addCallback(self.assertEqual, b"root")
     return d
예제 #9
0
    def test_requestHeaders(self):
        """
        The request headers are available on the request object passed to a
        distributed resource's C{render} method.
        """
        requestHeaders = {}

        class ReportRequestHeaders(resource.Resource):
            def render(self, request):
                requestHeaders.update(
                    dict(request.requestHeaders.getAllRawHeaders()))
                return ""

        distribRoot = resource.Resource()
        distribRoot.putChild("headers", ReportRequestHeaders())
        distribSite = server.Site(distribRoot)
        self.f1 = distribFactory = PBServerFactory(
            distrib.ResourcePublisher(distribSite))
        distribPort = reactor.listenTCP(0,
                                        distribFactory,
                                        interface="127.0.0.1")
        self.addCleanup(distribPort.stopListening)
        addr = distribPort.getHost()

        self.sub = mainRoot = distrib.ResourceSubscription(
            addr.host, addr.port)
        mainSite = server.Site(mainRoot)
        mainPort = reactor.listenTCP(0, mainSite, interface="127.0.0.1")
        self.addCleanup(mainPort.stopListening)
        mainAddr = mainPort.getHost()

        request = client.getPage("http://%s:%s/headers" %
                                 (mainAddr.host, mainAddr.port),
                                 headers={'foo': 'bar'})

        def cbRequested(result):
            self.assertEquals(requestHeaders['Foo'], ['bar'])

        request.addCallback(cbRequested)
        return request
예제 #10
0
# Copyright (c) Twisted Matrix Laboratories.
# See LICENSE for details.

"""
This shows an example of a bare-bones distributed web set up.  The "master" and
"slave" parts will usually be in different files -- they are here together only
for brevity of illustration.  In normal usage they would each run in a separate
process.

Usage:
    $ python silly-web.py

Then visit http://localhost:19988/.
"""

from twisted.internet import reactor, protocol
from twisted.web import server, distrib, static
from twisted.spread import pb

# The "master" server
site = server.Site(distrib.ResourceSubscription('unix', '.rp'))
reactor.listenTCP(19988, site)

# The "slave" server
fact = pb.PBServerFactory(distrib.ResourcePublisher(server.Site(static.File('static'))))

reactor.listenUNIX('./.rp', fact)
reactor.run()
예제 #11
0
    def __init__(self, http_port=None, distrib_port=None, allowForce=False):
        """Run a web server that provides Buildbot status.

        @type  http_port: int or L{twisted.application.strports} string
        @param http_port: a strports specification describing which port the
                          buildbot should use for its web server, with the
                          Waterfall display as the root page. For backwards
                          compatibility this can also be an int. Use
                          'tcp:8000' to listen on that port, or
                          'tcp:12345:interface=127.0.0.1' if you only want
                          local processes to connect to it (perhaps because
                          you are using an HTTP reverse proxy to make the
                          buildbot available to the outside world, and do not
                          want to make the raw port visible).

        @type  distrib_port: int or L{twisted.application.strports} string
        @param distrib_port: Use this if you want to publish the Waterfall
                             page using web.distrib instead. The most common
                             case is to provide a string that is an absolute
                             pathname to the unix socket on which the
                             publisher should listen
                             (C{os.path.expanduser(~/.twistd-web-pb)} will
                             match the default settings of a standard
                             twisted.web 'personal web server'). Another
                             possibility is to pass an integer, which means
                             the publisher should listen on a TCP socket,
                             allowing the web server to be on a different
                             machine entirely. Both forms are provided for
                             backwards compatibility; the preferred form is a
                             strports specification like
                             'unix:/home/buildbot/.twistd-web-pb'. Providing
                             a non-absolute pathname will probably confuse
                             the strports parser.
        @param allowForce: boolean, if True then the webserver will allow
                           visitors to trigger and cancel builds
        """

        service.MultiService.__init__(self)
        if type(http_port) is int:
            http_port = "tcp:%d" % http_port
        self.http_port = http_port
        if distrib_port is not None:
            if type(distrib_port) is int:
                distrib_port = "tcp:%d" % distrib_port
            if distrib_port[0] in "/~.":  # pathnames
                distrib_port = "unix:%s" % distrib_port
        self.distrib_port = distrib_port
        self.allowForce = allowForce

        # this will be replaced once we've been attached to a parent (and
        # thus have a basedir and can reference BASEDIR/public_html/)
        root = static.Data("placeholder", "text/plain")
        self.site = server.Site(root)
        self.childrenToBeAdded = {}

        self.setupUsualPages()

        # the following items are accessed by HtmlResource when it renders
        # each page.
        self.site.buildbot_service = self
        self.header = HEADER
        self.head_elements = HEAD_ELEMENTS[:]
        self.body_attrs = BODY_ATTRS.copy()
        self.footer = FOOTER
        self.template_values = {}

        # keep track of cached connections so we can break them when we shut
        # down. See ticket #102 for more details.
        self.channels = weakref.WeakKeyDictionary()

        if self.http_port is not None:
            s = strports.service(self.http_port, self.site)
            s.setServiceParent(self)
        if self.distrib_port is not None:
            f = pb.PBServerFactory(distrib.ResourcePublisher(self.site))
            s = strports.service(self.distrib_port, f)
            s.setServiceParent(self)
예제 #12
0
파일: baseweb.py 프로젝트: suh4s/buildbot
class WebStatus(service.MultiService):
    implements(IStatusReceiver)
    # TODO: IStatusReceiver is really about things which subscribe to hear
    # about buildbot events. We need a different interface (perhaps a parent
    # of IStatusReceiver) for status targets that don't subscribe, like the
    # WebStatus class. buildbot.master.BuildMaster.loadConfig:737 asserts
    # that everything in c['status'] provides IStatusReceiver, but really it
    # should check that they provide IStatusTarget instead.
    """
    The webserver provided by this class has the following resources:

     /waterfall : the big time-oriented 'waterfall' display, with links
                  to individual changes, builders, builds, steps, and logs.
                  A number of query-arguments can be added to influence
                  the display.
     /rss : a rss feed summarizing all failed builds. The same
            query-arguments used by 'waterfall' can be added to
            influence the feed output.
     /atom : an atom feed summarizing all failed builds. The same
             query-arguments used by 'waterfall' can be added to
             influence the feed output.
     /grid : another summary display that shows a grid of builds, with
             sourcestamps on the x axis, and builders on the y.  Query
             arguments similar to those for the waterfall can be added.
     /tgrid : similar to the grid display, but the commits are down the
              left side, and the build hosts are across the top.
     /builders/BUILDERNAME: a page summarizing the builder. This includes
                            references to the Schedulers that feed it,
                            any builds currently in the queue, which
                            buildslaves are designated or attached, and a
                            summary of the build process it uses.
     /builders/BUILDERNAME/builds/NUM: a page describing a single Build
     /builders/BUILDERNAME/builds/NUM/steps/STEPNAME: describes a single step
     /builders/BUILDERNAME/builds/NUM/steps/STEPNAME/logs/LOGNAME: a StatusLog
     /builders/_all/{force,stop}: force a build/stop building on all builders.
     /buildstatus?builder=...&number=...: an embedded iframe for the console
     /changes : summarize all ChangeSources
     /changes/CHANGENUM: a page describing a single Change
     /buildslaves : list all BuildSlaves
     /buildslaves/SLAVENAME : describe a single BuildSlave
     /one_line_per_build : summarize the last few builds, one line each
     /one_line_per_build/BUILDERNAME : same, but only for a single builder
     /about : describe this buildmaster (Buildbot and support library versions)
     /change_hook[/DIALECT] : accepts changes from external sources, optionally
                              choosing the dialect that will be permitted
                              (i.e. github format, etc..)

     and more!  see the manual.


    All URLs for pages which are not defined here are used to look
    for files in PUBLIC_HTML, which defaults to BASEDIR/public_html.
    This means that /robots.txt or /favicon.ico can be placed in
    that directory

    This webserver uses the jinja2 template system to generate the web pages
    (see http://jinja.pocoo.org/2/) and by default loads pages from the
    buildbot.status.web.templates package. Any file here can be overridden by placing
    a corresponding file in the master's 'templates' directory.

    The main customization points are layout.html which loads style sheet
    (css) and provides header and footer content, and root.html, which
    generates the root page.

    All of the resources provided by this service use relative URLs to reach
    each other. The only absolute links are the c['titleURL'] links at the
    top and bottom of the page, and the buildbot home-page link at the
    bottom.

    Buildbot uses some generic classes to identify the type of object, and
    some more specific classes for the various kinds of those types. It does
    this by specifying both in the class attributes where applicable,
    separated by a space. It is important that in your CSS you declare the
    more generic class styles above the more specific ones. For example,
    first define a style for .Event, and below that for .SUCCESS

    The following CSS class names are used:
        - Activity, Event, BuildStep, LastBuild: general classes
        - waiting, interlocked, building, offline, idle: Activity states
        - start, running, success, failure, warnings, skipped, exception:
          LastBuild and BuildStep states
        - Change: box with change
        - Builder: box for builder name (at top)
        - Project
        - Time

    """

    # we are not a ComparableMixin, and therefore the webserver will be
    # rebuilt every time we reconfig. This is because WebStatus.putChild()
    # makes it too difficult to tell whether two instances are the same or
    # not (we'd have to do a recursive traversal of all children to discover
    # all the changes).

    def __init__(self,
                 http_port=None,
                 distrib_port=None,
                 allowForce=None,
                 public_html="public_html",
                 site=None,
                 numbuilds=20,
                 num_events=200,
                 num_events_max=None,
                 auth=None,
                 order_console_by_time=False,
                 changecommentlink=None,
                 revlink=None,
                 projects=None,
                 repositories=None,
                 authz=None,
                 logRotateLength=None,
                 maxRotatedFiles=None,
                 change_hook_dialects={},
                 provide_feeds=None,
                 jinja_loaders=None):
        """Run a web server that provides Buildbot status.

        @type  http_port: int or L{twisted.application.strports} string
        @param http_port: a strports specification describing which port the
                          buildbot should use for its web server, with the
                          Waterfall display as the root page. For backwards
                          compatibility this can also be an int. Use
                          'tcp:8000' to listen on that port, or
                          'tcp:12345:interface=127.0.0.1' if you only want
                          local processes to connect to it (perhaps because
                          you are using an HTTP reverse proxy to make the
                          buildbot available to the outside world, and do not
                          want to make the raw port visible).

        @type  distrib_port: int or L{twisted.application.strports} string
        @param distrib_port: Use this if you want to publish the Waterfall
                             page using web.distrib instead. The most common
                             case is to provide a string that is an absolute
                             pathname to the unix socket on which the
                             publisher should listen
                             (C{os.path.expanduser(~/.twistd-web-pb)} will
                             match the default settings of a standard
                             twisted.web 'personal web server'). Another
                             possibility is to pass an integer, which means
                             the publisher should listen on a TCP socket,
                             allowing the web server to be on a different
                             machine entirely. Both forms are provided for
                             backwards compatibility; the preferred form is a
                             strports specification like
                             'unix:/home/buildbot/.twistd-web-pb'. Providing
                             a non-absolute pathname will probably confuse
                             the strports parser.

        @param allowForce: deprecated; use authz instead
        @param auth: deprecated; use with authz

        @param authz: a buildbot.status.web.authz.Authz instance giving the authorization
                           parameters for this view

        @param public_html: the path to the public_html directory for this display,
                            either absolute or relative to the basedir.  The default
                            is 'public_html', which selects BASEDIR/public_html.

        @type site: None or L{twisted.web.server.Site}
        @param site: Use this if you want to define your own object instead of
                     using the default.`

        @type numbuilds: int
        @param numbuilds: Default number of entries in lists at the /one_line_per_build
        and /builders/FOO URLs.  This default can be overriden both programatically ---
        by passing the equally named argument to constructors of OneLinePerBuildOneBuilder
        and OneLinePerBuild --- and via the UI, by tacking ?numbuilds=xy onto the URL.

        @type num_events: int
        @param num_events: Default number of events to show in the waterfall.

        @type num_events_max: int
        @param num_events_max: The maximum number of events that are allowed to be
        shown in the waterfall.  The default value of C{None} will disable this
        check

        @type auth: a L{status.web.auth.IAuth} or C{None}
        @param auth: an object that performs authentication to restrict access
                     to the C{allowForce} features. Ignored if C{allowForce}
                     is not C{True}. If C{auth} is C{None}, people can force or
                     stop builds without auth.

        @type order_console_by_time: bool
        @param order_console_by_time: Whether to order changes (commits) in the console
                     view according to the time they were created (for VCS like Git) or
                     according to their integer revision numbers (for VCS like SVN).

        @type changecommentlink: callable, dict, tuple (2 or 3 strings) or C{None}
        @param changecommentlink: adds links to ticket/bug ids in change comments,
            see buildbot.status.web.base.changecommentlink for details

        @type revlink: callable, dict, string or C{None}
        @param revlink: decorations revision ids with links to a web-view,
            see buildbot.status.web.base.revlink for details

        @type projects: callable, dict or c{None}
        @param projects: maps project identifiers to URLs, so that any project listed
            is automatically decorated with a link to it's front page.
            see buildbot.status.web.base.dictlink for details

        @type repositories: callable, dict or c{None}
        @param repositories: maps repository identifiers to URLs, so that any project listed
            is automatically decorated with a link to it's web view.
            see buildbot.status.web.base.dictlink for details

        @type logRotateLength: None or int
        @param logRotateLength: file size at which the http.log is rotated/reset.
            If not set, the value set in the buildbot.tac will be used, 
             falling back to the BuildMaster's default value (1 Mb).
        
        @type maxRotatedFiles: None or int
        @param maxRotatedFiles: number of old http.log files to keep during log rotation.
            If not set, the value set in the buildbot.tac will be used, 
             falling back to the BuildMaster's default value (10 files).       
        
        @type  change_hook_dialects: None or dict
        @param change_hook_dialects: If empty, disables change_hook support, otherwise      
                                     whitelists valid dialects. In the format of
                                     {"dialect1": "Option1", "dialect2", None}
                                     Where the values are options that will be passed
                                     to the dialect
                                     
                                     To enable the DEFAULT handler, use a key of DEFAULT
                                     
                                     
        
    
        @type  provide_feeds: None or list
        @param provide_feeds: If empty, provides atom, json, and rss feeds.
                              Otherwise, a dictionary of strings of
                              the type of feeds provided.  Current
                              possibilities are "atom", "json", and "rss"

        @type  jinja_loaders: None or list
        @param jinja_loaders: If not empty, a list of additional Jinja2 loader
                              objects to search for templates.
        """

        service.MultiService.__init__(self)
        if type(http_port) is int:
            http_port = "tcp:%d" % http_port
        self.http_port = http_port
        if distrib_port is not None:
            if type(distrib_port) is int:
                distrib_port = "tcp:%d" % distrib_port
            if distrib_port[0] in "/~.":  # pathnames
                distrib_port = "unix:%s" % distrib_port
        self.distrib_port = distrib_port
        self.num_events = num_events
        if num_events_max:
            if num_events_max < num_events:
                config.error("num_events_max must be greater than num_events")
            self.num_events_max = num_events_max
        self.public_html = public_html

        # make up an authz if allowForce was given
        if authz:
            if allowForce is not None:
                config.error("cannot use both allowForce and authz parameters")
            if auth:
                config.error(
                    "cannot use both auth and authz parameters (pass " +
                    "auth as an Authz parameter)")
        else:
            # invent an authz
            if allowForce and auth:
                authz = Authz(auth=auth, default_action="auth")
            elif allowForce:
                authz = Authz(default_action=True)
            else:
                if auth:
                    log.msg(
                        "Warning: Ignoring authentication. Search for 'authorization'"
                        " in the manual")
                authz = Authz()  # no authorization for anything

        self.authz = authz

        self.orderConsoleByTime = order_console_by_time

        # If we were given a site object, go ahead and use it. (if not, we add one later)
        self.site = site

        # store the log settings until we create the site object
        self.logRotateLength = logRotateLength
        self.maxRotatedFiles = maxRotatedFiles

        # create the web site page structure
        self.childrenToBeAdded = {}
        self.setupUsualPages(numbuilds=numbuilds,
                             num_events=num_events,
                             num_events_max=num_events_max)

        self.revlink = revlink
        self.changecommentlink = changecommentlink
        self.repositories = repositories
        self.projects = projects

        # keep track of cached connections so we can break them when we shut
        # down. See ticket #102 for more details.
        self.channels = weakref.WeakKeyDictionary()

        # do we want to allow change_hook
        self.change_hook_dialects = {}
        if change_hook_dialects:
            self.change_hook_dialects = change_hook_dialects
            self.putChild(
                "change_hook",
                ChangeHookResource(dialects=self.change_hook_dialects))

        # Set default feeds
        if provide_feeds is None:
            self.provide_feeds = ["atom", "json", "rss"]
        else:
            self.provide_feeds = provide_feeds

        self.jinja_loaders = jinja_loaders

    def setupUsualPages(self, numbuilds, num_events, num_events_max):
        #self.putChild("", IndexOrWaterfallRedirection())
        self.putChild(
            "waterfall",
            WaterfallStatusResource(num_events=num_events,
                                    num_events_max=num_events_max))
        self.putChild("grid", GridStatusResource())
        self.putChild(
            "console",
            ConsoleStatusResource(orderByTime=self.orderConsoleByTime))
        self.putChild("tgrid", TransposedGridStatusResource())
        self.putChild("builders", BuildersResource())  # has builds/steps/logs
        self.putChild("one_box_per_builder", Redirect("builders"))
        self.putChild("changes", ChangesResource())
        self.putChild("buildslaves", BuildSlavesResource())
        self.putChild("buildstatus", BuildStatusStatusResource())
        self.putChild("one_line_per_build",
                      OneLinePerBuild(numbuilds=numbuilds))
        self.putChild("about", AboutBuildbot())
        self.putChild("authfail", AuthFailResource())
        self.putChild("authzfail", AuthzFailResource())
        self.putChild("users", UsersResource())
        self.putChild("login", LoginResource())
        self.putChild("logout", LogoutResource())

    def __repr__(self):
        if self.http_port is None:
            return "<WebStatus on path %s at %s>" % (self.distrib_port,
                                                     hex(id(self)))
        if self.distrib_port is None:
            return "<WebStatus on port %s at %s>" % (self.http_port,
                                                     hex(id(self)))
        return ("<WebStatus on port %s and path %s at %s>" %
                (self.http_port, self.distrib_port, hex(id(self))))

    def setServiceParent(self, parent):
        # this class keeps a *separate* link to the buildmaster, rather than
        # just using self.parent, so that when we are "disowned" (and thus
        # parent=None), any remaining HTTP clients of this WebStatus will still
        # be able to get reasonable results.
        self.master = parent.master

        # set master in IAuth instance
        if self.authz.auth:
            self.authz.auth.master = self.master

        def either(a, b):  # a if a else b for py2.4
            if a:
                return a
            else:
                return b

        rotateLength = either(self.logRotateLength,
                              self.master.log_rotation.rotateLength)
        maxRotatedFiles = either(self.maxRotatedFiles,
                                 self.master.log_rotation.maxRotatedFiles)

        # Set up the jinja templating engine.
        if self.revlink:
            revlink = self.revlink
        else:
            revlink = self.master.config.revlink
        self.templates = createJinjaEnv(revlink, self.changecommentlink,
                                        self.repositories, self.projects,
                                        self.jinja_loaders)

        if not self.site:

            class RotateLogSite(server.Site):
                def _openLogFile(self, path):
                    try:
                        from twisted.python.logfile import LogFile
                        log.msg(
                            "Setting up http.log rotating %s files of %s bytes each"
                            % (maxRotatedFiles, rotateLength))
                        if hasattr(LogFile, "fromFullPath"
                                   ):  # not present in Twisted-2.5.0
                            return LogFile.fromFullPath(
                                path,
                                rotateLength=rotateLength,
                                maxRotatedFiles=maxRotatedFiles)
                        else:
                            log.msg(
                                "WebStatus: rotated http logs are not supported on this version of Twisted"
                            )
                    except ImportError, e:
                        log.msg(
                            "WebStatus: Unable to set up rotating http.log: %s"
                            % e)

                    # if all else fails, just call the parent method
                    return server.Site._openLogFile(self, path)

            # this will be replaced once we've been attached to a parent (and
            # thus have a basedir and can reference BASEDIR)
            root = static.Data("placeholder", "text/plain")
            httplog = os.path.abspath(
                os.path.join(self.master.basedir, "http.log"))
            self.site = RotateLogSite(root, logPath=httplog)

        # the following items are accessed by HtmlResource when it renders
        # each page.
        self.site.buildbot_service = self

        if self.http_port is not None:
            s = strports.service(self.http_port, self.site)
            s.setServiceParent(self)
        if self.distrib_port is not None:
            f = pb.PBServerFactory(distrib.ResourcePublisher(self.site))
            s = strports.service(self.distrib_port, f)
            s.setServiceParent(self)

        self.setupSite()

        service.MultiService.setServiceParent(self, parent)
예제 #13
0
# Copyright (c) Twisted Matrix Laboratories.
# See LICENSE for details.
"""
This shows an example of a bare-bones distributed web set up.  The "master" and
"slave" parts will usually be in different files -- they are here together only
for brevity of illustration.  In normal usage they would each run in a separate
process.

Usage:
    $ python silly-web.py

Then visit http://localhost:19988/.
"""

from twisted.internet import reactor
from twisted.web import server, distrib, static
from twisted.spread import pb

# The "master" server
site = server.Site(distrib.ResourceSubscription("unix", ".rp"))
reactor.listenTCP(19988, site)

# The "slave" server
fact = pb.PBServerFactory(
    distrib.ResourcePublisher(server.Site(static.File("static"))))

reactor.listenUNIX("./.rp", fact)
reactor.run()
예제 #14
0
    def __init__(self, http_port=None, distrib_port=None, allowForce=False,
                 public_html="public_html", site=None, numbuilds=20,
                 num_events=200, num_events_max=None, auth=None,
                 order_console_by_time=False):
        """Run a web server that provides Buildbot status.

        @type  http_port: int or L{twisted.application.strports} string
        @param http_port: a strports specification describing which port the
                          buildbot should use for its web server, with the
                          Waterfall display as the root page. For backwards
                          compatibility this can also be an int. Use
                          'tcp:8000' to listen on that port, or
                          'tcp:12345:interface=127.0.0.1' if you only want
                          local processes to connect to it (perhaps because
                          you are using an HTTP reverse proxy to make the
                          buildbot available to the outside world, and do not
                          want to make the raw port visible).

        @type  distrib_port: int or L{twisted.application.strports} string
        @param distrib_port: Use this if you want to publish the Waterfall
                             page using web.distrib instead. The most common
                             case is to provide a string that is an absolute
                             pathname to the unix socket on which the
                             publisher should listen
                             (C{os.path.expanduser(~/.twistd-web-pb)} will
                             match the default settings of a standard
                             twisted.web 'personal web server'). Another
                             possibility is to pass an integer, which means
                             the publisher should listen on a TCP socket,
                             allowing the web server to be on a different
                             machine entirely. Both forms are provided for
                             backwards compatibility; the preferred form is a
                             strports specification like
                             'unix:/home/buildbot/.twistd-web-pb'. Providing
                             a non-absolute pathname will probably confuse
                             the strports parser.

        @param allowForce: boolean, if True then the webserver will allow
                           visitors to trigger and cancel builds

        @param public_html: the path to the public_html directory for this display,
                            either absolute or relative to the basedir.  The default
                            is 'public_html', which selects BASEDIR/public_html.

        @type site: None or L{twisted.web.server.Site}
        @param site: Use this if you want to define your own object instead of
                     using the default.`

        @type numbuilds: int
        @param numbuilds: Default number of entries in lists at the /one_line_per_build
        and /builders/FOO URLs.  This default can be overriden both programatically ---
        by passing the equally named argument to constructors of OneLinePerBuildOneBuilder
        and OneLinePerBuild --- and via the UI, by tacking ?numbuilds=xy onto the URL.

        @type num_events: int
        @param num_events: Defaualt number of events to show in the waterfall.

        @type num_events_max: int
        @param num_events_max: The maximum number of events that are allowed to be
        shown in the waterfall.  The default value of C{None} will disable this
        check

        @type auth: a L{status.web.auth.IAuth} or C{None}
        @param auth: an object that performs authentication to restrict access
                     to the C{allowForce} features. Ignored if C{allowForce}
                     is not C{True}. If C{auth} is C{None}, people can force or
                     stop builds without auth.

        @type order_console_by_time: bool
        @param order_console_by_time: Whether to order changes (commits) in the console
                     view according to the time they were created (for VCS like Git) or
                     according to their integer revision numbers (for VCS like SVN).
        """

        service.MultiService.__init__(self)
        if type(http_port) is int:
            http_port = "tcp:%d" % http_port
        self.http_port = http_port
        if distrib_port is not None:
            if type(distrib_port) is int:
                distrib_port = "tcp:%d" % distrib_port
            if distrib_port[0] in "/~.": # pathnames
                distrib_port = "unix:%s" % distrib_port
        self.distrib_port = distrib_port
        self.allowForce = allowForce
        self.num_events = num_events
        if num_events_max:
            assert num_events_max >= num_events
            self.num_events_max = num_events_max
        self.public_html = public_html

        if self.allowForce and auth:
            assert IAuth.providedBy(auth)
            self.auth = auth
        else:
            if auth:
                log.msg("Warning: Ignoring authentication. allowForce must be"
                        " set to True use this")
            self.auth = None

        self.orderConsoleByTime = order_console_by_time

        # If we were given a site object, go ahead and use it.
        if site:
            self.site = site
        else:
            # this will be replaced once we've been attached to a parent (and
            # thus have a basedir and can reference BASEDIR)
            root = static.Data("placeholder", "text/plain")
            self.site = server.Site(root)
        self.childrenToBeAdded = {}

        self.setupUsualPages(numbuilds=numbuilds, num_events=num_events,
                             num_events_max=num_events_max)

        # the following items are accessed by HtmlResource when it renders
        # each page.
        self.site.buildbot_service = self
        self.header = HEADER
        self.head_elements = HEAD_ELEMENTS[:]
        self.body_attrs = BODY_ATTRS.copy()
        self.footer = FOOTER
        self.template_values = {}

        # keep track of cached connections so we can break them when we shut
        # down. See ticket #102 for more details.
        self.channels = weakref.WeakKeyDictionary()

        if self.http_port is not None:
            s = strports.service(self.http_port, self.site)
            s.setServiceParent(self)
        if self.distrib_port is not None:
            f = pb.PBServerFactory(distrib.ResourcePublisher(self.site))
            s = strports.service(self.distrib_port, f)
            s.setServiceParent(self)