merge
[nepi.git] / examples / wireless_overlay.py
1 #!/usr/bin/env python
2 # -*- coding: utf-8 -*-
3
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
9 import os
10 import shutil
11 import tempfile
12 import itertools
13 import time
14 import math
15
16 """
17   -----------------------------------------------------------------------
18   |    NETNS                                                            |                                    
19   |                                                                     |
20   |    +-----+     +-----+    +-----+    +-----+          +------+      |
21   |    |node1|     |node2|    |node3|    |node4 |         |server|      |
22   |    +-----+     +-----+    +-----+    +-----+          +------+      |
23   |     65/30       69/30      73/30      77/30            226/30       |
24   |    -------     -------    -------    -------          --------      |
25   |       |           |          |          |                |          |
26   |---------------------------------------------------------------------|
27   |       |           |          |          |                |          | 
28   |    -------     -------    -------    -------             |          |
29   |     66/30       30/30     74/30       78/30              |          |
30   |    +-----+     +-----+    +-----+    +-----+             |          |
31   |    |sta0 |     |sta1 |    |sta2 |    |sta3 |             |          |
32   |    +-----+     +-----+    +-----+    +-----+             |          |
33   |     34/27       35/27      36/27      37/27              |          |
34   |    -------     --------    -------   -------             |          |
35   |     ((*))       ((*))       ((*))     ((*))              |          |
36   |                                                          |          |
37   |                                                          |          |
38   |                       ((*))                              |          |
39   |                     --------                             |          |
40   |                       33/27                              |          |
41   |                     +------+                             |          |
42   |                     |  AP  |  225/30 |--------------------          |
43   |                     +------+                                        |
44   |                                                                     |
45   |     NS-3                                                            |
46   -----------------------------------------------------------------------
47
48 """
49
50 class WirelessOverlay(object):
51     def __init__(self):
52         usage = "usage: %prog -n number_sta -m movie -u user"
53         parser = OptionParser(usage=usage)
54         parser.add_option("-u", "--user", dest="user", help="Valid linux system user (not root).", type="str")
55         parser.add_option("-m", "--movie", dest="movie", help="Path to movie file to play", type="str")
56         parser.add_option("-n", "--nsta", dest="nsta", help="Number of wifi stations", type="int")
57         parser.add_option("-a", "--base_addr", dest="base_addr", help="Base address segment for the experiment", type="str")
58         (options, args) = parser.parse_args()
59         if not options.movie:
60             parser.error("Missing 'movie' option.")
61         if options.user == 'root':
62             parser.error("Missing or invalid 'user' option.")
63         if options.user == 'root':
64             parser.error("Missing or invalid 'user' option.")
65         if options.nsta and options.nsta > 8:
66             parser.error("Try a number of stations under 9.")
67
68         self.user = options.user if options.user else os.getlogin()
69         self.movie = options.movie
70         self.nsta = options.nsta if options.nsta else 4
71         base = options.base_addr if options.base_addr else "192.168.4"
72         self.base_addr = base + ".%d"
73         self.root_dir = tempfile.mkdtemp()
74
75     def add_netns_tap(self, netns_desc, node):
76         tap = netns_desc.create("TapNodeInterface")
77         tap.set_attribute_value("up", True)
78         node.connector("devs").connect(tap.connector("node"))
79         return tap
80
81     def add_ns3_fdnd(self, ns3_desc, node):
82         fdnd = ns3_desc.create("ns3::FdNetDevice")
83         node.connector("devs").connect(fdnd.connector("node"))
84         fdnd.enable_trace("FdPcapTrace")
85         return fdnd
86
87     def add_ns3_node(self, ns3_desc):
88         node = ns3_desc.create("ns3::Node")
89         ipv4 = ns3_desc.create("ns3::Ipv4L3Protocol")
90         arp  = ns3_desc.create("ns3::ArpL3Protocol")
91         icmp = ns3_desc.create("ns3::Icmpv4L4Protocol")
92         udp = ns3_desc.create("ns3::UdpL4Protocol")
93         node.connector("protos").connect(ipv4.connector("node"))
94         node.connector("protos").connect(arp.connector("node"))
95         node.connector("protos").connect(icmp.connector("node"))
96         node.connector("protos").connect(udp.connector("node"))
97         return node
98
99     def add_ns3_wifi(self, ns3_desc, node, access_point = False):
100         wifi = ns3_desc.create("ns3::WifiNetDevice")
101         node.connector("devs").connect(wifi.connector("node"))
102
103         phy = ns3_desc.create("ns3::YansWifiPhy")
104         error = ns3_desc.create("ns3::NistErrorRateModel")
105         manager = ns3_desc.create("ns3::ArfWifiManager")
106         if access_point:
107             mac = ns3_desc.create("ns3::ApWifiMac")
108         else:
109             mac = ns3_desc.create("ns3::StaWifiMac")
110
111         phy.set_attribute_value("Standard", "WIFI_PHY_STANDARD_80211a")
112         mac.set_attribute_value("Standard", "WIFI_PHY_STANDARD_80211a")
113         phy.connector("err").connect(error.connector("phy"))
114         wifi.connector("phy").connect(phy.connector("dev"))
115         wifi.connector("mac").connect(mac.connector("dev"))
116         wifi.connector("manager").connect(manager.connector("dev"))
117
118         phy.enable_trace("YansWifiPhyPcapTrace")
119         return wifi, phy
120
121     def add_ns3_constant_mobility(self, ns3_desc, node, x, y, z):
122         mobility = ns3_desc.create("ns3::ConstantPositionMobilityModel") 
123         position = "%d:%d:%d" % (x, y, z)
124         mobility.set_attribute_value("Position", position)
125         node.connector("mobility").connect(mobility.connector("node"))
126         return mobility
127
128     def add_ns3_wifi_channel(self, ns3_desc):
129         channel = ns3_desc.create("ns3::YansWifiChannel")
130         delay = ns3_desc.create("ns3::ConstantSpeedPropagationDelayModel")
131         loss  = ns3_desc.create("ns3::LogDistancePropagationLossModel")
132         channel.connector("delay").connect(delay.connector("chan"))
133         channel.connector("loss").connect(loss.connector("prev"))
134         return channel
135
136     def add_ip_address(self, iface, address, netprefix):
137         ip = iface.add_address()
138         ip.set_attribute_value("Address", address)
139         ip.set_attribute_value("NetPrefix", netprefix)
140
141     def add_route(self, node, destination, netprefix, nexthop):
142         route = node.add_route()
143         route.set_attribute_value("Destination", destination)
144         route.set_attribute_value("NetPrefix", netprefix)
145         route.set_attribute_value("NextHop", nexthop)
146
147     def run(self):
148         exp_desc = ExperimentDescription()
149
150         ## NS3 ##
151
152         ns3_provider = FactoriesProvider("ns3")
153         ns3_desc = exp_desc.add_testbed_description(ns3_provider)
154         ns3_desc.set_attribute_value("homeDirectory", self.root_dir)
155         ns3_desc.set_attribute_value("SimulatorImplementationType", "ns3::RealtimeSimulatorImpl")
156         ns3_desc.set_attribute_value("ChecksumEnabled", True)
157         
158         ## NS3 wifi network 32/27
159         r = 50
160         wifi_chan = self.add_ns3_wifi_channel(ns3_desc)
161         
162         # AP node
163         ap = self.add_ns3_node(ns3_desc)
164         self.add_ns3_constant_mobility(ns3_desc, ap, 0, 0, 0)
165
166         wifi_ap, phy_ap = self.add_ns3_wifi(ns3_desc, ap, access_point = True)
167         self.add_ip_address(wifi_ap, (self.base_addr%33), 27)
168         phy_ap.connector("chan").connect(wifi_chan.connector("phys"))
169         
170         sta_nodes = []
171         
172         # STA nodes
173         for i in xrange(0, self.nsta):
174             stai = self.add_ns3_node(ns3_desc)
175             angi = (360/self.nsta)*i
176             xi = r*math.cos(angi)
177             yi = r*math.sin(angi)
178             self.add_ns3_constant_mobility(ns3_desc, stai, xi, yi, 0)
179             wifi, phy= self.add_ns3_wifi(ns3_desc, stai, access_point = False)
180             wifi_addr = self.base_addr%(34 + i)
181             self.add_ip_address(wifi, wifi_addr, 27)
182             phy.connector("chan").connect(wifi_chan.connector("phys"))
183             sta_nodes.append((stai, wifi_addr))
184
185         ## NETNS ##
186
187         netns_provider = FactoriesProvider("netns")
188         netns_desc = exp_desc.add_testbed_description(netns_provider)
189         netns_desc.set_attribute_value("homeDirectory", self.root_dir)
190         netns_desc.set_attribute_value(DC.DEPLOYMENT_MODE, DC.MODE_DAEMON)
191         netns_root_dir = os.path.join(self.root_dir, "netns_instance")
192         os.mkdir(netns_root_dir)
193         netns_desc.set_attribute_value(DC.ROOT_DIRECTORY, netns_root_dir)
194         netns_desc.set_attribute_value(DC.LOG_LEVEL, DC.DEBUG_LEVEL)
195         netns_desc.set_attribute_value(DC.USE_SUDO, True)
196
197         server = netns_desc.create("Node")
198         server.set_attribute_value("forward_X11", True)
199
200         #command = "xterm" 
201         #app = netns_desc.create("Application")
202         #app.set_attribute_value("command", command)
203         #app.set_attribute_value("user", self.user)
204         #app.connector("node").connect(server.connector("apps"))
205
206         # INTERCONNECTION NETNS/NS3
207
208         tap = self.add_netns_tap(netns_desc, server)
209         fdnd_ap = self.add_ns3_fdnd(ns3_desc, ap)
210         fdnd_ap.connector("->fd").connect(tap.connector("fd->"))
211         ## net NS3::fdnd/NETNS::tap 224/30
212         self.add_ip_address(fdnd_ap, (self.base_addr%225), 30)
213         self.add_ip_address(tap, (self.base_addr%226), 30)
214
215         # ROUTES
216         self.add_route(server, (self.base_addr%32), 27, (self.base_addr%225))
217
218         servers = []
219         clients = []
220         i = 0
221         for (stai, wifi_addr) in sta_nodes:
222             # fdnd - netns tap
223             nodei = netns_desc.create("Node")
224             nodei.set_attribute_value("forward_X11", True)
225             label = "client%d" % i
226             nodei.set_attribute_value("label", label)
227             tapi = self.add_netns_tap(netns_desc, nodei)
228             fdndi = self.add_ns3_fdnd(ns3_desc, stai)
229             fdndi.connector("->fd").connect(tapi.connector("fd->"))
230             
231             ## net NS3::fdnd/NETNS::tap subnets of 64/27
232             net = 64 + i
233             self.add_ip_address(tapi, (self.base_addr%(net + 1)), 30)
234             self.add_ip_address(fdndi, (self.base_addr%(net + 2)), 30)
235             
236             # routes
237             self.add_route(nodei, (self.base_addr%32), 27, (self.base_addr%(net+2)))
238             self.add_route(nodei, (self.base_addr%224), 30, (self.base_addr%(net+2)))
239             self.add_route(stai, (self.base_addr%224), 30, (self.base_addr%33))
240             
241             self.add_route(ap, (self.base_addr%net), 30, wifi_addr)
242             self.add_route(server, (self.base_addr%net), 30, (self.base_addr%225))
243
244             # applications
245             #target = "{#[%s].addr[0].[Address]#}" % label
246             target = self.base_addr%(net+1)
247             port = 5000 + net + 1
248             command = "vlc -I dummy %s --sout '#rtp{dst=%s,port=%d,mux=ts}' vlc://quit" \
249                 % (self.movie, target, port)
250             vlc_server = netns_desc.create("Application")
251             vlc_server.set_attribute_value("command", command)
252             vlc_server.set_attribute_value("user", self.user)
253             vlc_server.connector("node").connect(server.connector("apps"))
254             servers.append(vlc_server.guid)
255
256             command = "vlc rtp://%s:%d/test.ts" % (target, port)
257             vlc_client = netns_desc.create("Application")
258             vlc_client.set_attribute_value("command", command)
259             vlc_client.set_attribute_value("user", self.user)
260             vlc_client.connector("node").connect(nodei.connector("apps"))
261             clients.append(vlc_client.guid)
262
263             i += 4
264
265         xml = exp_desc.to_xml()
266         controller = ExperimentController(xml, self.root_dir)
267         controller.start()
268         stop = False
269         while not stop:
270             time.sleep(0.5)
271             stop = True
272             for guid in clients:
273                 if not controller.is_finished(guid):
274                     stop = False
275         time.sleep(0.1)
276         controller.stop()
277         controller.shutdown()
278
279     def clean(self):
280         shutil.rmtree(self.root_dir)
281
282 if __name__ == '__main__':
283     example = WirelessOverlay()
284     example.run()
285     example.clean()
286