@@ -27,32 +27,74 @@ def __init__(self, flow, idn, length=1):
27
27
self .flow = flow
28
28
self .idn = idn
29
29
self .length = length
30
- self .rank = 0
31
30
32
31
def __repr__ (self ):
33
32
return f"P(F:{ self .flow } , I:{ self .idn } , L:{ self .length } )"
34
33
35
34
36
35
class Runner :
37
- def __init__ (self , pkts , queue ):
36
+ """This class is responsible for running a test on a packet scheduling
37
+ algorithm. It is accountable for enquing and dequeing packets. For now, it
38
+ does so by dequing as many packets as it enqued. In the next iteration, when
39
+ we add pacing, it will need to handle virtual time cycling.
40
+ """
41
+
42
+ def __init__ (self , pkts , scheduler ):
38
43
self .input_pkts = pkts
39
- self .queue = queue
44
+ self .scheduler = scheduler
40
45
41
46
def run (self ):
42
- print (f"Running with queue : { self .queue } " )
43
- print (" Inserting packets into queue :" )
47
+ print (f"Running with scheduler : { self .scheduler } " )
48
+ print (" Inserting packets into scheduler :" )
44
49
pprint (self .input_pkts , indent = 4 )
45
50
for p in self .input_pkts :
46
- self .queue .enqueue (p )
47
- print (" Queue state:" )
48
- self .queue .dump ()
51
+ self .scheduler .enqueue (p )
52
+ print (" Scheduler state:" )
53
+ self .scheduler .dump ()
49
54
output = []
50
- for p in self .queue :
55
+
56
+ for p in self .scheduler :
51
57
output .append (p )
52
58
print (" Got packets from queue:" )
53
59
pprint (output , indent = 4 )
54
60
55
61
62
+ class SchedulingAlgorithm ():
63
+
64
+ """A queuing packet scheduling algorithm requires an abstraction that keeps
65
+ the queuing data structure and the algorithm separate. To create a new
66
+ Scheduling algorithm, inherit this class, add the scheduling data structures
67
+ to the constructor, and implement the constructor, enqueue, dequeue, and the
68
+ dump functions.
69
+
70
+ Please look at the pifo_fifo.py to see how you implement a FIFO.
71
+ """
72
+
73
+ def __init__ (self ):
74
+ raise NotImplementedError (self .__class__ .__name__ + ' missing implementation' )
75
+
76
+ def enqueue (self , item ):
77
+ raise NotImplementedError (self .__class__ .__name__ + ' missing implementation' )
78
+
79
+ def dequeue (self ):
80
+ raise NotImplementedError (self .__class__ .__name__ + ' missing implementation' )
81
+
82
+ def dump (self ):
83
+ raise NotImplementedError (self .__class__ .__name__ + ' missing implementation' )
84
+
85
+ def __next__ (self ):
86
+ item = self .dequeue ()
87
+ if item is None :
88
+ raise StopIteration
89
+ return item
90
+
91
+ def __iter__ (self ):
92
+ return self
93
+
94
+ def __repr__ (self ):
95
+ return f"{ self .__class__ .__name__ } - { self .__class__ .__doc__ } "
96
+
97
+
56
98
class Queue :
57
99
def __init__ (self , idx = None ):
58
100
self ._list = []
@@ -97,11 +139,10 @@ def dump(self):
97
139
98
140
99
141
class Pifo (Queue ):
100
-
101
- def enqueue (self , item , rank = None ):
142
+ def enqueue (self , item , rank ):
102
143
if rank is None :
103
- rank = self . get_rank ( item )
104
- item . rank = rank
144
+ raise ValueError ( "Rank can't be of value 'None'." )
145
+
105
146
super ().enqueue ((rank , item ))
106
147
self .sort ()
107
148
@@ -116,24 +157,42 @@ def peek(self):
116
157
itm = super ().peek ()
117
158
return itm [1 ] if itm else None
118
159
119
- def get_rank (self , item ):
120
- raise NotImplementedError
121
-
122
160
123
161
class Flow (Queue ):
124
162
def __init__ (self , idx ):
125
163
super ().__init__ ()
126
164
self .idx = idx
127
- self .rank = 0
128
165
129
166
def __repr__ (self ):
130
- return f"F({ self .idx } )"
167
+ return f"F(I: { self .idx } , Q: { self . qlen } , L: { self . length } )"
131
168
132
- # Return the length of the first packet in the queue as the "length" of the
133
- # flow. This is not the correct thing to do, but it works as a stopgap
134
- # solution for testing the hierarchical mode, and we're only using
135
- # unit-length for that anyway
136
169
@property
137
170
def length (self ):
138
- itm = self .peek ()
139
- return itm .length if itm else 0
171
+ result = 0
172
+ for itm in self ._list :
173
+ result += itm .length if itm else 0
174
+ return result
175
+
176
+
177
+ class FlowTracker ():
178
+ """This class provides us with the typical operation of keeping track of
179
+ flows. Use this class in your scheduling algorithms when your algorithm only
180
+ has one type of flows.
181
+ """
182
+
183
+ def __init__ (self ):
184
+ self ._flows = {}
185
+
186
+ def enqueue (self , pkt , flow_id = None ):
187
+ if not isinstance (pkt , Packet ):
188
+ raise ValueError (f"Expected a packet, but got '{ pkt } ' instead." )
189
+ if flow_id is None :
190
+ flow_id = pkt .flow
191
+ if not flow_id in self ._flows :
192
+ self ._flows [flow_id ] = Flow (flow_id )
193
+ flow = self ._flows [flow_id ]
194
+ flow .enqueue (pkt )
195
+ return flow
196
+
197
+ def get_flow (self , flow_id ):
198
+ return self ._flows [flow_id ]
0 commit comments