view tools/python-PE/Routing/Routing.py @ 20:a0fd653d1121

Debug Client and Meta Engine for logging.
author kono
date Tue, 19 Aug 2008 06:26:20 +0900
parents 6c40056777be
children
line wrap: on
line source

import string
import struct

import xml.dom.minidom
import FederatedLinda
import LinkConfiguration


TUPLE_ID_LINKCONFIG = 1
TUPLE_ID_ROUTING = 2

ROUTING_HEADER_FORMAT = "!I"

ROUTING_COMMAND_CONNECT      =  1
ROUTING_COMMAND_DISCONNECT   =  2
ROUTING_COMMAND_TRANSFER     =  3
ROUTING_COMMAND_UPDATE_TABLE =  4

class TSInfo:
    def __init__(self, tsid, hopnum, ttl, nexthop):
        self.tsid = tsid        # tuple space id, like 'host:port'
        self.hopnum = hopnum    # hop number
        self.ttl = ttl          #
        self.nexthop = nexthop  # next hop (tuple space id)


""" XML format is as follows
<RoutingTable name="localhost:10000">
  <ts hopnum="1" tsid="localhost:10002" ttl="0"/>
  <ts hopnum="1" tsid="localhost:10001" ttl="256"/>
  <ts hopnum="0" tsid="localhost:10000" ttl="0"/>
</RoutingTable>
"""
class RoutingTable:
    def __init__(self, name):
        self.tslist = {}
        self.name = name

    def register(self, tsid, hopnum, ttl, nexthop):
        if (self.tslist.has_key(tsid)):
            del self.tslist[tsid]
        self.tslist[tsid] = TSInfo(tsid, hopnum, ttl, nexthop)

    def deregister(self, tsid):
        for t in self.tslist.keys():
            if self.tslist[t].nexthop == tsid:
                del self.tslist[t]

    def update(self, xmldoc, ts):
        rt = xml.dom.minidom.parseString(xmldoc).childNodes[0]
        tslist = rt.childNodes
        updateflag = False

        tmplist = []

        # append tuplespace
        for t in tslist:
            if t.nodeType == t.ELEMENT_NODE and t.localName == 'ts':
                tsattr  = t.attributes
                tsid    = tsattr['tsid'].nodeValue
                hopnum  = int( tsattr['hopnum'].nodeValue )
                ttl     = int( tsattr['ttl'].nodeValue )
                nexthop = ts

                tmplist.append(tsid)
                
                if ((not self.tslist.has_key(tsid)) or
                    (self.tslist[tsid].hopnum > hopnum+1)):
                    self.register(tsid, hopnum+1, ttl, nexthop)
                    updateflag = True
                
        # delete tuplespace
        for t in self.tslist.values():
            if ( not t.tsid in tmplist ):
                updateflag = True
                if (t.nexthop == ts):
                    del self.tslist[t.tsid]

        return updateflag

    def getxmldoc(self):
        doc = xml.dom.minidom.Document()
        rt = doc.createElement('RoutingTable')
        rt.setAttribute('name', self.name)
        for tskey in self.tslist.keys():
            elem = doc.createElement('ts')
            elem.setAttribute('tsid', self.tslist[tskey].tsid)
            elem.setAttribute('hopnum', str(self.tslist[tskey].hopnum))
            elem.setAttribute('ttl', str(self.tslist[tskey].ttl))
            rt.appendChild(elem)

        return rt
    
    def getxml(self):
        rt = self.getxmldoc()
        return rt.toxml()

    def printxml(self):
        rt = self.getxmldoc()
        print rt.toprettyxml()

    def getdstname(self, xmltext):
        rt = xml.dom.minidom.parseString(xmltext).childNodes[0]
        return rt.attributes['name'].nodeValue

class Routing:
    def __init__(self, headerFormat = ROUTING_HEADER_FORMAT):
        self.flinda = FederatedLinda.FederatedLinda()
        self.linda = None
        self.tsid = None
        self.neighbors = {}    # key is tsid ("host:port"),  value is Linda object
        self.headerFormat = headerFormat # struct.pack's format
        self.rt = None

    def __del__(self):
        self.linda.close()
        for ts in self.neighbors.values():
            ts.close()

    def connect(self, tsid):
        host, port = string.split(tsid, ':', 1)
        ts = self.flinda.open(host, int(port))
        if not ts:
            return None

        ts.getid()
        self.addNeighbor(ts, tsid)

        return ts

    def disconnect(self, tsid):
        if ( self.neighbors.has_key(tsid) ):
             self.delNeighbor(tsid)

    def addNeighbor(self, ts, tsid):
        self.neighbors[tsid] = ts
        return ts

    def delNeighbor(self, tsid):
        del self.neighbors[tsid]

    def pack(self, data, cmd):
        return struct.pack(self.headerFormat, cmd) + data

    def unpack(self, packet):
        r = struct.unpack(self.headerFormat +
                          str(len(packet)-struct.calcsize(self.headerFormat)) +
                          "s", packet)
        return r

    def run(self, mytsid):
        self.tsid = mytsid
        
        hostname, port = string.split(mytsid, ':', 1)
        self.linda = self.flinda.open(hostname, int(port))
        if not self.linda:
            return
        
        self.rt = RoutingTable(mytsid)
        self.rt.register(self.rt.name, 0, 0, None)

        self.linda.getid() # get client id from Tuple Space (ldserv)

        linkConfigReply = self.linda.In(TUPLE_ID_LINKCONFIG)
        routingReply = self.linda.In(TUPLE_ID_ROUTING)

        lcparser = LinkConfiguration.LinkConfigParser()

        self.flinda.sync()

        while (True):

            # Link Configuration
            rep = linkConfigReply.reply()
            if (rep):
                linkConfigReply = self.linda.In(TUPLE_ID_LINKCONFIG)
                linkConfig = lcparser.parseLinkConfig(self.tsid, rep)

                mydstlist = linkConfig.getDstlist(linkConfig.label)

                for n in mydstlist:
                    tsid = linkConfig.linklist[n].tsid
                    if ( tsid and not self.neighbors.has_key(tsid) ):
                        ts = self.connect(tsid)
                        ts.Out(TUPLE_ID_LINKCONFIG, rep)
                        ts.Out(TUPLE_ID_ROUTING,
                               self.pack(self.tsid, ROUTING_COMMAND_CONNECT))

            # Routing Protocol
            rep = routingReply.reply()
            if (rep):
                routingReply = self.linda.In(TUPLE_ID_ROUTING)
                cmd , data = self.unpack(rep)

                # connect to other tuplespace
                if (cmd == ROUTING_COMMAND_CONNECT):
                    print "connect"
                    if ( not self.neighbors.has_key(data) ):
                        ts = self.connect(data)
                        ts.Out(TUPLE_ID_ROUTING,
                               self.pack(self.tsid, ROUTING_COMMAND_CONNECT))

                    self.rt.register(data, 1, 256, data)
                    upedxml = self.rt.getxml()
                    for nts in self.neighbors.values():
                        nts.Out(TUPLE_ID_ROUTING,
                                self.pack(upedxml, ROUTING_COMMAND_UPDATE_TABLE))
                        
                # disconnect other tuplespace
                elif (cmd == ROUTING_COMMAND_DISCONNECT):
                    print "disconnect"
                    if ( self.neighbors.has_key(data) ):
                        ts = self.neighbors[data]
                        ts.Out(TUPLE_ID_ROUTING,
                               self.pack(self.tsid, ROUTING_COMMAND_DISCONNECT))
                        self.disconnect(data)

                    self.rt.deregister(data)
                    upedxml = self.rt.getxml()
                    for nts in self.neighbors.values():
                        nts.Out(TUPLE_ID_ROUTING,
                                self.pack(upedxml, ROUTING_COMMAND_UPDATE_TABLE))

                # transfer tuple
                elif (cmd == ROUTING_COMMAND_TRANSFER):
                    print "transfer"
                    # ReplyTuple is "DstTSID,TupleID,SrcTSID,DATA"
                    # Src/DstTSID is "hostname:portnum"

                    dsttsid, tid, srctsid, pdata = string.split(data,',',3)

                    if ( dsttsid == self.rt.name ):
                        self.linda.Out(int(tid), data)
                    else:
                        dstts = self.neighbors[self.rt.tslist[dsttsid].nexthop]
                        dstts.Out(TUPLE_ID_ROUTING, self.pack(data, cmd))

                # update own routing table
                elif (cmd == ROUTING_COMMAND_UPDATE_TABLE):
                    print "update"
                    srcname = self.rt.getdstname(data)

                    if ( self.rt.update(data, srcname) ):
                        # Send Update Info to Neighbors
                        upedxml = self.rt.getxml()
                        for n in self.neighbors.values():
                            n.Out(TUPLE_ID_ROUTING,
                                  self.pack(upedxml, ROUTING_COMMAND_UPDATE_TABLE))
                else:
                    pass

                print self.rt.printxml()

            self.flinda.sync()
        # end while

if __name__ == '__main__':
    import sys
    if (len(sys.argv) != 2) :
        print "Usage : %s <hostname:portnum>" % sys.argv[0]
        sys.exit(1)

    mytsid = sys.argv[1]

    routing = Routing()
    routing.run(mytsid)