-
Notifications
You must be signed in to change notification settings - Fork 1
/
node.py
309 lines (254 loc) · 8.98 KB
/
node.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
"""
In contrast to mininet, there are no Switches
or Controller nodes. Just an end host.
"""
import os
from util import run, ipStr, shell_cmd, Command
fileopen = open
from cmd import Cmd
import settings
from util import colored
def cmd(s, comment=None):
if comment is not None:
comment = colored('(%s)' % comment, 'green')
else:
comment = ''
print '# %s %s' % (colored(s, 'yellow'), comment)
if not settings.dryrun:
shell_cmd(s)
class Node(object):
def __init__(self,p):
pass
def start(self):
pass
def stop(self):
pass
def create(self):
pass
def destroy(self):
pass
class Controller(Node):
pass
class RemoteController(Node):
pass
class Host(Node):
def __init__(self,id):
self.id = id
self.name = 'h%d' % id
self.ifaces = []
# Create scratch area if not there already
self.create()
def create(self):
cmd("vzctl create %d --hostname %s --ostemplate debian-5.0-x86_64" % (self.id, self.name))
def start(self):
cmd("vzctl start %d" % self.id)
self.add_iface()
#self.shell = Cmd(self)
def stop(self):
cmd("vzctl stop %d" % self.id)
def configure(self, ip=None, mask=None):
# bring up the eth0 and set ip address
if ip is None:
# 10.0.0.0 + id
ip = ipStr(self.id)
mask = 8
cmd("vzctl exec %d ifconfig eth0 %s/%s" % (self.id, ip, mask))
self.ip = ip
self.mask = mask
def IP(self):
return self.ip
def cmd(self, c):
cmd("vzctl exec %d '%s'" % (self.id, c))
#self.shell.cmd(c)
def sendCmd(self, c):
return self.send_command(c)
def send_command(self, c):
"""
send command c to this particular host
and return a "command" handle that can
later be used to fetch output
this is non blocking
"""
return Command(["vzctl", "exec2", "%d" % self.id, c])
def open(self, file):
# check if root fs is mounted. if it is, then return
# that file. else return from private dir
fullpath1 = os.path.join("/var/lib/vz/root/%d" % self.id, file)
fullpath2 = os.path.join("/var/lib/vz/private/%d" % self.id, file)
print '# open %s' % fullpath1
try:
ret = fileopen(fullpath1)
except:
ret = fileopen(fullpath2)
return ret
def add_iface(self):
next_iface_id = len(self.ifaces)
veth = 'veth%d.%d' % (self.id, next_iface_id)
eth = 'eth%d' % next_iface_id
self.ifaces.append((veth, eth))
cmd("vzctl set %d --netif_add eth%d" % (self.id, next_iface_id))
def del_iface(self):
pass
def destroy(self):
"""Destroy's the host's root directory.
Please don't do it unless needed."""
cmd("vzctl destroy %d" % self.id)
_next_bridgewire_id=0
def next_bridgewire_id():
global _next_bridgewire_id
_next_bridgewire_id += 1
return _next_bridgewire_id
class UserSwitch(Node):
""" Ideally this should be a subclass of a switch class
but not for now."""
def __init__(self,id):
self.name = 'sw%d' % (id)
self.id = id
# container's id
self.cid = self.id + settings.offset_switch_id
self.created = []
self.create()
self.ifaces = []
self.bridgewires = []
def create(self):
"""This will create a userspace switch, which is nothing
but a container with a switch process in it"""
cmd("vzctl create %d --hostname %s --ostemplate debian-5.0-x86_64" % (self.cid, self.name))
# useless thing, avoid copying again and again
# maybe hook to a settings variable
# TODO!!
files_to_copy = [
"/home/jvimal/openflow/controller/controller",
"/home/jvimal/openflow/udatapath/ofdatapath",
"/home/jvimal/openflow/secchan/ofprotocol",
]
dst_dir = "/var/lib/vz/private/%d/bin" % self.cid
for f in files_to_copy:
cmd("cp %s %s" % (f, dst_dir))
def create_iface(self, insidename):
"""Just create an iface for the container and return its global
name."""
cmd("vzctl set %d --netif_add %s" % (self.cid, insidename),
"it's inside!")
self.ifaces.append(insidename)
outside_name = 'veth%d.%d' % (self.cid, len(self.ifaces))
return outside_name
def add_iface(self, node):
"""it's actually connect host's iface (node), bad choice of
naming this function. TODO: should change this!"""
iface = node.ifaces[0][0]
# add a corresponding interface to our switch
# let's name it the same because, it doesn't matter
# and it's useful to debug
# it will be visible as veth<container_id>.<n> to the outside world
outside_name = self.create_iface('in'+iface)
bridgewire_name = 'br%d' % next_bridgewire_id()
# this is the only new thing we've created.. a br<id>
self.bridgewires.append(bridgewire_name)
cmd("brctl addbr %s" % bridgewire_name)
# bridge the iface and our **outside** iface
# this is like the wire between the host and the switch
cmd("brctl addif %s %s" % (bridgewire_name, outside_name))
cmd("brctl addif %s %s" % (bridgewire_name, iface))
def connect_switch(self, sw):
# this becomes simple actually...
# no need for veth pair as in the normal switch case
# just bridge the two interfaces that the switches anyway have!
bridgewire_name = 'br%d' % next_bridgewire_id()
cmd("brctl addbr %s" % bridgewire_name)
# our interface goes to switch sw
our_their_outside = self.create_iface(sw.name)
their_our_outside = sw.create_iface(self.name)
cmd("brctl addif %s %s" % (bridgewire_name, our_their_outside))
cmd("brctl addif %s %s" % (bridgewire_name, their_our_outside))
self.bridgewires.append(bridgewire_name)
def configure(self):
for br in self.bridgewires:
cmd("ifconfig %s up" % br)
cmd("ifconfig %s 0" % br)
# let's initialise our own loopback
# needed for the controller
# let's just forget the case of having a
# single controller. right now, every switch
# has its own controller ;)
# need to worry about ip address and all that.. :-/
self.cmd("ifconfig lo up")
for iface in self.ifaces:
self.cmd("ifconfig %s up" % iface)
# make the tun device
self.cmd("mkdir -p /dev/net")
self.cmd("mknod /dev/net/tun c 10 200","enabling tun/tap for container")
self.cmd("chmod 600 /dev/net/tun")
self.cmd("controller -v ptcp: 2>&1 1> /tmp/controller.log &")
intfs = ','.join( self.ifaces )
self.cmd("ofdatapath --no-slicing -i %s punix:/tmp/ofsock 2>&1 1> /tmp/ofdp.log &" % intfs)
self.cmd("ofprotocol unix:/tmp/ofsock tcp:127.0.0.1 2>&1 1> /tmp/ofp.log &")
def cmd(self,c, comment=None):
cmd("vzctl exec %d '%s'" % (self.cid, c), comment)
def start(self):
# should use the host's command interface to
# start the of switch process
# self.cmd("ofprotocol blah blah")
cmd("vzctl set %d --devices c:10:200:rw" % self.cid)
cmd("vzctl set %d --capability net_admin:on" % self.cid)
cmd("vzctl start %d" % (self.cid))
def stop(self):
for br in self.bridgewires:
cmd("ifconfig %s down" % br)
# no veths created...
# but containers were created
cmd("vzctl stop %d" % self.cid)
def destroy(self):
# will remove all its files
cmd("vzctl destroy %d" % self.cid)
class Switch(Node):
def __init__(self,id):
# This will be a bridge sw<id>
self.name = 'sw%d' % id
self.id = id
self.created = []
self.create()
self.ifaces = []
def create(self):
cmd("brctl addbr %s" % self.name)
def add_iface(self, node):
iface = node.ifaces[0][0]
cmd("brctl addif %s %s" % (self.name, iface))
self.ifaces.append(iface)
def connect_switch(self, sw):
name = '%s%s' % (self.name, sw.name)
peer = '%s%s' % (sw.name, self.name)
#this command works ONLY on kernel versions 2.6.28+,
# as it requires the veth module support.
if settings.veth:
cmd("ip link add name %s type veth peer name %s" % (name, peer))
# with a patched 2.6.18 kernel having etun
# support, we should have something similar setup
# patch here: http://lwn.net/Articles/229677/
# i'll arrange a standalone patch shortly
elif settings.etun:
cmd("echo -n '%s,%s' > /sys/module/etun/parameters/newif" % (name, peer))
else:
print '*** Cannot proceed. Need support for virtual ethernet pair'
print '*** to create (%s,%s)' % (name, peer)
cmd("ifconfig %s up" % name)
cmd("ifconfig %s up" % peer)
cmd("brctl addif %s %s" % (self.name, name))
self.ifaces.append(name)
cmd("brctl addif %s %s" % (sw.name, peer))
sw.ifaces.append(peer)
self.created += [name] # It's enough to delete one of the pairs
def configure(self):
cmd("ifconfig %s 0" % self.name)
def stop(self):
# TODO: should we delif's first?
# We should do it at least for the links we create..
# The hosts will take care of the rest
for iface in self.created:
cmd("ifconfig %s down" % iface)
if settings.veth:
cmd("ip link del dev %s" % iface)
else:
cmd("echo -n '%s' > /sys/module/etun/parameters/delif" % iface)
cmd("ifconfig %s down" % self.name)
cmd("brctl delbr %s" % self.name)