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)
|