Finger演化:finger服务的配置和打包 The Evolution of Finger: configuration and packaging of the finger service
介绍(Introduction)
这是Twisted教程(Twisted入门 - Finger演化)的第十一部分。
This is the eleventh part of the Twisted tutorial Twisted from Scratch, or The Evolution of Finger.
在这个部分中,我们将使配置一个finger服务器对非程序员来说更容易,并且展示如何以 .deb 和 RPM 包格式对其进行打包。
In this part, we make it easier for non-programmers to configure a finger server, and show how to package it in the .deb and RPM package formats.
插件(Plugins)
迄今为止,用户多多少少需要是一个程序员才能够配置东西。也许我们甚至连这个都可以消除?将旧的代码移到 finger/init.py中,并且...
So far, the user had to be somewhat of a programmer to be able to configure stuff. Maybe we can eliminate even that? Move old code to finger/init.py and...
以下是finger模块的全部代码:
Full source code for finger module here:
1 # finger.py module
2
3 from twisted.application import internet, service, strports
4 from twisted.internet import protocol, reactor, defer
5 from twisted.protocols import basic, irc
6 from twisted.python import components
7 from twisted.web import resource, server, static, xmlrpc, microdom
8 from twisted.web.woven import page, model, interfaces
9 from twisted.spread import pb
10 from OpenSSL import SSL
11 import cgi
12
13 class IFingerService(components.Interface):
14
15 def getUser(self, user):
16 """Return a deferred returning a string"""
17
18 def getUsers(self):
19 """Return a deferred returning a list of strings"""
20
21 class IFingerSetterService(components.Interface):
22
23 def setUser(self, user, status):
24 """Set the user's status to something"""
25
26 def catchError(err):
27 return "Internal error in server"
28
29 class FingerProtocol(basic.LineReceiver):
30
31 def lineReceived(self, user):
32 d = self.factory.getUser(user)
33 d.addErrback(catchError)
34 def writeValue(value):
35 self.transport.write(value+'\n')
36 self.transport.loseConnection()
37 d.addCallback(writeValue)
38
39
40 class IFingerFactory(components.Interface):
41
42 def getUser(self, user):
43 """Return a deferred returning a string"""
44
45 def buildProtocol(self, addr):
46 """Return a protocol returning a string"""
47
48
49 class FingerFactoryFromService(protocol.ServerFactory):
50 __implements__ = protocol.ServerFactory.__implements__, IFingerFactory
51
52 protocol = FingerProtocol
53
54 def __init__(self, service):
55 self.service = service
56
57 def getUser(self, user):
58 return self.service.getUser(user)
59
60 components.registerAdapter(FingerFactoryFromService,
61 IFingerService,
62 IFingerFactory)
63
64 class FingerSetterProtocol(basic.LineReceiver):
65
66 def connectionMade(self):
67 self.lines = []
68
69 def lineReceived(self, line):
70 self.lines.append(line)
71
72 def connectionLost(self, reason):
73 if len(self.lines) == 2:
74 self.factory.setUser(*self.lines)
75
76
77 class IFingerSetterFactory(components.Interface):
78
79 def setUser(self, user, status):
80 """Return a deferred returning a string"""
81
82 def buildProtocol(self, addr):
83 """Return a protocol returning a string"""
84
85
86 class FingerSetterFactoryFromService(protocol.ServerFactory):
87
88 __implements__ = protocol.ServerFactory.__implements__,IFingerSetterFactory
89
90 protocol = FingerSetterProtocol
91
92 def __init__(self, service):
93 self.service = service
94
95 def setUser(self, user, status):
96 self.service.setUser(user, status)
97
98
99 components.registerAdapter(FingerSetterFactoryFromService,
100 IFingerSetterService,
101 IFingerSetterFactory)
102
103 class IRCReplyBot(irc.IRCClient):
104
105 def connectionMade(self):
106 self.nickname = self.factory.nickname
107 irc.IRCClient.connectionMade(self)
108
109 def privmsg(self, user, channel, msg):
110 user = user.split('!')[0]
111 if self.nickname.lower() == channel.lower():
112 d = self.factory.getUser(msg)
113 d.addErrback(catchError)
114 d.addCallback(lambda m: "Status of %s: %s" % (msg, m))
115 d.addCallback(lambda m: self.msg(user, m))
116
117
118 class IIRCClientFactory(components.Interface):
119
120 """
121 @ivar nickname
122 """
123
124 def getUser(self, user):
125 """Return a deferred returning a string"""
126
127 def buildProtocol(self, addr):
128 """Return a protocol"""
129
130
131 class IRCClientFactoryFromService(protocol.ClientFactory):
132
133 __implements__ = protocol.ClientFactory.__implements__, IIRCClientFactory
134
135 protocol = IRCReplyBot
136 nickname = None
137
138 def __init__(self, service):
139 self.service = service
140
141 def getUser(self, user):
142 return self.service.getUser(user)
143
144 components.registerAdapter(IRCClientFactoryFromService,
145 IFingerService,
146 IIRCClientFactory)
147
148 class UsersModel(model.MethodModel):
149
150 def initialize(self, *args, **kwargs):
151 self.service=args[0]
152
153 def wmfactory_users(self, request):
154 return self.service.getUsers()
155
156 components.registerAdapter(UsersModel, IFingerService, interfaces.IModel)
157
158 class UserStatusTree(page.Page):
159
160 template = """<html><head><title>Users</title></head><body>
161 <h1>Users</h1>
162 <ul model="users" view="List">
163 <li pattern="listItem"><a view="Anchor" /></li>
164 </ul></body></html>"""
165
166 def initialize(self, *args, **kwargs):
167 self.service=args[0]
168
169 def getDynamicChild(self, path, request):
170 return UserStatus(user=path, service=self.service)
171
172 def wchild_RPC2 (self, request):
173 return UserStatusXR(self.service)
174
175 components.registerAdapter(UserStatusTree, IFingerService, resource.IResource)
176
177
178 class UserStatus(page.Page):
179
180 template='''<html><head><title view="Text" model="user"/></head>
181 <body><h1 view="Text" model="user"/>
182 <p model="status" view="Text" />
183 </body></html>'''
184
185 def initialize(self, **kwargs):
186 self.user = kwargs['user']
187 self.service = kwargs['service']
188
189 def wmfactory_user(self, request):
190 return self.user
191
192 def wmfactory_status(self, request):
193 return self.service.getUser(self.user)
194
195
196 class UserStatusXR(xmlrpc.XMLRPC):
197
198 def __init__(self, service):
199 xmlrpc.XMLRPC.__init__(self)
200 self.service = service
201
202 def xmlrpc_getUser(self, user):
203 return self.service.getUser(user)
204
205 def xmlrpc_getUsers(self):
206 return self.service.getUsers()
207
208
209 class IPerspectiveFinger(components.Interface):
210
211 def remote_getUser(self, username):
212 """return a user's status"""
213
214 def remote_getUsers(self):
215 """return a user's status"""
216
217 class PerspectiveFingerFromService(pb.Root):
218
219 __implements__ = pb.Root.__implements__, IPerspectiveFinger
220
221 def __init__(self, service):
222 self.service = service
223
224 def remote_getUser(self, username):
225 return self.service.getUser(username)
226
227 def remote_getUsers(self):
228 return self.service.getUsers()
229
230 components.registerAdapter(PerspectiveFingerFromService,
231 IFingerService,
232 IPerspectiveFinger)
233
234
235 class FingerService(service.Service):
236
237 __implements__ = service.Service.__implements__, IFingerService
238
239 def __init__(self, filename):
240 self.filename = filename
241 self._read()
242
243 def _read(self):
244 self.users = {}
245 for line in file(self.filename):
246 user, status = line.split(':', 1)
247 user = user.strip()
248 status = status.strip()
249 self.users[user] = status
250 self.call = reactor.callLater(30, self._read)
251
252 def getUser(self, user):
253 return defer.succeed(self.users.get(user, "No such user"))
254
255 def getUsers(self):
256 return defer.succeed(self.users.keys())
257
258
259 class ServerContextFactory:
260
261 def getContext(self):
262 """Create an SSL context.
263
264 This is a sample implementation that loads a certificate from a file
265 called 'server.pem'."""
266 ctx = SSL.Context(SSL.SSLv23_METHOD)
267 ctx.use_certificate_file('server.pem')
268 ctx.use_privatekey_file('server.pem')
269 return ctx
270
271
272
273
274 # Easy configuration
275
276 def makeService(config):
277 # finger on port 79
278 s = service.MultiService()
279 f = FingerService(config['file'])
280 h = internet.TCPServer(79, IFingerFactory(f))
281 h.setServiceParent(s)
282
283
284 # website on port 8000
285 r = resource.IResource(f)
286 r.templateDirectory = config['templates']
287 site = server.Site(r)
288 j = internet.TCPServer(8000, site)
289 j.setServiceParent(s)
290
291 # ssl on port 443
292 # if config.get('ssl'):
293 # k = internet.SSLServer(443, site, ServerContextFactory())
294 # k.setServiceParent(s)
295
296 # irc fingerbot
297 if config.has_key('ircnick'):
298 i = IIRCClientFactory(f)
299 i.nickname = config['ircnick']
300 ircserver = config['ircserver']
301 b = internet.TCPClient(ircserver, 6667, i)
302 b.setServiceParent(s)
303
304 # Pespective Broker on port 8889
305 if config.has_key('pbport'):
306 m = internet.TCPServer(
307 int(config['pbport']),
308 pb.PBServerFactory(IPerspectiveFinger(f)))
309 m.setServiceParent(s)
310
311 return s
finger模块 - listings/finger/finger/finger.py
finger module - listings/finger/finger/finger.py
1 # finger/tap.py
2 from twisted.application import internet, service
3 from twisted.internet import interfaces
4 from twisted.python import usage
5 import finger
6
7 class Options(usage.Options):
8
9 optParameters = [
10 ['file', 'f', '/etc/users'],
11 ['templates', 't', '/usr/share/finger/templates'],
12 ['ircnick', 'n', 'fingerbot'],
13 ['ircserver', None, 'irc.freenode.net'],
14 ['pbport', 'p', 8889],
15 ]
16
17 optFlags = [['ssl', 's']]
18
19 def makeService(config):
20 return finger.makeService(config)
finger/tap.py - listings/finger/finger/tap.py 并且完全登记:
And register it all:
finger/plugins.tml - listings/finger/finger/plugins.tml 现在,下面可以工作了
And now, the following works
% mktap finger --file=/etc/users --ircnick=fingerbot % sudo twistd -nf finger.tap
OS集成(OS Integration)
如果我们已经将finger包安装在PYTHONPATH下(例如,我们将它安装在site-packages目录下),我们可以容易地实现集成:
If we already have the finger package installed in PYTHONPATH (e.g. we added it to site-packages), we can achieve easy integration:
Debian
% tap2deb --unsigned -m "Foo <[email protected]>" --type=python finger.tac % sudo dpkg -i .build/*.deb
Red Hat / Mandrake
% tap2rpm --type=python finger.tac #[maybe other options needed] % sudo rpm -i .build/*.rpm
对于给定的文件将正确地登记 tap/tac,init.d脚本,等等。
Will properly register the tap/tac, init.d scripts, etc. for the given file.
如果在你心爱的OS上不能用:我们接受补丁!
If it doesn't work on your favorite OS: patches accepted!
return index-->["TwistedTUT"]
Version: 1.3.0