2 # NEPI, a framework to manage network experiments
\r
3 # Copyright (C) 2013 INRIA
\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
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
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
17 # Author: Damien Saucez <damien.saucez@inria.fr>
\r
18 # Alina Quereilhac <alina.quereilhac@inria.fr>
\r
21 from random import randint
\r
22 from nepi.execution.ec import ExperimentController
\r
23 from nepi.execution.resource import ResourceState, ResourceAction
\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
32 # remember the ExperimentController the experiment is associated to
\r
35 # define the physical machine to run the experiment on
\r
36 self.add_node(node_info)
\r
38 # number of simulated nodes moving in the
\r
39 self.nb_nodes = nb_nodes
\r
41 # fix the geographical boundaries of the network
\r
42 self.bounds_width = self.bounds_height = 100
\r
44 # fix the speed at which mobile nodes can move
\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
50 self.nsnodes = list()
\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
57 # prepare the ns-3 simulator to use for the experiment
\r
58 self.add_simulator(real_time)
\r
61 self.topology_built = False
\r
63 def add_node(self, node_info):
\r
65 Define the physical machine on which run the experiment
\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
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
80 def add_simulator(self, real_time):
\r
82 Add a ns-3 simulator on the node used for the experiment
\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
88 # run it in realtime mode if asked
\r
90 self.ec.set(self.simu, "simulatorImplementationType", "ns3::RealtimeSimulatorImpl")
\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
100 # == ns-3 simulation helper functions =====================================
\r
101 def add_nsnode(self):
\r
103 Create a ns-3 node and add it to the simulator
\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
113 def add_wifi_channel(self):
\r
115 Create the WiFi channel on which all nodes will be connected
\r
118 channel = self.ec.register_resource("ns3::YansWifiChannel")
\r
120 # specify the delay model
\r
121 delay = self.ec.register_resource("ns3::ConstantSpeedPropagationDelayModel")
\r
122 self.ec.register_connection(channel, delay)
\r
124 # specify a loss model
\r
125 loss = self.ec.register_resource("ns3::LogDistancePropagationLossModel")
\r
126 self.ec.register_connection(channel, loss)
\r
130 def add_wifi_device(self, node, ip, prefix, access_point = False):
\r
132 Add and configure the WiFi interface on a simulated node
\r
135 # create the WiFi network interface
\r
136 dev = self.ec.register_resource("ns3::WifiNetDevice")
\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
143 # specify the MAC layer parameters
\r
145 # can be in access point mode or not
\r
147 mac = self.ec.register_resource("ns3::ApWifiMac")
\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
154 # specify the physical layer parameters
\r
155 phy = self.ec.register_resource("ns3::YansWifiPhy")
\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
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
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
171 def add_random_mobility(self, node, x, y, z, speed, bounds_width, bounds_height):
\r
173 Create a mobility model for node with random movements
\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
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
189 def add_constant_mobility(self, node, x, y, z):
\r
191 Create a mobility model for node with a constant position
\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
200 def create_simulated_node(self, ip, prefix, channel, access_point, x, y):
\r
202 Create a simulated node connected on a WiFi channel
\r
204 # Create the ns node that will run the application
\r
205 nsnode = self.add_nsnode()
\r
207 # Add a WiFi interface to the node
\r
208 dev, phy = self.add_wifi_device(nsnode, ip, prefix, access_point)
\r
210 # Connect the access point to the WiFi network
\r
211 self.ec.register_connection(channel, phy)
\r
213 # Specify that the node mobility
\r
215 # access point is not mobile
\r
217 mobility = self.add_constant_mobility(nsnode, x, y, 0)
\r
218 # other nodes have random mobility pattern
\r
220 mobility = self.add_random_mobility(nsnode, x, y, 0, self.speed, self.bounds_width, self.bounds_height)
\r
224 def add_route(self, nsnode, netblock, prefix, nexthop):
\r
226 add a route on ns-3 node nsnode for netblock/prefix via nexthop
\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
237 def add_vroute(self, dev, netblock, prefix, nexthop):
\r
239 Add a route on Planetlab node for netblock/prefix via nexthop
\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
250 def add_agent(self, nsnode):
\r
252 Add a agent application
\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
264 # Associate the application with the simulated node
\r
265 self.ec.register_connection(app, nsnode)
\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
273 def add_transmitter(self, nsnode):
\r
275 Add a transmitter application
\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
287 # Associate the application with the simulated node
\r
288 self.ec.register_connection(app, nsnode)
\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
296 def add_planetlab_transmitter(self, target):
\r
298 Add a planetlab transmitter application
\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
307 # Associate the application with the Planetlab node
\r
308 self.ec.register_connection(app, self.node)
\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
316 # == Topology construction ================================================
\r
317 def build_topology(self, netblock, prefix, target):
\r
319 Builds a topology composed of one fixed access point and nb_nodes
\r
323 # Rember network parameters
\r
324 self.netblock = netblock
\r
325 self.prefix = prefix
\r
327 # Create the WiFi network via which nodes are connected
\r
328 chan = self.add_wifi_channel()
\r
331 # Geographical position of the access point
\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
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
342 # add the node in the collection of simulated nodes
\r
343 self.nsnodes.append(nsnode)
\r
345 # Run a agent application on the access point
\r
346 agent = self.add_agent(nsnode)
\r
348 # add the agent application in the collection of applications
\r
349 self.apps.append(agent)
\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
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
360 print "IP mobile: " , ip
\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
365 # add the node in the collection of simulated nodes
\r
366 self.nsnodes.append(nsnode)
\r
369 # Run a transmitter application on the mobile node
\r
370 transmitter = self.add_transmitter(nsnode)
\r
372 # add the trasmitter application in the collection of applications
\r
373 self.apps.append(transmitter)
\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
379 self.topology_built = True
\r