~launchpad-pqm/launchpad/devel

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
#!/usr/bin/env python
""" Copyright Canonical Limited 2005
 Author: Daniel Silverstone <daniel.silverstone@canonical.com>
         Celso Providelo <celso.providelo@canonical.com>

Buildd-Slave monitor, support multiple slaves and requires LPDB access.
"""
from string import join
from sqlobject import SQLObjectNotFound

from canonical.lp import initZopeless
from canonical.launchpad.database import Builder

from twisted.internet import stdio
from twisted.protocols import basic
from twisted.internet import reactor, defer
from twisted.web.xmlrpc import Proxy

class BuilddSlaveMonitorApp:
    """Simple application class to expose some special methods and
    wrap to the RPC server.
    """
    def __init__(self, tm, write):
        self.tm = tm
        self.write = write

    def requestReceived(self, line):
        """Process requests typed in."""
        # identify empty ones
        if line.strip() == '':
            self.prompt()
            return
        request = line.strip().split()

        # select between local or remote method
        cmd = 'cmd_' + request[0]

        if hasattr(self, cmd):
            args = join(request[1:])
            meth = getattr(self, cmd)
            d = defer.maybeDeferred(meth, args)
            d.addCallbacks(self._printResult).addErrback(self._printError)
            return
        
        elif len(request) > 1:
            try:
                builder_id = request.pop(1)
                bid = int(builder_id)
                builder = Builder.get(bid)
            except ValueError:
                self.write('Wrong builder ID: %s' % builder_id)
            except SQLObjectNotFound:
                self.write('Builder Not Found: %s' % bid)
            else:
                slave = Proxy(builder.url.encode('ascii'))
                d = slave.callRemote(*request)
                d.addCallbacks(self._printResult).addErrback(self._printError)
                return
        else:
            self.write('Syntax Error: %s' % request)

        self.prompt()
        return
    
    def prompt(self):
        """Simple display a prompt according with current state."""
        self.write('\nbuildd-monitor>>> ')
            
    def cmd_quit(self, data=None):
        """Ohh my ! stops the reactor, i.e., QUIT, if requested.""" 
        reactor.stop()

    def cmd_builders(self, data=None):
        """Read access through initZopeless."""
        builders = Builder.select(orderBy='id')
        blist = 'List of Builders\nID - STATUS - NAME - URL\n'
        for builder in builders:
            name = builder.name.encode('ascii')
            url = builder.url.encode('ascii')
            blist += '%s - %s - %s - %s\n' % (builder.id, builder.builderok,
                                              name, url)
        return blist

    def cmd_reset(self, data=None):
        try:
            builder = Builder.get(int(data[0]))
        except ValueError, IndexError:
            msg =  'Argument must be the builder ID'
        except SQLObjectNotFound:
            msg = 'Builder not found: %d' % int(data[0])
        else:
            builder.builderok = True
            self.tm.commit()
            msg = '%s was reset sucessfully' % builder.name
        return msg

    def cmd_clear(self, data=None):
        """Simply returns the VT100 reset string."""
        return '\033c'
        
    def cmd_help(self, data=None):
        return ('Command Help\n'
                'clear - clear screen'
                'builders - list available builders\n'
                'reset <BUILDERID> - reset builder\n'
                'quit - exit the program\n'
                'Usage: <CMD> <BUILDERID> <ARGS>\n')
            
    def _printResult(self, result):
        """Callback for connections."""
        if result is None:
            return
        self.write('Got: %s' % str(result).strip())
        self.prompt()
            
    def _printError(self, error):
        """ErrBack for normal RPC transactions."""
        self.write('Error: ' + repr(error))
        self.prompt()

class BuilddSlaveMonitorProtocol(basic.LineReceiver):
    """Terminal Style Protocol"""
    # set local line delimiter
    from os import linesep as delimiter
    # store the trasaction manager locally
    tm = None

    def connectionMade(self):
        """Setup the backend application and send welcome message."""
        self.app = BuilddSlaveMonitorApp(self.tm, self.transport.write)
        self.transport.write('Welcome Buildd Slave Monitor\n>>> ')

    def lineReceived(self, line):
        """Use the Backend App to process each request."""
        self.app.requestReceived(line)

def main(tm):
    """Setup the interactive interface with the respective protocol,
    and start the reactor.
    """
    # ensure we store the transaction manager instance before
    # initialise the reactor.
    proto = BuilddSlaveMonitorProtocol()
    proto.tm = tm
    stdio.StandardIO(proto)
    reactor.run()
    
if __name__ == '__main__':
    # for main, the only think to setup is the initZopeless
    # environment and the application wrapper. 
    tm = initZopeless()
    main(tm)