##language:zh ''' Finger演化:finger服务的配置和打包 The Evolution of Finger: configuration and packaging of the finger service ''' -- dreamingk [<>] <> == 介绍(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: {{{ #!python # finger.py module from twisted.application import internet, service, strports from twisted.internet import protocol, reactor, defer from twisted.protocols import basic, irc from twisted.python import components from twisted.web import resource, server, static, xmlrpc, microdom from twisted.web.woven import page, model, interfaces from twisted.spread import pb from OpenSSL import SSL import cgi class IFingerService(components.Interface): def getUser(self, user): """Return a deferred returning a string""" def getUsers(self): """Return a deferred returning a list of strings""" class IFingerSetterService(components.Interface): def setUser(self, user, status): """Set the user's status to something""" def catchError(err): return "Internal error in server" class FingerProtocol(basic.LineReceiver): def lineReceived(self, user): d = self.factory.getUser(user) d.addErrback(catchError) def writeValue(value): self.transport.write(value+'\n') self.transport.loseConnection() d.addCallback(writeValue) class IFingerFactory(components.Interface): def getUser(self, user): """Return a deferred returning a string""" def buildProtocol(self, addr): """Return a protocol returning a string""" class FingerFactoryFromService(protocol.ServerFactory): __implements__ = protocol.ServerFactory.__implements__, IFingerFactory protocol = FingerProtocol def __init__(self, service): self.service = service def getUser(self, user): return self.service.getUser(user) components.registerAdapter(FingerFactoryFromService, IFingerService, IFingerFactory) class FingerSetterProtocol(basic.LineReceiver): def connectionMade(self): self.lines = [] def lineReceived(self, line): self.lines.append(line) def connectionLost(self, reason): if len(self.lines) == 2: self.factory.setUser(*self.lines) class IFingerSetterFactory(components.Interface): def setUser(self, user, status): """Return a deferred returning a string""" def buildProtocol(self, addr): """Return a protocol returning a string""" class FingerSetterFactoryFromService(protocol.ServerFactory): __implements__ = protocol.ServerFactory.__implements__,IFingerSetterFactory protocol = FingerSetterProtocol def __init__(self, service): self.service = service def setUser(self, user, status): self.service.setUser(user, status) components.registerAdapter(FingerSetterFactoryFromService, IFingerSetterService, IFingerSetterFactory) class IRCReplyBot(irc.IRCClient): def connectionMade(self): self.nickname = self.factory.nickname irc.IRCClient.connectionMade(self) def privmsg(self, user, channel, msg): user = user.split('!')[0] if self.nickname.lower() == channel.lower(): d = self.factory.getUser(msg) d.addErrback(catchError) d.addCallback(lambda m: "Status of %s: %s" % (msg, m)) d.addCallback(lambda m: self.msg(user, m)) class IIRCClientFactory(components.Interface): """ @ivar nickname """ def getUser(self, user): """Return a deferred returning a string""" def buildProtocol(self, addr): """Return a protocol""" class IRCClientFactoryFromService(protocol.ClientFactory): __implements__ = protocol.ClientFactory.__implements__, IIRCClientFactory protocol = IRCReplyBot nickname = None def __init__(self, service): self.service = service def getUser(self, user): return self.service.getUser(user) components.registerAdapter(IRCClientFactoryFromService, IFingerService, IIRCClientFactory) class UsersModel(model.MethodModel): def initialize(self, *args, **kwargs): self.service=args[0] def wmfactory_users(self, request): return self.service.getUsers() components.registerAdapter(UsersModel, IFingerService, interfaces.IModel) class UserStatusTree(page.Page): template = """Users

Users

""" def initialize(self, *args, **kwargs): self.service=args[0] def getDynamicChild(self, path, request): return UserStatus(user=path, service=self.service) def wchild_RPC2 (self, request): return UserStatusXR(self.service) components.registerAdapter(UserStatusTree, IFingerService, resource.IResource) class UserStatus(page.Page): template='''</head> <body><h1 view="Text" model="user"/> <p model="status" view="Text" /> </body></html>''' def initialize(self, **kwargs): self.user = kwargs['user'] self.service = kwargs['service'] def wmfactory_user(self, request): return self.user def wmfactory_status(self, request): return self.service.getUser(self.user) class UserStatusXR(xmlrpc.XMLRPC): def __init__(self, service): xmlrpc.XMLRPC.__init__(self) self.service = service def xmlrpc_getUser(self, user): return self.service.getUser(user) def xmlrpc_getUsers(self): return self.service.getUsers() class IPerspectiveFinger(components.Interface): def remote_getUser(self, username): """return a user's status""" def remote_getUsers(self): """return a user's status""" class PerspectiveFingerFromService(pb.Root): __implements__ = pb.Root.__implements__, IPerspectiveFinger def __init__(self, service): self.service = service def remote_getUser(self, username): return self.service.getUser(username) def remote_getUsers(self): return self.service.getUsers() components.registerAdapter(PerspectiveFingerFromService, IFingerService, IPerspectiveFinger) class FingerService(service.Service): __implements__ = service.Service.__implements__, IFingerService def __init__(self, filename): self.filename = filename self._read() def _read(self): self.users = {} for line in file(self.filename): user, status = line.split(':', 1) user = user.strip() status = status.strip() self.users[user] = status self.call = reactor.callLater(30, self._read) def getUser(self, user): return defer.succeed(self.users.get(user, "No such user")) def getUsers(self): return defer.succeed(self.users.keys()) class ServerContextFactory: def getContext(self): """Create an SSL context. This is a sample implementation that loads a certificate from a file called 'server.pem'.""" ctx = SSL.Context(SSL.SSLv23_METHOD) ctx.use_certificate_file('server.pem') ctx.use_privatekey_file('server.pem') return ctx # Easy configuration def makeService(config): # finger on port 79 s = service.MultiService() f = FingerService(config['file']) h = internet.TCPServer(79, IFingerFactory(f)) h.setServiceParent(s) # website on port 8000 r = resource.IResource(f) r.templateDirectory = config['templates'] site = server.Site(r) j = internet.TCPServer(8000, site) j.setServiceParent(s) # ssl on port 443 # if config.get('ssl'): # k = internet.SSLServer(443, site, ServerContextFactory()) # k.setServiceParent(s) # irc fingerbot if config.has_key('ircnick'): i = IIRCClientFactory(f) i.nickname = config['ircnick'] ircserver = config['ircserver'] b = internet.TCPClient(ircserver, 6667, i) b.setServiceParent(s) # Pespective Broker on port 8889 if config.has_key('pbport'): m = internet.TCPServer( int(config['pbport']), pb.PBServerFactory(IPerspectiveFinger(f))) m.setServiceParent(s) return s }}} finger模块 - listings/finger/finger/finger.py finger module - listings/finger/finger/finger.py {{{ #!python # finger/tap.py from twisted.application import internet, service from twisted.internet import interfaces from twisted.python import usage import finger class Options(usage.Options): optParameters = [ ['file', 'f', '/etc/users'], ['templates', 't', '/usr/share/finger/templates'], ['ircnick', 'n', 'fingerbot'], ['ircserver', None, 'irc.freenode.net'], ['pbport', 'p', 8889], ] optFlags = [['ssl', 's']] def makeService(config): return finger.makeService(config) }}} finger/tap.py - listings/finger/finger/tap.py 并且完全登记: And register it all: {{{ #!python #finger/plugins.tml register('finger', 'finger.tap', description='Build a finger server tap', 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