Adding examples/ns3/multi_host
[nepi.git] / examples / ns3 / multi_host / experiment.py
1 #\r
2 #    NEPI, a framework to manage network experiments\r
3 #    Copyright (C) 2013 INRIA\r
4 #\r
5 #    This program is free software: you can redistribute it and/or modify\r
6 #    it under the terms of the GNU General Public License version 2 as\r
7 #    published by the Free Software Foundation;\r
8 #\r
9 #    This program is distributed in the hope that it will be useful,\r
10 #    but WITHOUT ANY WARRANTY; without even the implied warranty of\r
11 #    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
12 #    GNU General Public License for more details.\r
13 #\r
14 #    You should have received a copy of the GNU General Public License\r
15 #    along with this program.  If not, see <http://www.gnu.org/licenses/>.\r
16 #\r
17 # Author: Damien Saucez <damien.saucez@inria.fr>\r
18 #         Alina Quereilhac <alina.quereilhac@inria.fr>\r
19 \r
20 import ipaddr\r
21 from random import randint\r
22 from nepi.execution.ec import ExperimentController \r
23 from nepi.execution.resource import ResourceState, ResourceAction\r
24 \r
25 # ########################################################\r
26 class Experiment(object):\r
27     # ec : ExperimentController\r
28     # node: planetlab::Node\r
29     def __init__(self, ec, node_info, nb_nodes, real_time = True):\r
30         print "Experiement %s %s" % (node_info, nb_nodes)\r
31 \r
32         # remember the ExperimentController the experiment is associated to\r
33         self.ec = ec\r
34 \r
35         # define the physical machine to run the experiment on\r
36         self.add_node(node_info)\r
37 \r
38         # number of simulated nodes moving in the \r
39         self.nb_nodes = nb_nodes\r
40         \r
41         # fix the geographical boundaries of the network\r
42         self.bounds_width = self.bounds_height = 100\r
43         \r
44         # fix the speed at which mobile nodes can move\r
45         self.speed = 1\r
46 \r
47         # collection of simulated node (their GID) in the simulator\r
48         #   nsnodes[0] is always the GID of the simulated node containing the\r
49         #   access point\r
50         self.nsnodes = list()\r
51         \r
52         # collection of application (their GID) running in the simulator\r
53         #   apps[0] is always the GID of the agent application running on the\r
54         #   access point\r
55         self.apps = list()\r
56 \r
57         # prepare the ns-3 simulator to use for the experiment\r
58         self.add_simulator(real_time)\r
59 \r
60         # for sanity check\r
61         self.topology_built = False\r
62 \r
63     def add_node(self, node_info):\r
64         """\r
65         Define the physical machine on which run the experiment\r
66         """\r
67         if node_info["hostname"] == "localhost":\r
68             self.node = self.ec.register_resource("linux::Node")\r
69             self.ec.set(self.node, "hostname", "localhost")\r
70         else:\r
71             self.node = self.ec.register_resource("planetlab::Node")\r
72             self.ec.set(self.node, "hostname", node_info["hostname"])\r
73             self.ec.set(self.node, "username", node_info["username"])\r
74             self.ec.set(self.node, "identity", node_info["identity"])\r
75             self.ec.set(self.node, "cleanProcesses", True)\r
76             self.ec.set(self.node, "cleanExperiment", True)\r
77 \r
78         return self.node\r
79 \r
80     def add_simulator(self, real_time):\r
81         """\r
82         Add a ns-3 simulator on the node used for the experiment\r
83         """\r
84         # creat the ns-3 simulator instance\r
85         self.simu = self.ec.register_resource("linux::ns3::Simulation")\r
86         self.ec.set (self.simu, "StopTime", "200s")\r
87         #\r
88         # run it in realtime mode if asked\r
89         if real_time:\r
90             self.ec.set(self.simu, "simulatorImplementationType", "ns3::RealtimeSimulatorImpl")\r
91         # \r
92         # log additional information\r
93         self.ec.set(self.simu, "checksumEnabled", True)\r
94         self.ec.set(self.simu, "verbose", True)\r
95         self.ec.set(self.simu, "enableDump", True)\r
96         self.ec.register_connection(self.simu, self.node)\r
97 \r
98         return self.simu\r
99 \r
100     # == ns-3 simulation helper functions =====================================\r
101     def add_nsnode(self):\r
102         """\r
103         Create a ns-3 node and add it to the simulator\r
104         """\r
105         # create a ns-3 node\r
106         nsnode = self.ec.register_resource("ns3::Node")\r
107         # enable its network stack\r
108         self.ec.set(nsnode, "enableStack", True)\r
109         self.ec.register_connection(nsnode, self.simu)\r
110 \r
111         return nsnode\r
112 \r
113     def add_wifi_channel(self):\r
114         """\r
115         Create the WiFi channel on which all nodes will be connected\r
116         """\r
117         # create a channel\r
118         channel = self.ec.register_resource("ns3::YansWifiChannel")\r
119         \r
120         # specify the delay model\r
121         delay = self.ec.register_resource("ns3::ConstantSpeedPropagationDelayModel")\r
122         self.ec.register_connection(channel, delay)\r
123 \r
124         # specify a loss model\r
125         loss  = self.ec.register_resource("ns3::LogDistancePropagationLossModel")\r
126         self.ec.register_connection(channel, loss)\r
127 \r
128         return channel\r
129 \r
130     def add_wifi_device(self, node, ip, prefix, access_point = False):\r
131         """\r
132         Add and configure the WiFi interface on a simulated node\r
133         """\r
134         \r
135         # create the WiFi network interface\r
136         dev = self.ec.register_resource("ns3::WifiNetDevice")\r
137         \r
138         # specify the network layer parameters\r
139         self.ec.set(dev, "ip", ip)\r
140         self.ec.set(dev, "prefix", prefix)\r
141         self.ec.register_connection(node, dev)\r
142         \r
143         # specify the MAC layer parameters\r
144         #\r
145         # can be in access point mode or not\r
146         if access_point:\r
147             mac = self.ec.register_resource("ns3::ApWifiMac")\r
148         else:\r
149             mac = self.ec.register_resource("ns3::StaWifiMac")\r
150         # the MAC is IEEE 802.11a\r
151         self.ec.set(mac, "Standard", "WIFI_PHY_STANDARD_80211a")\r
152         self.ec.register_connection(dev, mac)\r
153 \r
154         # specify the physical layer parameters\r
155         phy = self.ec.register_resource("ns3::YansWifiPhy")\r
156         #\r
157         # it physical layer is IEEE802.11a\r
158         self.ec.set(phy, "Standard", "WIFI_PHY_STANDARD_80211a")\r
159         self.ec.register_connection(dev, phy)\r
160         #\r
161         # specify an error model for transmissions\r
162         error = self.ec.register_resource("ns3::NistErrorRateModel")\r
163         self.ec.register_connection(phy, error)\r
164         \r
165         # specify the Wifi manager to be assocated with the interface\r
166         manager = self.ec.register_resource("ns3::ArfWifiManager")\r
167         self.ec.register_connection(dev, manager)\r
168 \r
169         return dev, phy\r
170 \r
171     def add_random_mobility(self, node, x, y, z, speed, bounds_width, bounds_height):\r
172         """\r
173         Create a mobility model for node with random movements\r
174         """\r
175         position = "%d:%d:%d" % (x, y, z)\r
176         bounds = "0|%d|0|%d" % (bounds_width, bounds_height) \r
177         speed = "ns3::UniformRandomVariable[Min=%d|Max=%s]" % (speed, speed)\r
178         pause = "ns3::ConstantRandomVariable[Constant=1.0]"\r
179 \r
180         mobility = self.ec.register_resource("ns3::RandomDirection2dMobilityModel")\r
181         self.ec.set(mobility, "Position", position)\r
182         self.ec.set(mobility, "Bounds", bounds)\r
183         self.ec.set(mobility, "Speed", speed)\r
184         self.ec.set(mobility, "Pause",  pause)\r
185         self.ec.register_connection(node, mobility)\r
186 \r
187         return mobility\r
188 \r
189     def add_constant_mobility(self, node, x, y, z):\r
190         """\r
191         Create a mobility model for node with a constant position\r
192         """\r
193         mobility = self.ec.register_resource("ns3::ConstantPositionMobilityModel") \r
194         position = "%d:%d:%d" % (x, y, z)\r
195         self.ec.set(mobility, "Position", position)\r
196         self.ec.register_connection(node, mobility)\r
197 \r
198         return mobility\r
199 \r
200     def create_simulated_node(self, ip, prefix, channel, access_point, x, y):\r
201         """\r
202         Create a simulated node connected on a WiFi channel\r
203         """\r
204         # Create the ns node that will run the application\r
205         nsnode = self.add_nsnode()\r
206 \r
207         # Add a WiFi interface to the node\r
208         dev, phy = self.add_wifi_device(nsnode, ip, prefix, access_point)\r
209         #\r
210         # Connect the access point to the WiFi network\r
211         self.ec.register_connection(channel, phy)\r
212 \r
213         # Specify that the node mobility \r
214         #\r
215         # access point is not mobile\r
216         if access_point:\r
217             mobility = self.add_constant_mobility(nsnode, x, y, 0)\r
218         # other nodes have random mobility pattern\r
219         else:\r
220             mobility = self.add_random_mobility(nsnode, x, y, 0, self.speed, self.bounds_width, self.bounds_height)\r
221 \r
222         return nsnode\r
223 \r
224     def add_route(self, nsnode, netblock, prefix, nexthop):\r
225         """\r
226         add a route on ns-3 node nsnode for netblock/prefix via nexthop\r
227         """\r
228         route = self.ec.register_resource("ns3::Route")\r
229         self.ec.set(route, "network", netblock)\r
230         self.ec.set(route, "prefix", prefix)\r
231         self.ec.set(route, "nexthop", nexthop)\r
232         self.ec.register_connection(route, nsnode)\r
233         print "route %s/%s via %s added on nsnode %s (%s)" % (netblock, prefix, nexthop, nsnode, self)\r
234 \r
235         return route\r
236 \r
237     def add_vroute(self, dev, netblock, prefix, nexthop):\r
238         """\r
239         Add a route on Planetlab node for netblock/prefix via nexthop\r
240         """\r
241         route = self.ec.register_resource("planetlab::Vroute")\r
242         self.ec.set(route, "network", netblock)\r
243         self.ec.set(route, "prefix", prefix)\r
244         self.ec.set(route, "nexthop", nexthop)\r
245         self.ec.register_connection(route, dev)\r
246         print "Vroute %s/%s via %s added on nsnode %s (%s)" % (netblock, prefix, nexthop, dev, self)\r
247 \r
248         return route\r
249 \r
250     def add_agent(self, nsnode):\r
251         """\r
252         Add a agent application\r
253         """\r
254         # Create a DCE application running the agent code\r
255         app = self.ec.register_resource("linux::ns3::dce::Application")\r
256         self.ec.set(app, "sources", "code/agent.c")\r
257         self.ec.set(app "build", "gcc -fPIC -pie -rdynamic ${SRC}/agent.c -o ${BIN_DCE}/agent")\r
258         self.ec.set(app, "binary", "agent")\r
259         self.ec.set(app, "arguments", "45005")\r
260         self.ec.set(app, "stackSize", 1<<20)\r
261         self.ec.set(app, "StartTime", "10s")\r
262         self.ec.set(app, "StopTime", "200s")\r
263 \r
264         # Associate the application with the simulated node\r
265         self.ec.register_connection(app, nsnode)\r
266         \r
267         # Make the application start only once the simulated node is started\r
268         self.ec.register_condition(app, ResourceAction.START, \r
269                 nsnode, ResourceState.STARTED, time="5s")\r
270 \r
271         return app\r
272 \r
273     def add_transmitter(self, nsnode):\r
274         """\r
275         Add a transmitter application\r
276         """\r
277         # Create a DCE application running the transmitter code\r
278         app = self.ec.register_resource("linux::ns3::dce::Application")\r
279         self.ec.set(app, "sources", "code/transmitter.c")\r
280         self.ec.set(app, "build", "gcc -fPIC -pie -rdynamic ${SRC}/transmitter.c -o ${BIN_DCE}/transmitter")\r
281         self.ec.set(app, "binary", "transmitter")\r
282         self.ec.set(app, "arguments", "%s;45005" % target)\r
283         self.ec.set(app, "stackSize", 1<<20)\r
284         self.ec.set(app, "StartTime", "10s")\r
285         self.ec.set(app, "StopTime", "200s")\r
286 \r
287         # Associate the application with the simulated node\r
288         self.ec.register_connection(app, nsnode)\r
289         #\r
290         # Make the application start only once the simulated node and the serer are started\r
291         self.ec.register_condition(app, ResourceAction.START, \r
292                 [nsnode, self.apps[0]], ResourceState.STARTED, time="10s")\r
293 \r
294         return app\r
295 \r
296     def add_planetlab_transmitter(self, target):\r
297         """\r
298         Add a planetlab transmitter application\r
299         """\r
300 \r
301         # Create an application running the transmitter code\r
302         app = self.ec.register_resource("linux::Application")\r
303         self.ec.set(app, "sources", "code/transmitter.c")\r
304         self.ec.set(app, "build", "make ${SRC}/transmitter")\r
305         self.ec.set(app, "command", "${SRC}/transmitter %s 45005" % target)\r
306 \r
307         # Associate the application with the Planetlab node\r
308         self.ec.register_connection(app, self.node)\r
309         \r
310         # Make the application start only once the simulated agent and the node are started\r
311         self.ec.register_condition(app, ResourceAction.START, \r
312                 [self.apps[0], self.node], ResourceState.STARTED, time="10s")\r
313 \r
314         return app\r
315 \r
316     # == Topology construction ================================================\r
317     def build_topology(self, netblock, prefix, target):\r
318         """\r
319         Builds a topology composed of one fixed access point and nb_nodes\r
320         mobile nodes\r
321         """\r
322 \r
323         # Rember network parameters\r
324         self.netblock = netblock\r
325         self.prefix = prefix\r
326 \r
327         # Create the WiFi network via which nodes are connected\r
328         chan = self.add_wifi_channel()\r
329 \r
330         # == Access point\r
331         # Geographical position of the access point\r
332         x=50\r
333         y=0\r
334 \r
335         # the IP address of the access point is the first in the prefix\r
336         self.ip_ap = str(ipaddr.IPv4Address(self.netblock) + 1)\r
337         print "IP AP: %s " % (self.ip_ap)\r
338 \r
339         # Create the ns node that will run the access point\r
340         nsnode = self.create_simulated_node(self.ip_ap, self.prefix, chan, True, x, y)\r
341         \r
342         # add the node in the collection of simulated nodes\r
343         self.nsnodes.append(nsnode)\r
344 \r
345         # Run a agent application on the access point\r
346         agent = self.add_agent(nsnode)\r
347         \r
348         # add the agent application in the collection of applications\r
349         self.apps.append(agent)\r
350         \r
351         # Add nb_nodes mobile nodes in the WiFi network\r
352         for i in range(1, self.nb_nodes + 1):\r
353             # pic a random initial location\r
354             x = randint(0, self.bounds_width)\r
355             y = randint(0, self.bounds_height)\r
356 \r
357             # define the appropriate IP address of the node (sequential IP in what remains after the access point IP)\r
358             ip = str(ipaddr.IPv4Address(self.ip_ap) + i)\r
359 \r
360             print "IP mobile: " , ip\r
361 \r
362             # Create the ns node that will run the mobile node\r
363             nsnode = self.create_simulated_node(ip, self.prefix, chan, False, x, y)\r
364             # \r
365             # add the node in the collection of simulated nodes\r
366             self.nsnodes.append(nsnode)\r
367 \r
368             if target:\r
369                 # Run a transmitter application on the mobile node\r
370                 transmitter = self.add_transmitter(nsnode)\r
371                 \r
372                 # add the trasmitter application in the collection of applications\r
373                 self.apps.append(transmitter)\r
374 \r
375             # Add a default route via the access point\r
376             self.add_route(nsnode, "0.0.0.0", "0", self.ip_ap)\r
377 \r
378         # for sanity check\r
379         self.topology_built = True\r