Writing Mantissa AMP Services

Introduction

Applications can expose AMP APIs through Mantissa by providing factories to create AMP box receiver objects. These objects are accessible to authenticated AMP clients and an arbitrary number of them can be served over a single port.

This document will explain how an application can control Mantissa’s response to AMP commands. It will also explain how to write clients which can connect to the Mantissa server and interact with these applications.

Readers should familiarize themselves with the following concepts in order to understand all sections of this document:

  • Zope Interfaces
  • Twisted TCP clients
  • Twisted AMP
  • AMP Authentication
  • AMP Routes
  • Axiom Powerups
  • Mantissa Port Configuration

Servers

Applications label the AMP functionality they are adding to Mantissa using a protocol identifier. This serves a similar purpose similar to that of the port number used with TCP connections. A powerup for IBoxReceiverFactory specifies the name of the AMP functionality it is providing with its protocol attribute. When a user connects to and authenticates with the Mantissa AMP server, he will be able to select a protocol defined by one of the IBoxReceiverFactory powerups on his store to which to create a new connection.

Once a client has selected a IBoxReceiverFactory powerup by its protocol attribute, the getBoxReceiver method of that powerup is invoked to create a IBoxReceiver to handle the AMP traffic. This can be a twisted.protocols.amp.AMP subclass or it can be any other IBoxReceiver implementation. Whatever is returned will be associated a IBoxSender and then be able to pass boxes to and receive boxes from the client.

# Copyright (c) 2008 Divmod.  See LICENSE for details.

from zope.interface import implements

from twisted.protocols.amp import AMP

from axiom.item import Item
from axiom.attributes import integer

from xmantissa.ixmantissa import IBoxReceiverFactory


class SimpleFactory(Item):
    powerupInterfaces = (IBoxReceiverFactory,)
    implements(IBoxReceiverFactory)

    extra = integer()

    protocol = u"http://divmod.org/ns/example"

    def getBoxReceiver(self):
        return AMP()

Here, SimpleFactory uses a URI to ensure uniqueness for the protocol it is specifying and returns a new instance of twisted.protocols.amp.AMP from each call to getBoxReceiver . This powerup won’t be able to do much of interest, since it can’t respond to any commands. If you have an existing AMP subclass which implements responders to an interesting set of commands, returning it here would let a client invoke those commands on it.

Clients

Clients connecting to a Mantissa AMP server must do two things not generally required of AMP clients: they must authenticate using credentials valid for the Mantissa server and they must select a protocol to which to connect. For details regarding authentication, see the documentation for epsilon.ampauth .

connectRoute allows a client to specify the IBoxReceiverFactory it to which it wants to connect. It does this by specifying a protocol value. The IBoxReceiverFactory with a corresponding value for its protocol will be used to create the server-side IBoxReceiver . For example, if SimpleFactory were installed, specifying u"http://divmod.com/ns/example/0.0" would cause its getBoxReceiver method to be used to create the box receiver.

Mantissa includes an AMP echo server. This example connects to a Mantissa server where this powerup has been installed and sends one box to it, printing out the result:

# Copyright (c) 2008 Divmod.  See LICENSE for details.

from sys import stdout
from getpass import getpass

from zope.interface import implements

from twisted.python.log import msg, startLogging
from twisted.cred.credentials import UsernamePassword
from twisted.internet.protocol import ClientCreator
from twisted.protocols.amp import IBoxReceiver, Box, AMP
from twisted.internet.task import deferLater
from twisted.internet import reactor

from epsilon.react import react
from epsilon.ampauth import login
from epsilon.amprouter import Router

from xmantissa.ampserver import connectRoute


class BoxPrinter:
    implements(IBoxReceiver)

    def startReceivingBoxes(self, sender):
        self.sender = sender


    def ampBoxReceived(self, box):
        msg(str(box))


    def stopReceivingBoxes(self, reason):
        pass


def sendBox(printer):
    printer.sender.sendBox(Box({'foo': 'bar'}))
    return deferLater(reactor, 1, lambda: None)


def main(reactor, username, password):
    startLogging(stdout)
    router = Router()
    proto = AMP(router)
    router.bindRoute(proto, None).connectTo(None)
    cc = ClientCreator(reactor, lambda: proto)
    d = cc.connectTCP(username.split('@')[1], 7805)
    d.addCallback(login, UsernamePassword(username, password))
    d.addCallback(
        connectRoute, router, BoxPrinter(), u'http://divmod.org/ns/echo')
    d.addCallback(sendBox)
    return d


if __name__ == '__main__':
    react(reactor, main, [raw_input('Username (localpart@domain): '),
                          getpass('Password: ')])

For clients wishing to take advantage of epsilon.ampauth ‘s one-time pad authentication support, AMPConfiguration implements the IOneTimePadGenerator interface, and can generate pads which will be valid for clients connecting to its factory.

Table Of Contents

Previous topic

Statistics Reporting and Collection

Next topic

Writing Mantissa SSH Services

This Page