8
|
1 import string
|
|
2 import struct
|
|
3 import time
|
|
4
|
|
5 import xml.dom.minidom
|
|
6 import FederatedLinda
|
|
7 import LinkConfiguration
|
|
8
|
|
9 TUPLE_ID_LINKCONFIG = 1
|
|
10 TUPLE_ID_ROUTING = 2
|
|
11
|
|
12 ROUTING_HEADER_FORMAT = "!I"
|
|
13
|
|
14 ROUTING_COMMAND_CONNECT = 1
|
|
15 ROUTING_COMMAND_DISCONNECT = 2
|
|
16 ROUTING_COMMAND_TRANSFER = 3
|
|
17 ROUTING_COMMAND_UPDATE_TABLE = 4
|
|
18
|
|
19 class TSInfo:
|
|
20 def __init__(self, tsid, hopnum, ttl, nexthop):
|
|
21 self.tsid = tsid # tuple space id, like 'host:port'
|
|
22 self.hopnum = hopnum # hop number
|
|
23 self.ttl = ttl #
|
|
24 self.nexthop = nexthop # next hop (tuple space id)
|
|
25
|
|
26
|
|
27 """ XML format is as follows
|
|
28 <RoutingTable name="localhost:10000">
|
|
29 <ts hopnum="1" tsid="localhost:10002" ttl="0"/>
|
|
30 <ts hopnum="1" tsid="localhost:10001" ttl="256"/>
|
|
31 <ts hopnum="0" tsid="localhost:10000" ttl="0"/>
|
|
32 </RoutingTable>
|
|
33 """
|
|
34 class RoutingTable:
|
|
35 def __init__(self, name):
|
|
36 self.tslist = {}
|
|
37 self.name = name
|
|
38
|
|
39 def register(self, tsid, hopnum, ttl, nexthop):
|
|
40 if (self.tslist.has_key(tsid)):
|
|
41 del self.tslist[tsid]
|
|
42 self.tslist[tsid] = TSInfo(tsid, hopnum, ttl, nexthop)
|
|
43
|
|
44 def deregister(self, tsid):
|
|
45 for t in self.tslist.keys():
|
|
46 if self.tslist[t].nexthop == tsid:
|
|
47 del self.tslist[t]
|
|
48
|
|
49 def update(self, xmldoc, ts):
|
|
50 rt = xml.dom.minidom.parseString(xmldoc).childNodes[0]
|
|
51 tslist = rt.childNodes
|
|
52 updateflag = False
|
|
53
|
|
54 tmplist = []
|
|
55
|
|
56 # append tuplespace
|
|
57 for t in tslist:
|
|
58 if t.nodeType == t.ELEMENT_NODE and t.localName == 'ts':
|
|
59 tsattr = t.attributes
|
|
60 tsid = tsattr['tsid'].nodeValue
|
|
61 hopnum = int( tsattr['hopnum'].nodeValue )
|
|
62 ttl = int( tsattr['ttl'].nodeValue )
|
|
63 nexthop = ts
|
|
64
|
|
65 tmplist.append(tsid)
|
|
66
|
|
67 if ((not self.tslist.has_key(tsid)) or
|
|
68 (self.tslist[tsid].hopnum > hopnum+1)):
|
|
69 self.register(tsid, hopnum+1, ttl, nexthop)
|
|
70 updateflag = True
|
|
71
|
|
72 # delete tuplespace
|
|
73 for t in self.tslist.values():
|
|
74 if ( not t.tsid in tmplist ):
|
|
75 updateflag = True
|
|
76 if (t.nexthop == ts):
|
|
77 del self.tslist[t.tsid]
|
|
78
|
|
79 return updateflag
|
|
80
|
|
81 def getxmldoc(self):
|
|
82 doc = xml.dom.minidom.Document()
|
|
83 rt = doc.createElement('RoutingTable')
|
|
84 rt.setAttribute('name', self.name)
|
|
85 for tskey in self.tslist.keys():
|
|
86 elem = doc.createElement('ts')
|
|
87 elem.setAttribute('tsid', self.tslist[tskey].tsid)
|
|
88 elem.setAttribute('hopnum', str(self.tslist[tskey].hopnum))
|
|
89 elem.setAttribute('ttl', str(self.tslist[tskey].ttl))
|
|
90 rt.appendChild(elem)
|
|
91
|
|
92 return rt
|
|
93
|
|
94 def getxml(self):
|
|
95 rt = self.getxmldoc()
|
|
96 return rt.toxml()
|
|
97
|
|
98 def printxml(self):
|
|
99 rt = self.getxmldoc()
|
|
100 print rt.toprettyxml()
|
|
101 end = time.time()
|
|
102 print "passed time ",end
|
|
103
|
|
104 def getdstname(self, xmltext):
|
|
105 rt = xml.dom.minidom.parseString(xmltext).childNodes[0]
|
|
106 return rt.attributes['name'].nodeValue
|
|
107
|
|
108 class Routing:
|
|
109 def __init__(self, headerFormat = ROUTING_HEADER_FORMAT):
|
|
110 self.flinda = FederatedLinda.FederatedLinda()
|
|
111 self.linda = None
|
|
112 self.tsid = None
|
|
113 self.neighbors = {} # key is tsid ("host:port"), value is Linda object
|
|
114 self.headerFormat = headerFormat # struct.pack's format
|
|
115 self.rt = None
|
|
116
|
|
117 def __del__(self):
|
|
118 self.linda.close()
|
|
119 for ts in self.neighbors.values():
|
|
120 ts.close()
|
|
121
|
|
122 def connect(self, tsid):
|
|
123 host, port = string.split(tsid, ':', 1)
|
|
124 ts = self.flinda.open(host, int(port))
|
|
125 if not ts:
|
|
126 return None
|
|
127
|
|
128 ts.getid()
|
|
129 self.addNeighbor(ts, tsid)
|
|
130
|
|
131 return ts
|
|
132
|
|
133 def disconnect(self, tsid):
|
|
134 if ( self.neighbors.has_key(tsid) ):
|
|
135 self.delNeighbor(tsid)
|
|
136
|
|
137 def addNeighbor(self, ts, tsid):
|
|
138 self.neighbors[tsid] = ts
|
|
139 return ts
|
|
140
|
|
141 def delNeighbor(self, tsid):
|
|
142 del self.neighbors[tsid]
|
|
143
|
|
144 def pack(self, data, cmd):
|
|
145 return struct.pack(self.headerFormat, cmd) + data
|
|
146
|
|
147 def unpack(self, packet):
|
|
148 r = struct.unpack(self.headerFormat +
|
|
149 str(len(packet)-struct.calcsize(self.headerFormat)) +
|
|
150 "s", packet)
|
|
151 return r
|
|
152
|
|
153 def LinkConfig(self, mytsid, packet):
|
|
154 self.tsid = mytsid
|
|
155 lcparser = LinkConfiguration.LinkConfigParser()
|
|
156 linkConfig = lcparser.parseLinkConfig(self.tsid, packet)
|
|
157 mydstlist = linkConfig.getDstlist(linkConfig.label)
|
|
158
|
|
159 for n in mydstlist:
|
|
160 tsid = linkConfig.linklist[n].tsid
|
|
161 if ( tsid and not self.neighbors.has_key(tsid) ):
|
|
162 ts = self.connect(tsid)
|
|
163 ts.Out(TUPLE_ID_LINKCONFIG, packet)
|
|
164 ts.Out(TUPLE_ID_ROUTING, self.pack(self.tsid, ROUTING_COMMAND_CONNECT))
|
|
165 pass
|
|
166
|
|
167 def RoutingConnect(self, data):
|
|
168 print "connect"
|
|
169 if ( not self.neighbors.has_key(data) ):
|
|
170 ts = self.connect(data)
|
|
171 ts.Out(TUPLE_ID_ROUTING, self.pack(self.tsid, ROUTING_COMMAND_CONNECT))
|
|
172
|
|
173 self.rt.register(data, 1, 16, data)
|
|
174 upedxml = self.rt.getxml()
|
|
175 # print "Gen XML ",upedxml
|
|
176 # print "Neighbors ",self.neighbors
|
|
177 for nts in self.neighbors.values():
|
|
178 nts.Out(TUPLE_ID_ROUTING, self.pack(upedxml, ROUTING_COMMAND_UPDATE_TABLE))
|
|
179 pass
|
|
180
|
|
181 def RoutingDisconnect(self, data):
|
|
182 print "disconnect"
|
|
183 if ( self.neighbors.has_key(data) ):
|
|
184 ts = self.neighbors[data]
|
|
185 ts.Out(TUPLE_ID_ROUTING, self.pack(self.tsid, ROUTING_COMMAND_DISCONNECT))
|
|
186 self.disconnect(data)
|
|
187
|
|
188 self.rt.deregister(data)
|
|
189 upedxml = self.rt.getxml()
|
|
190 for nts in self.neighbors.values():
|
|
191 nts.Out(TUPLE_ID_ROUTING, self.pack(upedxml, ROUTING_COMMAND_UPDATE_TABLE))
|
|
192 pass
|
|
193
|
|
194 def RoutingTransfer(self, data):
|
|
195 #print "transfer"
|
|
196 # ReplyTuple is "DstTSID,TupleID,SrcTSID,DATA"
|
|
197 # Src/DstTSID is "hostname:portnum"
|
|
198
|
|
199 dsttsid, tid, srctsid, pdata = string.split(data,',',3)
|
|
200 print "transfer tuple:"
|
|
201 print "dsttsid=>",dsttsid, "tid=>",tid, "srctsid=>",srctsid, "data=>",pdata
|
|
202 if ( dsttsid == self.rt.name ):
|
|
203 self.linda.Out(int(tid), data)
|
|
204 else:
|
|
205 dstts = self.neighbors[self.rt.tslist[dsttsid].nexthop]
|
|
206 dstts.Out(TUPLE_ID_ROUTING, self.pack(data, ROUTING_COMMAND_TRANSFER))
|
|
207 pass
|
|
208
|
|
209 def RoutingTableUpdate(self, data):
|
|
210 print "update"
|
|
211 srcname = self.rt.getdstname(data)
|
|
212
|
|
213 if ( self.rt.update(data, srcname) ):
|
|
214 # Send Update Info to Neighbors
|
|
215 upedxml = self.rt.getxml()
|
|
216 for n in self.neighbors.values():
|
|
217 n.Out(TUPLE_ID_ROUTING, self.pack(upedxml, ROUTING_COMMAND_UPDATE_TABLE))
|
|
218
|
|
219 pass
|
|
220
|
|
221 def run(self, mytsid):
|
|
222 self.tsid = mytsid
|
|
223 hostname, port = string.split(mytsid, ':', 1)
|
|
224 self.linda = self.flinda.open(hostname, int(port))
|
|
225 if not self.linda:
|
|
226 return
|
|
227
|
|
228 self.rt = RoutingTable(mytsid)
|
|
229 self.rt.register(self.rt.name, 0, 0, None)
|
|
230
|
|
231 self.linda.getid() # get client id from Tuple Space (ldserv)
|
|
232
|
|
233 linkConfigReply = self.linda.In(TUPLE_ID_LINKCONFIG)
|
|
234 routingReply = self.linda.In(TUPLE_ID_ROUTING)
|
|
235
|
|
236 self.flinda.sync()
|
|
237
|
|
238 while (True):
|
|
239 # Link Configuration
|
|
240 rep = linkConfigReply.reply()
|
|
241 if (rep):
|
|
242 linkConfigReply = self.linda.In(TUPLE_ID_LINKCONFIG)
|
|
243
|
|
244 # Link Configuration main
|
|
245 self.LinkConfig(self.tsid, rep)
|
|
246
|
|
247 # Routing Protocol
|
|
248 rep = routingReply.reply()
|
|
249 if (rep):
|
|
250 routingReply = self.linda.In(TUPLE_ID_ROUTING)
|
|
251 cmd , data = self.unpack(rep)
|
|
252
|
|
253 # connect to other tuplespace
|
|
254 if (cmd == ROUTING_COMMAND_CONNECT):
|
|
255 # connect main
|
|
256 self.RoutingConnect(data)
|
|
257
|
|
258 # disconnect other tuplespace
|
|
259 elif (cmd == ROUTING_COMMAND_DISCONNECT):
|
|
260 # disconnect main
|
|
261 self.RoutingDisconnect(data)
|
|
262
|
|
263 # transfer tuple
|
|
264 elif (cmd == ROUTING_COMMAND_TRANSFER):
|
|
265 # transfer main
|
|
266 self.RoutingTransfer(data)
|
|
267
|
|
268 # update own routing table
|
|
269 elif (cmd == ROUTING_COMMAND_UPDATE_TABLE):
|
|
270 #routing table main
|
|
271 self.RoutingTableUpdate(data)
|
|
272 else:
|
|
273 pass
|
|
274
|
|
275 print self.rt.printxml()
|
|
276
|
|
277 self.flinda.sync()
|
|
278 # end while
|
|
279
|
|
280 if __name__ == '__main__':
|
|
281 import sys
|
|
282 if (len(sys.argv) != 2) :
|
|
283 print "Usage : %s <hostname:portnum>" % sys.argv[0]
|
|
284 sys.exit(1)
|
|
285
|
|
286 mytsid = sys.argv[1]
|
|
287
|
|
288 routing = Routing()
|
|
289 routing.run(mytsid)
|
|
290
|