Finger演化:finger服务的配置和打包

The Evolution of Finger: configuration and packaging of the finger service -- dreamingk [2004-08-09 02:17:25]

介绍(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:

   1 #finger/plugins.tml
   2 register('finger', 'finger.tap',
   3  description='Build a finger server tap',
   4  type='tap', tapname='finger')

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 <foo@example.com>" --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

twistedTUT11 (last edited 2009-12-25 07:19:09 by localhost)