Hi all,

This is my first thread on Daniweb!. I was wondering if someone would help nudge me in the right direction on this one. I'm new to python, been using it for about 6 months or so trying to develop a network simulator for school. This is not an assignment for a class, there is no grade tied to it, it's a bit of research I'm involved in.

I was tasked with writing a network simulator that would eventually be used to simulate peer-to-peer networks for traffic research. Basically the script reads in a network topology file generated by BRITE (a network topology generator written in java/c++) and creates nodes and edges based on this information. The script does this just fine.

The next step sets up the routing table so all the nodes in the network can start talking to each other. Whenever I initialize my routing tables, I add routing table entries to the node's incoming routes list. The node then update's it's list as it would normally, using whatever metric to judge which routes to change. Because the list, the node's routing table, should be empty, it should add the contents of the incoming routes list to the node's routing table.

Each node has three lists of routing table entries: the one that represents it's actual routing table, the one that represents outgoing route changes, and the one that represents incoming route changes. Each routing table entry is a tuple representing where it came from, the route, the next_hop (via), and the cost. When I initialize the lists, though, they not only share the same values (all 3 lists per node), but every node that I initialize shares those values, so when I change one list it changes everywhere. This smacks of a shared memory address, but I fear I don't know enough python to figure out whats up. I'll post the code in the thread, as well as the graph input file.

If anyone could point me in the right direction, even insofar as a text to read to find the answer myself, I'd be very grateful.

Thanks for reading.

Here's the code!

Node.py

class node(dict):

    """ Node class, contains edge list, node name, and other node info

    """

    

    def __init__(self, iD, xPos, yPos, InDeg, OutDeg, ASId, Type, EdgeList = None):

       """ Initialize everything, likely read in from a file

       """ 



       self["nodeData"] = {

                            "id"        :   iD,

                            "xpos"      :   xPos,

                            "ypos"      :   yPos,

                            "inDeg"     :   InDeg,

                            "outDeg"    :   OutDeg,

                            "ASid"      :   ASId,

                            "type"      :   Type

                        }

       if EdgeList is None:

          self["edgeList"] = []

       else:

          self["edgeList"] = EdgeList

       self["incRTE"] = None#RTable()

       self["outRTE"] = None#RTable()

       self["myRTable"] = None#RTable()







    def tick(self, number = 1):

        """ Either tick once, or tick number times

        """

        #thisTickDo = self.msgQ.pop(0)

        #testext = thisTickDo.getMessage()

        #print testext



        #while number > 0:

            #Do tick things here

            

         #   number = number - 1







    def addEdge(self, target):

        """ Appends the edge to the end of edgeList. This may be extended to order the edges

            on insert.

        """

        li = target

        self["edgeList"].append(li)



    def initRTable(self):

        if self["incRTE"] is None:

            self["incRTE"] = RTable()

        for curr in self["edgeList"]:  

            foo = rtEntry(int(curr["to"]), int(self["nodeData"]["id"]), int(curr["length"]), int(curr["from"]))

            self["incRTE"].addRTE(foo)

        



    def updateRTable(self):

        if self["outRTE"] is None:

            self["outRTE"] = RTable()

        if self["incRTE"] is None:

            self["incRTE"] = RTable()

        for curr1 in self["incRTE"]:

            if len(self["myRTable"]) is 0:

                self["myRTable"].addRTE(curr1)

            else:

                for curr2 in self["myRTable"]:

                    if curr2[0] == curr1[0]:

                        if curr1.getCost() < curr2.getCost():

                            del curr2

                            self["myRTable"].addRTE(curr1)

        self["incRTE"] = None

        self["outRTE"] = None                           # Might be a problem later!!



    def isNeighborRoute(self, targetRTE):

       if targetRTE[1] == targetRTE[3]:

          return True

       else:

          return False





class rtEntry(list): 

    def __init__(self, dest, origin, cost, via):

        self.entry = [dest, origin, cost, via]



    def getDest(self): return self.entry[0]

    def getOrigin(self): return self.entry[1]

    def getCost(self): return self.entry[2]

    def getVia(self): return self.entry[3]

    def __repr__(self):

        foo = str(self.getDest())+", "+str(self.getOrigin())+", "+str(self.getCost())+", "+str(self.getVia())

        return foo





class RTable(list):

    internalRTable = None

    def __init__(self, rtbl=None):

       if self.internalRTable == None:

            self.internalRTable = []

       if rtbl is not None:

           for rtentry in rtbl:

              self.addRTE([rtentry])



    def addRTE(self, rteToAdd):

        if self.internalRTable is None:

            self.internalRTable = []

        self.internalRTable.append([rteToAdd])



    def __repr__(self):

        ret = ""

        if self.internalRTable is None:

            return ret

        else:

            for curr in self.internalRTable:

               if curr is not None:

                  ret = ret + str(curr)+"\n"     

        return ret



    def clearRTList(self):

        self.internalRTable = None

       #for rte in self.internalRTable:

          #del rte





class edge(dict):

    """An edge object. Each node has a set of edge objects.

       Each graph has a set of all the edges that will supply

       each node with edges to populate the edge lists over time

    """

    def __init__(self, edgeId, From, To, Length, Delay, Bandwidth, ASFrom, ASTo, Type):

        self.edgeData = {

                            "edgeId"    :   edgeId,

                            "from"      :   From,

                            "to"        :   To,

                            "length"    :   Length,

                            "delay"     :   Delay,

                            "bandwidth" :   Bandwidth,

                            "ASfrom"    :   ASFrom,

                            "ASto"      :   ASTo,

                            "type"      :   Type

                        }

                

        self.load = 0





    def __getitem__(self, key): return self.edgeData[key]



    def __setitem__(self, key, item): self.edgeData[key] = item



    def __repr__(self):

       ret = ""

       for k,v in self.edgeData.iteritems():

          ret = ret + str(k) + ": " + str(v) + "; "

       return ret



    def addLoad(self, Load = 5):

        """ If called without any parameters, adds 5 units to the edge's current load.

            Otherwise, add Load units to the current load. If an add would increase

            the current load over the edge's bandwidth, set the edge's current load to it's

            max load (bandwidth). Then, return the value of the current load.

        """

        

        if ((self.load + Load) > self.edgeData["bandwidth"]):

            self.load = self.edgeData["bandwidth"]           

        else:

            self.load = self.load + Load

        return self.load



    def remLoad(self, Load = 5):

        """ If called without any parameters, removes 5 units from the edge's

            current load. Otherwise, remove Load units from the current load.

            If a remove would make the current load zero or less, set the

            edge's current load to zero. Then, return the value of the current load.

        """

        

        if ((self.load - Load) < 0):

            self.load = 0

        else:

            self.load = self.load - Load

        return self.load



    def isFull(self):

        if self.load == self.edgeData["bandwidth"]:

            return True

        else:

            return False

And the second file, Network.py:

from Node import *



class Network:

    """ This is a class that will hold a network's hardware description.

        Eventually, many network objects will be able to be instantiated

        and communicate with each other. These will be controlled in

        another class, NetManager. The interaction between each network

        will be handled by another configuration file suited for EGPs.

    """

    def __init__(self, filename = "", NodeList = [], ModelInfo = [""], EdgeList = []):

        """ This function prepares the network. It reads all the

            configuration info from the infile and initializes all the main

            lists and node lists. The edge distribution will be taken care

            of by the driver program, as this may vary from simulation to

            simulation.

        """

        self.nodeList = NodeList

        self.modelInfo = ModelInfo

        self.sourcefile = filename

        self.fullEdgeList = EdgeList

        self.routingProtocol = ""



        if self.sourcefile == "":

            self.sourcefile = \

                raw_input("Please enter the path and filename \

                of the configuration file: ")

        

        infile = open(self.sourcefile, 'r')

        

        buffer = infile.readline()

        helper = buffer.split(" ")



        self.numNodes = int(helper[2])

        self.numEdges = int(helper[4])

        buffer = infile.readline()

        self.modelInfo = buffer.split(" ") # No functionality yet



        # Read in all the node info, a line at a time.



        buffer = infile.readline()      #Nodes:(blah)\n



        i = 0

        while i < self.numNodes:

            buffer = infile.readline()

            helper = buffer.split("\t")

            self.nodeList.append(node(int(helper[0]), int(helper[1]), \

                int(helper[2]), int(helper[3]), int(helper[4]), \

                int(helper[5]), helper[6], []))

            i+=1



        buffer = infile.readline()      #"\n"

        buffer = infile.readline()      #"\n"



        buffer = infile.readline()      #Edges:(blah)\n

        #helper = buffer.split(" ")

        

        

        i = 0

        while i < self.numEdges:
            buffer = infile.readline()

            helper = buffer.split("\t")

            self.fullEdgeList.append(edge(int(helper[0]), int(helper[1]),\

                int(helper[2]), float(helper[3]), float(helper[4]), \

                float(helper[5]), int(helper[6]), int(helper[7]), \

                helper[8]))

            i+=1

        infile.close()



        # End __init__()



    def setRoutingType(self, lsProto = "OSPF"):

        if (lsProto == "OSPF"):             # Add other protocols later

            self.routingProtocol = "OSPF"

            """ Here is where I would call an object seperate from the

                Network object to preserve the

                physical network configuration. This is an attempt to

                isolate the hardware from the protocol.

            # #############################################################

            # #####################NOT FINISHED!!!!########################

            """

            pass



    def addAllEdges(self):

        """ addAllEdges() adds every edge in the edge list to the node

            who's id is in it's from field

        """

        for edge in self.fullEdgeList:

            for node in self.nodeList:

                if node["nodeData"]["id"] == edge["from"]:

                    node.addEdge(edge)



    def addNode(self, targetNode):

        self.nodeList.append(targetNode)

        self.numNodes = self.numNodes + 1



    def initAllRTables(self):

        for rtable in self.nodeList:

            rtable.initRTable()

            rtable.updateRTable()

            #print rtable

And here's the .brite file, you don't have to save it as a .brite, but that's the program that generated it, BRITE:

Topology: ( 50 Nodes, 100 Edges )
Model (7 - Imported From BRITE format file /home/ich1/Desktop/BRITE/router50wax.brite ): Model (1 - RTWaxman):  50 1000 100 1  2  0.15000000596046448 0.20000000298023224 1 3 10.0 1024.0 
Nodes: ( 50 )
0	604	820	2	2	-1	RT_NODE
1	161	651	5	5	-1	RT_NODE
2	175	671	2	2	-1	RT_NODE
3	723	140	5	5	-1	RT_NODE
4	542	626	2	2	-1	RT_NODE
5	768	945	3	3	-1	RT_NODE
6	429	108	2	2	-1	RT_NODE
7	649	828	6	6	-1	RT_NODE
8	123	390	3	3	-1	RT_NODE
9	835	523	3	3	-1	RT_NODE
10	360	95	2	2	-1	RT_NODE
11	698	906	3	3	-1	RT_NODE
12	903	147	4	4	-1	RT_NODE
13	975	184	2	2	-1	RT_NODE
14	541	487	6	6	-1	RT_NODE
15	738	897	7	7	-1	RT_NODE
16	893	867	5	5	-1	RT_NODE
17	886	214	4	4	-1	RT_NODE
18	394	681	4	4	-1	RT_NODE
19	261	895	2	2	-1	RT_NODE
20	286	113	2	2	-1	RT_NODE
21	525	249	5	5	-1	RT_NODE
22	250	837	3	3	-1	RT_NODE
23	803	655	4	4	-1	RT_NODE
24	580	135	3	3	-1	RT_NODE
25	957	474	4	4	-1	RT_NODE
26	419	562	2	2	-1	RT_NODE
27	56	699	4	4	-1	RT_NODE
28	647	250	5	5	-1	RT_NODE
29	229	325	3	3	-1	RT_NODE
30	461	498	8	8	-1	RT_NODE
31	662	200	6	6	-1	RT_NODE
32	554	574	4	4	-1	RT_NODE
33	888	73	2	2	-1	RT_NODE
34	68	533	5	5	-1	RT_NODE
35	924	679	4	4	-1	RT_NODE
36	178	442	2	2	-1	RT_NODE
37	11	935	3	3	-1	RT_NODE
38	240	582	8	8	-1	RT_NODE
39	925	762	2	2	-1	RT_NODE
40	926	110	5	5	-1	RT_NODE
41	849	616	7	7	-1	RT_NODE
42	681	157	4	4	-1	RT_NODE
43	796	538	12	12	-1	RT_NODE
44	102	550	2	2	-1	RT_NODE
45	458	424	4	4	-1	RT_NODE
46	593	168	2	2	-1	RT_NODE
47	111	273	5	5	-1	RT_NODE
48	726	442	5	5	-1	RT_NODE
49	931	338	3	3	-1	RT_NODE


Edges: ( 100 )
0	43	30	337.3796081542969	1.1253772373229511	10.0	-1	-1	E_RT	U
1	43	15	363.6550598144531	1.2130227099123791	10.0	-1	-1	E_RT	U
2	16	15	157.8765411376953	0.5266194559760917	10.0	-1	-1	E_RT	U
3	16	43	343.00146484375	1.1441297327224622	10.0	-1	-1	E_RT	U
4	31	43	363.5931701660156	1.2128162682665473	10.0	-1	-1	E_RT	U
5	31	30	359.4509582519531	1.1989993365742146	10.0	-1	-1	E_RT	U
6	48	43	118.81077575683594	0.3963100891511952	10.0	-1	-1	E_RT	U
7	48	31	250.31979370117188	0.8349769549611947	10.0	-1	-1	E_RT	U
8	32	48	216.8132781982422	0.7232112496914188	10.0	-1	-1	E_RT	U
9	32	30	120.10411834716797	0.4006242156604486	10.0	-1	-1	E_RT	U
10	34	31	680.9735717773438	2.271483333237635	10.0	-1	-1	E_RT	U
11	34	32	487.7263488769531	1.6268799826743912	10.0	-1	-1	E_RT	U
12	41	15	302.1291198730469	1.007794265034669	10.0	-1	-1	E_RT	U
13	41	16	254.827392578125	0.8500126863702655	10.0	-1	-1	E_RT	U
14	1	34	150.24313354492188	0.5011571490064699	10.0	-1	-1	E_RT	U
15	1	30	336.7625427246094	1.1233189286056335	10.0	-1	-1	E_RT	U
16	29	48	510.5859375	1.703131362630877	10.0	-1	-1	E_RT	U
17	29	15	765.679443359375	2.5540317073599463	10.0	-1	-1	E_RT	U
18	14	41	333.92364501953125	1.1138493851620885	10.0	-1	-1	E_RT	U
19	14	32	87.96590423583984	0.2934226725471521	10.0	-1	-1	E_RT	U
20	47	41	813.8138427734375	2.7145907812445285	10.0	-1	-1	E_RT	U
21	47	30	416.0829162597656	1.3879032148959718	10.0	-1	-1	E_RT	U
22	18	34	358.0223388671875	1.1942339752495958	10.0	-1	-1	E_RT	U
23	18	29	392.3786315917969	1.3088342322200677	10.0	-1	-1	E_RT	U
24	3	14	391.8328857421875	1.3070138200147368	10.0	-1	-1	E_RT	U
25	3	43	404.63934326171875	1.3497315641666967	10.0	-1	-1	E_RT	U
26	12	14	496.6326599121094	1.6565882384943433	10.0	-1	-1	E_RT	U
27	12	41	472.0985107421875	1.5747511258011284	10.0	-1	-1	E_RT	U
28	27	47	429.5357971191406	1.4327771952126316	10.0	-1	-1	E_RT	U
29	27	15	710.1605224609375	2.3688405212012955	10.0	-1	-1	E_RT	U
30	44	47	277.14617919921875	0.9244601450221231	10.0	-1	-1	E_RT	U
31	44	14	443.4974670410156	1.4793483131620864	10.0	-1	-1	E_RT	U
32	17	12	69.12307739257812	0.23056976767767162	10.0	-1	-1	E_RT	U
33	17	43	336.26776123046875	1.1216685151914954	10.0	-1	-1	E_RT	U
34	2	30	334.2528991699219	1.1149476587897413	10.0	-1	-1	E_RT	U
35	2	27	122.24974060058594	0.40778124111643244	10.0	-1	-1	E_RT	U
36	13	43	396.6824951171875	1.3231903756471002	10.0	-1	-1	E_RT	U
37	13	48	358.55963134765625	1.196026190050639	10.0	-1	-1	E_RT	U
38	28	3	133.70115661621094	0.4459790533363282	10.0	-1	-1	E_RT	U
39	28	43	324.2607116699219	1.0816173089648635	10.0	-1	-1	E_RT	U
40	38	34	178.843505859375	0.5965577221404783	10.0	-1	-1	E_RT	U
41	38	47	334.8462219238281	1.1169267704654136	10.0	-1	-1	E_RT	U
42	37	41	896.6632690429688	2.9909467203573503	10.0	-1	-1	E_RT	U
43	37	27	240.251953125	0.8013942536372947	10.0	-1	-1	E_RT	U
44	8	38	224.83995056152344	0.749985346734518	10.0	-1	-1	E_RT	U
45	8	18	397.64556884765625	1.3264028438222293	10.0	-1	-1	E_RT	U
46	23	17	448.74267578125	1.4968444462377035	10.0	-1	-1	E_RT	U
47	23	1	642.012451171875	2.1415230238109424	10.0	-1	-1	E_RT	U
48	40	3	205.20477294921875	0.6844894441914838	10.0	-1	-1	E_RT	U
49	40	28	312.1553955078125	1.0412383206378477	10.0	-1	-1	E_RT	U
50	35	23	123.35720825195312	0.4114753555673276	10.0	-1	-1	E_RT	U
51	35	16	190.53871154785156	0.635568729176808	10.0	-1	-1	E_RT	U
52	7	35	312.771484375	1.0432933718932982	10.0	-1	-1	E_RT	U
53	7	14	357.6940002441406	1.1931387554924435	10.0	-1	-1	E_RT	U
54	49	28	297.3213806152344	0.9917573730798603	10.0	-1	-1	E_RT	U
55	49	16	530.3630981445312	1.769100869590693	10.0	-1	-1	E_RT	U
56	22	17	890.294921875	2.9697042007474383	10.0	-1	-1	E_RT	U
57	22	8	464.6912841796875	1.5500432775386481	10.0	-1	-1	E_RT	U
58	9	12	382.0994567871094	1.2745465957889754	10.0	-1	-1	E_RT	U
59	9	41	94.04785919189453	0.3137098905666751	10.0	-1	-1	E_RT	U
60	21	22	649.12939453125	2.165262591533407	10.0	-1	-1	E_RT	U
61	21	1	542.3098754882812	1.8089510293427102	10.0	-1	-1	E_RT	U
62	33	40	53.037723541259766	0.17691480264410042	10.0	-1	-1	E_RT	U
63	33	43	474.0137023925781	1.5811395175010645	10.0	-1	-1	E_RT	U
64	6	38	510.2911071777344	1.702147914534042	10.0	-1	-1	E_RT	U
65	6	7	752.8612060546875	2.5112746700742137	10.0	-1	-1	E_RT	U
66	24	7	696.4265747070312	2.3230290026409914	10.0	-1	-1	E_RT	U
67	24	40	346.9020080566406	1.1571405443983538	10.0	-1	-1	E_RT	U
68	42	3	45.31004333496094	0.15113803608415305	10.0	-1	-1	E_RT	U
69	42	1	717.241943359375	2.3924615987483415	10.0	-1	-1	E_RT	U
70	4	38	305.1884765625	1.0179991804947275	10.0	-1	-1	E_RT	U
71	4	21	377.3830871582031	1.2588144801101138	10.0	-1	-1	E_RT	U
72	19	38	313.70367431640625	1.0464028228368782	10.0	-1	-1	E_RT	U
73	19	43	643.1749267578125	2.1454006249810744	10.0	-1	-1	E_RT	U
74	36	40	818.3690795898438	2.729785415715307	10.0	-1	-1	E_RT	U
75	36	49	760.1480102539062	2.5355808325701985	10.0	-1	-1	E_RT	U
76	26	42	482.3577575683594	1.6089722896509937	10.0	-1	-1	E_RT	U
77	26	7	351.6475524902344	1.172969976750497	10.0	-1	-1	E_RT	U
78	11	37	687.61181640625	2.2936261338710864	10.0	-1	-1	E_RT	U
79	11	23	272.07720947265625	0.9075518820178466	10.0	-1	-1	E_RT	U
80	39	9	255.38401794433594	0.8518693887367105	10.0	-1	-1	E_RT	U
81	39	7	283.7816162109375	0.946593580452706	10.0	-1	-1	E_RT	U
82	45	38	269.2359619140625	0.8980745003066838	10.0	-1	-1	E_RT	U
83	45	24	313.6957092285156	1.0463762541635242	10.0	-1	-1	E_RT	U
84	20	31	385.9339294433594	1.2873370198104161	10.0	-1	-1	E_RT	U
85	20	21	274.9854431152344	0.9172527052539606	10.0	-1	-1	E_RT	U
86	46	38	544.0634155273438	1.8148002093079465	10.0	-1	-1	E_RT	U
87	46	31	76.05918884277344	0.253706145078451	10.0	-1	-1	E_RT	U
88	25	28	382.46044921875	1.2757507369273113	10.0	-1	-1	E_RT	U
89	25	45	501.4987487792969	1.6728197637957152	10.0	-1	-1	E_RT	U
90	10	42	326.9327087402344	1.0905301318161726	10.0	-1	-1	E_RT	U
91	10	45	343.28558349609375	1.1450774505344419	10.0	-1	-1	E_RT	U
92	5	25	507.50567626953125	1.6928567171277247	10.0	-1	-1	E_RT	U
93	5	35	308.3699035644531	1.028611278688182	10.0	-1	-1	E_RT	U
94	0	18	251.83526611328125	0.840032026800625	10.0	-1	-1	E_RT	U
95	0	43	341.1568603515625	1.1379767944381125	10.0	-1	-1	E_RT	U
96	15	11	41.0	0.13676127903124233	10.0	-1	-1	E_RT	U
97	15	25	476.3297119140625	1.588864893706107	10.0	-1	-1	E_RT	U
98	30	5	542.2711791992188	1.8088219524162237	10.0	-1	-1	E_RT	U
99	30	21	257.0933532714844	0.8575711176546155	10.0	-1	-1	E_RT	U

Nice code!

Line 19 in the second file is (one of?) your troubles:

def __init__(self, filename = "", NodeList = [], ModelInfo = [""], EdgeList = [])

This is tricky, and it gets even experienced Python users several times until they learn by banging their heads against walls.

Here's what happens:

On compilation, NodeList's default value is set to a reference (pointer) to an empty list []. Likewise, ModeInfo is set to a reference to [""], and EdgeList is set to a reference to [].

NOW.

Your code executes and creates a single Network object. It adds items to, say, NodeList. As a result, that empty list in memory changes. But the default reference doesn't change! ACK!! As a result, the NEXT Network object you create will have as its default value the list of all the items added to NodeList the last time.

It's the peril of mutable objects: changing the object "automagically" changes it for all shared references (which are shared memory locations, as you pointed out in your post).

So...

Two options.

One is to use immutable tuples as your default values. Then convert each to a list straight away in the __init__ code. :

def __init__(self, filename = "", NodeList = (), ModelInfo = ("",), EdgeList = ()):

    NodeList = list(NodeList)
    ModeInfo = list(ModeInfo)
    EdgeList = list(EdgeList)

I like this option.

OR, set the default values to be None. Then, check for None in the __init__ code and if None, then assign to an empty list. Because references within the __init__ are local scope, a new list will be created each time.

def __init__(..., NodeList = None, ...):

    if NodeList == None:
        NodeList = []

This is one of the (relatively few) annoying quirks of Python, and it is an unavoidable feature of its otherwise very useful distinction between mutable and immutable objects.

Jeff

Thanks for the compliment!

Hrmmm, interesting, I think I understand. You're saying that instead of initializing the lists every time I call a Network object, python uses the list that already exists with the same name (lists being mutable), so I should pass the parameters as immutable objects because these are recreated every time I call a new Network object. I'll try you idea with the tuples, thanks a ton!

Another question, do you think inheriting object would make this any easier or more clear?

No, I think it's clear enough. class Network should inherit from object, though. Only old-style classes get to be parent-less.

class Network(object):
...

Jeff

When I was searching python network simulator I came across this thread. I found it very helpful for those who want to start writing network simulation in python. When you complete your simulator, it would be nice if you could put it on the web. If you do, could you post the link here. I have seen network simulators in C++, Java but not in python.
Thanks.

This article has been dead for over six months. Start a new discussion instead.