2 # -*- coding: utf-8 -*-
4 from nepi.core.design import ExperimentDescription, FactoriesProvider
5 from nepi.core.execute import ExperimentController
6 from optparse import OptionParser, SUPPRESS_HELP
7 from nepi.util import proxy
8 from nepi.util.constants import DeploymentConfiguration as DC, ATTR_NEPI_TESTBED_ENVIRONMENT_SETUP
19 ___________________________________________________________
23 | |netns_node| VLC_SERVER |
28 |_________________________|_________________________________|
31 _________________________|_________________________________
38 |_________________________|_________________________________|
41 _________________________|_________________________________
53 | ((*)) ((*)) ((*)) ((*)) |
54 | ------- -------- ------- ------- |
55 | 34/27 35/27 36/27 37/27 |
56 | +-----+ +-----+ +-----+ +-----+ |
57 | |sta0 | |sta1 | |sta2 | |sta3 | |
58 | +-----+ +-----+ +-----+ +-----+ |
59 | 66/30 70/30 74/30 78/30 |
60 | ------- ------- ------- ------- |
61 |_______|___________|__________|__________|_________________|
63 ______|____ ____|____ ___|____ ___|____
64 | PL2 | | |PL3 | | |PL4| | |PL5| |
65 | | | | | | | | | | | |
66 | ------- | | ------- | | ------ | |------- |
67 | 65/30 | | 69/30 | | 73/30 | | 77/30 |
68 |___________| |_________| |________| |________|
72 class WirelessOverlay(object):
74 usage = "usage: %prog -n number_sta -m movie -u user"
75 parser = OptionParser(usage=usage)
76 parser.add_option("-u", "--user", dest="user", help="Valid linux system user (not root).", type="str")
77 parser.add_option("-m", "--movie", dest="movie", help="Path to movie file to play", type="str")
78 parser.add_option("-n", "--nsta", dest="nsta", help="Number of wifi stations", type="int")
79 parser.add_option("-a", "--base_addr", dest="base_addr", help="Base address segment for the experiment", type="str")
80 parser.add_option("-s", "--slicename", dest="slicename", help="PlanetLab slice", type="str")
81 (options, args) = parser.parse_args()
83 parser.error("Missing 'movie' option.")
84 if options.user == 'root':
85 parser.error("Missing or invalid 'user' option.")
86 if options.user == 'root':
87 parser.error("Missing or invalid 'user' option.")
88 if options.nsta and options.nsta > 8:
89 parser.error("Try a number of stations under 9.")
91 self.user = options.user if options.user else os.getlogin()
92 self.movie = options.movie
93 self.nsta = options.nsta if options.nsta else 3
94 self.slicename = options.slicename if options.slicename else "inria_nepi3"
95 base = options.base_addr if options.base_addr else "192.168.4"
96 self.base_addr = base + ".%d"
97 self.root_dir = tempfile.mkdtemp()
99 def add_ip_address(self, iface, address, netprefix):
100 ip = iface.add_address()
101 ip.set_attribute_value("Address", address)
102 ip.set_attribute_value("NetPrefix", netprefix)
104 def add_route(self, node, destination, netprefix, nexthop):
105 route = node.add_route()
106 route.set_attribute_value("Destination", destination)
107 route.set_attribute_value("NetPrefix", netprefix)
108 route.set_attribute_value("NextHop", nexthop)
110 def add_ns3_fdnd(self, ns3_desc, node):
111 fdnd = ns3_desc.create("ns3::FdNetDevice")
112 node.connector("devs").connect(fdnd.connector("node"))
113 #fdnd.enable_trace("FdPcapTrace")
116 def add_ns3_node(self, ns3_desc):
117 node = ns3_desc.create("ns3::Node")
118 ipv4 = ns3_desc.create("ns3::Ipv4L3Protocol")
119 arp = ns3_desc.create("ns3::ArpL3Protocol")
120 icmp = ns3_desc.create("ns3::Icmpv4L4Protocol")
121 udp = ns3_desc.create("ns3::UdpL4Protocol")
122 node.connector("protos").connect(ipv4.connector("node"))
123 node.connector("protos").connect(arp.connector("node"))
124 node.connector("protos").connect(icmp.connector("node"))
125 node.connector("protos").connect(udp.connector("node"))
128 def add_ns3_wifi(self, ns3_desc, node, access_point = False):
129 wifi = ns3_desc.create("ns3::WifiNetDevice")
130 node.connector("devs").connect(wifi.connector("node"))
132 phy = ns3_desc.create("ns3::YansWifiPhy")
133 error = ns3_desc.create("ns3::NistErrorRateModel")
134 manager = ns3_desc.create("ns3::ArfWifiManager")
136 mac = ns3_desc.create("ns3::ApWifiMac")
138 mac = ns3_desc.create("ns3::StaWifiMac")
140 phy.set_attribute_value("Standard", "WIFI_PHY_STANDARD_80211a")
141 mac.set_attribute_value("Standard", "WIFI_PHY_STANDARD_80211a")
142 phy.connector("err").connect(error.connector("phy"))
143 wifi.connector("phy").connect(phy.connector("dev"))
144 wifi.connector("mac").connect(mac.connector("dev"))
145 wifi.connector("manager").connect(manager.connector("dev"))
147 #phy.enable_trace("YansWifiPhyPcapTrace")
150 def add_ns3_constant_mobility(self, ns3_desc, node, x, y, z):
151 mobility = ns3_desc.create("ns3::ConstantPositionMobilityModel")
152 position = "%d:%d:%d" % (x, y, z)
153 mobility.set_attribute_value("Position", position)
154 node.connector("mobility").connect(mobility.connector("node"))
157 def add_ns3_wifi_channel(self, ns3_desc):
158 channel = ns3_desc.create("ns3::YansWifiChannel")
159 delay = ns3_desc.create("ns3::ConstantSpeedPropagationDelayModel")
160 loss = ns3_desc.create("ns3::LogDistancePropagationLossModel")
161 channel.connector("delay").connect(delay.connector("chan"))
162 channel.connector("loss").connect(loss.connector("prev"))
165 def add_pl_testbed(self, exp_desc):
166 plchost = "nepiplc.pl.sophia.inria.fr"
167 port_base = 2000 + (os.getpid() % 1000) * 13
168 pl_ssh_key = os.environ.get(
170 "%s/.ssh/id_rsa_planetlab" % (os.environ['HOME'],) )
171 pl_user, pl_pwd = test_util.pl_auth()
173 pl_provider = FactoriesProvider("planetlab")
174 pl_desc = exp_desc.add_testbed_description(pl_provider)
175 pl_desc.set_attribute_value("homeDirectory", self.root_dir)
176 pl_desc.set_attribute_value("slice", self.slicename)
177 pl_desc.set_attribute_value("sliceSSHKey", pl_ssh_key)
178 pl_desc.set_attribute_value("authUser", pl_user)
179 pl_desc.set_attribute_value("authPass", pl_pwd)
180 pl_desc.set_attribute_value("plcHost", plchost)
181 pl_desc.set_attribute_value("tapPortBase", port_base)
182 pl_desc.set_attribute_value("p2pDeployment", False) # it's interactive, we don't want it in tests
183 pl_desc.set_attribute_value("dedicatedSlice", True)
184 pl_desc.set_attribute_value("plLogLevel", "DEBUG")
187 def add_pl_node(self, pl_desc, inet, label_prefix):
188 node = pl_desc.create("Node")
189 node.set_attribute_value("label", label_prefix)
190 iface = pl_desc.create("NodeInterface")
191 iface.set_attribute_value("label", label_prefix+"iface")
192 iface.connector("inet").connect(inet.connector("devs"))
193 node.connector("devs").connect(iface.connector("node"))
194 forwarder = pl_desc.create("MulticastForwarder")
195 forwarder.enable_trace("stderr")
196 node.connector("apps").connect(forwarder.connector("node"))
199 def add_ns3_in_pl(self, exp_desc, pl_desc, pl_node, pl_iface, root):
200 # Add NS3 support in node
201 plnepi = pl_desc.create("NepiDependency")
202 plns3 = pl_desc.create("NS3Dependency")
203 plnepi.connector("node").connect(pl_node.connector("deps"))
204 plns3.connector("node").connect(pl_node.connector("deps"))
206 # Create NS3 testbed running in pl_node
207 ns3_provider = FactoriesProvider("ns3")
208 ns3_desc = exp_desc.add_testbed_description(ns3_provider)
209 ns3_desc.set_attribute_value("rootDirectory", root)
210 ns3_desc.set_attribute_value("SimulatorImplementationType", "ns3::RealtimeSimulatorImpl")
211 ns3_desc.set_attribute_value("ChecksumEnabled", True)
212 ns3_desc.set_attribute_value(DC.DEPLOYMENT_HOST, "{#[%s].addr[0].[Address]#}" % (
213 pl_iface.get_attribute_value("label"),))
214 ns3_desc.set_attribute_value(DC.DEPLOYMENT_USER,
215 pl_desc.get_attribute_value("slice"))
216 ns3_desc.set_attribute_value(DC.DEPLOYMENT_KEY,
217 pl_desc.get_attribute_value("sliceSSHKey"))
218 ns3_desc.set_attribute_value(DC.DEPLOYMENT_MODE, DC.MODE_DAEMON)
219 ns3_desc.set_attribute_value(DC.DEPLOYMENT_COMMUNICATION, DC.ACCESS_SSH)
220 ns3_desc.set_attribute_value(DC.DEPLOYMENT_ENVIRONMENT_SETUP,
222 pl_node.get_attribute_value("label"),
223 ATTR_NEPI_TESTBED_ENVIRONMENT_SETUP,))
224 ns3_desc.set_attribute_value(DC.LOG_LEVEL, DC.DEBUG_LEVEL)
227 def add_netns_testbed(self, exp_desc):
228 netns_provider = FactoriesProvider("netns")
229 netns_desc = exp_desc.add_testbed_description(netns_provider)
230 netns_desc.set_attribute_value("homeDirectory", self.root_dir)
231 netns_desc.set_attribute_value(DC.DEPLOYMENT_MODE, DC.MODE_DAEMON)
232 netns_root_dir = os.path.join(self.root_dir, "netns")
233 os.mkdir(netns_root_dir)
234 netns_desc.set_attribute_value(DC.ROOT_DIRECTORY, netns_root_dir)
235 netns_desc.set_attribute_value(DC.LOG_LEVEL, DC.DEBUG_LEVEL)
236 netns_desc.set_attribute_value(DC.USE_SUDO, True)
239 def add_netns_node(self, netns_desc):
240 node = netns_desc.create("Node")
241 node.set_attribute_value("forward_X11", True)
244 def add_pl_ns3_connection(self, pl_desc, pl_node, pl_addr,
245 ns3_desc, ns3_node, ns3_addr):
246 pl_tap = pl_desc.create("TapInterface")
247 pl_tap.set_attribute_value("tun_cipher", "PLAIN")
248 self.add_ip_address(pl_tap, pl_addr, 30)
249 pl_node.connector("devs").connect(pl_tap.connector("node"))
250 ns3_fdnd = ns3_desc.create("ns3::FdNetDevice")
251 ns3_node.connector("devs").connect(ns3_fdnd.connector("node"))
252 self.add_ip_address(ns3_fdnd, ns3_addr, 30)
253 pl_tap.connector("fd->").connect(ns3_fdnd.connector("->fd"))
255 def add_pl_ns3_tunchan_connection(self, pl_desc, pl_node, pl_addr,
256 ns3_desc, ns3_node, ns3_addr):
257 pl_tap = pl_desc.create("TunInterface")
258 self.add_ip_address(pl_tap, pl_addr, 30)
259 pl_node.connector("devs").connect(pl_tap.connector("node"))
260 ns3_fdnd = ns3_desc.create("ns3::FdNetDevice")
261 ns3_fdnd.enable_trace("FdPcapTrace")
262 self.add_ip_address(ns3_fdnd, ns3_addr, 30)
263 ns3_node.connector("devs").connect(ns3_fdnd.connector("node"))
264 ns3_tc = ns3_desc.create("ns3::Nepi::TunChannel")
265 ns3_tc.connector("fd->").connect(ns3_fdnd.connector("->fd"))
266 pl_tap.connector("tcp").connect(ns3_tc.connector("tcp"))
268 def add_pl_netns_connection(self, pl_desc, pl_node, pl_addr,
269 netns_desc, netns_node, netns_addr):
270 pl_tap = pl_desc.create("TunInterface")
271 #pl_tap.set_attribute_value("tun_cipher", "PLAIN")
272 pl_tap.set_attribute_value("multicast", True)
273 #pl_tap.enable_trace("pcap")
274 #pl_tap.enable_trace("packets")
275 self.add_ip_address(pl_tap, pl_addr, 30)
276 pl_node.connector("devs").connect(pl_tap.connector("node"))
277 netns_tap = netns_desc.create("TunNodeInterface")
278 netns_tap.set_attribute_value("up", True)
279 netns_tap.set_attribute_value("mtu", 1448)
280 self.add_ip_address(netns_tap, netns_addr, 30)
281 netns_node.connector("devs").connect(netns_tap.connector("node"))
282 netns_tunchannel = netns_desc.create("TunChannel")
283 #netns_tunchannel.set_attribute_value("tun_cipher", "PLAIN")
284 netns_tunchannel.connector("->fd").connect(netns_tap.connector("fd->"))
285 pl_tap.connector("tcp").connect(netns_tunchannel.connector("tcp"))
288 exp_desc = ExperimentDescription()
291 pl_desc = self.add_pl_testbed(exp_desc)
292 pl_inet = pl_desc.create("Internet")
293 pl_node1, pl_iface1 = self.add_pl_node(pl_desc, pl_inet,
297 netns_desc = self.add_netns_testbed(exp_desc)
298 netns_node = self.add_netns_node(netns_desc)
301 ns3_desc = self.add_ns3_in_pl(exp_desc, pl_desc, pl_node1, pl_iface1, "ns3")
302 wifi_chan = self.add_ns3_wifi_channel(ns3_desc)
305 ap_node = self.add_ns3_node(ns3_desc)
306 self.add_ns3_constant_mobility(ns3_desc, ap_node, 0, 0, 0)
307 ap_wifi, ap_phy = self.add_ns3_wifi(ns3_desc, ap_node, access_point = True)
308 self.add_ip_address(ap_wifi, (self.base_addr%33), 27)
309 ap_phy.connector("chan").connect(wifi_chan.connector("phys"))
314 for i in xrange(0, self.nsta):
315 stai = self.add_ns3_node(ns3_desc)
316 angi = (360/self.nsta)*i
317 xi = r*math.cos(angi)
318 yi = r*math.sin(angi)
319 self.add_ns3_constant_mobility(ns3_desc, stai, xi, yi, 0)
320 wifi, phy= self.add_ns3_wifi(ns3_desc, stai, access_point = False)
321 wifi_addr = self.base_addr%(34 + i)
322 self.add_ip_address(wifi, wifi_addr, 27)
323 phy.connector("chan").connect(wifi_chan.connector("phys"))
325 self.add_route(stai, (self.base_addr%0), 30, (self.base_addr%33))
326 self.add_route(stai, (self.base_addr%4), 30, (self.base_addr%33))
329 pl_nodei, pl_ifacei = self.add_pl_node(pl_desc, pl_inet,
332 pl_addr = (self.base_addr%(net+1))
333 ns3_addr = (self.base_addr%(net+2))
334 self.add_pl_ns3_tunchan_connection(pl_desc, pl_nodei, pl_addr,
335 ns3_desc, stai, ns3_addr)
336 self.add_route(pl_nodei, (self.base_addr%32), 27, ns3_addr)
337 self.add_route(pl_nodei, (self.base_addr%0), 30, ns3_addr)
338 self.add_route(pl_nodei, (self.base_addr%4), 30, ns3_addr)
340 network = (self.base_addr%net)
341 self.add_route(netns_node, network, 30, (self.base_addr%2))
342 self.add_route(pl_node1, network, 30, (self.base_addr%6))
343 self.add_route(ap_node, network, 30, wifi_addr)
345 # connection PL1/NETNS
346 pl_addr = (self.base_addr%2)
347 netns_addr = (self.base_addr%1)
348 self.add_pl_netns_connection(pl_desc, pl_node1, pl_addr,
349 netns_desc, netns_node, netns_addr)
352 pl_addr = (self.base_addr%5)
353 ns3_addr = (self.base_addr%6)
354 self.add_pl_ns3_connection(pl_desc, pl_node1, pl_addr,
355 ns3_desc, ap_node, ns3_addr)
359 app = netns_desc.create("Application")
360 app.set_attribute_value("command", command)
361 app.set_attribute_value("user", self.user)
362 app.connector("node").connect(netns_node.connector("apps"))
365 #target = "{#[%s].addr[0].[Address]#}" % label
369 target = self.base_addr%2
370 local = self.base_addr%1
372 command = "sleep 2; vlc -I dummy %s --miface-addr=%s --sout '#udp{dst=%s:%d}' vlc://quit" \
373 % (local,self.movie, target, port)
374 vlc_server = netns_desc.create("Application")
375 vlc_server.set_attribute_value("command", command)
376 vlc_server.set_attribute_value("user", self.user)
377 vlc_server.connector("node").connect(netns_node.connector("apps"))
378 servers.append(vlc_server.guid)
380 command = "sudo dbus-uuidgen --ensure; vlc -vvv -I dummy udp://@%s:%d --sout '#std{access=file,mux=ts,dst=big_buck_bunny_stream.ts}' " % (target, port)
381 vlc_client = pl_desc.create("Application")
382 vlc_client.set_attribute_value("buildDepends", "vlc")
383 vlc_client.set_attribute_value("rpmFusion", True)
384 vlc_client.set_attribute_value("command", command)
385 vlc_client.enable_trace("stdout")
386 vlc_client.enable_trace("stderr")
387 vlc_client.connector("node").connect(pl_node1.connector("apps"))
388 clients.append(vlc_client.guid)
391 self.add_route(netns_node, (self.base_addr%32), 27, (self.base_addr%2))
392 self.add_route(netns_node, (self.base_addr%4), 30, (self.base_addr%2))
394 self.add_route(pl_node1, (self.base_addr%32), 27, (self.base_addr%6))
396 self.add_route(ap_node, (self.base_addr%0), 30, (self.base_addr%5))
398 xml = exp_desc.to_xml()
399 controller = ExperimentController(xml, self.root_dir)
401 while not controller.is_finished(app.guid):
405 controller.shutdown()
408 xml = exp_desc.to_xml()
409 controller = ExperimentController(xml, self.root_dir)
416 if not controller.is_finished(guid):
420 controller.shutdown()
424 shutil.rmtree(self.root_dir)
426 if __name__ == '__main__':
427 example = WirelessOverlay()