#
# NEPI, a framework to manage network experiments
# Copyright (C) 2013 INRIA
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License version 2 as
# published by the Free Software Foundation;
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see .
#
# Author: Damien Saucez
# Alina Quereilhac
import ipaddr
from random import randint
from nepi.execution.ec import ExperimentController
from nepi.execution.resource import ResourceState, ResourceAction
# ########################################################
class Experiment(object):
# ec : ExperimentController
# node: planetlab::Node
def __init__(self, ec, node_info, nb_nodes, real_time = True):
print "Experiement %s %s" % (node_info, nb_nodes)
# remember the ExperimentController the experiment is associated to
self.ec = ec
# define the physical machine to run the experiment on
self.add_node(node_info)
# number of simulated nodes moving in the
self.nb_nodes = nb_nodes
# fix the geographical boundaries of the network
self.bounds_width = self.bounds_height = 100
# fix the speed at which mobile nodes can move
self.speed = 1
# collection of simulated node (their GID) in the simulator
# nsnodes[0] is always the GID of the simulated node containing the
# access point
self.nsnodes = list()
# collection of application (their GID) running in the simulator
# apps[0] is always the GID of the agent application running on the
# access point
self.apps = list()
# prepare the ns-3 simulator to use for the experiment
self.add_simulator(real_time)
# for sanity check
self.topology_built = False
def add_node(self, node_info):
"""
Define the physical machine on which run the experiment
"""
if node_info["hostname"] == "localhost":
self.node = self.ec.register_resource("linux::Node")
self.ec.set(self.node, "hostname", "localhost")
else:
self.node = self.ec.register_resource("planetlab::Node")
self.ec.set(self.node, "hostname", node_info["hostname"])
self.ec.set(self.node, "username", node_info["username"])
self.ec.set(self.node, "identity", node_info["identity"])
self.ec.set(self.node, "cleanProcesses", True)
self.ec.set(self.node, "cleanExperiment", True)
return self.node
def add_simulator(self, real_time):
"""
Add a ns-3 simulator on the node used for the experiment
"""
# creat the ns-3 simulator instance
self.simu = self.ec.register_resource("linux::ns3::Simulation")
self.ec.set (self.simu, "StopTime", "200s")
#
# run it in realtime mode if asked
if real_time:
self.ec.set(self.simu, "simulatorImplementationType", "ns3::RealtimeSimulatorImpl")
#
# log additional information
self.ec.set(self.simu, "checksumEnabled", True)
self.ec.set(self.simu, "verbose", True)
self.ec.set(self.simu, "enableDump", True)
self.ec.register_connection(self.simu, self.node)
return self.simu
# == ns-3 simulation helper functions =====================================
def add_nsnode(self):
"""
Create a ns-3 node and add it to the simulator
"""
# create a ns-3 node
nsnode = self.ec.register_resource("ns3::Node")
# enable its network stack
self.ec.set(nsnode, "enableStack", True)
self.ec.register_connection(nsnode, self.simu)
return nsnode
def add_wifi_channel(self):
"""
Create the WiFi channel on which all nodes will be connected
"""
# create a channel
channel = self.ec.register_resource("ns3::YansWifiChannel")
# specify the delay model
delay = self.ec.register_resource("ns3::ConstantSpeedPropagationDelayModel")
self.ec.register_connection(channel, delay)
# specify a loss model
loss = self.ec.register_resource("ns3::LogDistancePropagationLossModel")
self.ec.register_connection(channel, loss)
return channel
def add_wifi_device(self, node, ip, prefix, access_point = False):
"""
Add and configure the WiFi interface on a simulated node
"""
# create the WiFi network interface
dev = self.ec.register_resource("ns3::WifiNetDevice")
# specify the network layer parameters
self.ec.set(dev, "ip", ip)
self.ec.set(dev, "prefix", prefix)
self.ec.register_connection(node, dev)
# specify the MAC layer parameters
#
# can be in access point mode or not
if access_point:
mac = self.ec.register_resource("ns3::ApWifiMac")
else:
mac = self.ec.register_resource("ns3::StaWifiMac")
# the MAC is IEEE 802.11a
self.ec.set(mac, "Standard", "WIFI_PHY_STANDARD_80211a")
self.ec.register_connection(dev, mac)
# specify the physical layer parameters
phy = self.ec.register_resource("ns3::YansWifiPhy")
#
# it physical layer is IEEE802.11a
self.ec.set(phy, "Standard", "WIFI_PHY_STANDARD_80211a")
self.ec.register_connection(dev, phy)
#
# specify an error model for transmissions
error = self.ec.register_resource("ns3::NistErrorRateModel")
self.ec.register_connection(phy, error)
# specify the Wifi manager to be assocated with the interface
manager = self.ec.register_resource("ns3::ArfWifiManager")
self.ec.register_connection(dev, manager)
return dev, phy
def add_random_mobility(self, node, x, y, z, speed, bounds_width, bounds_height):
"""
Create a mobility model for node with random movements
"""
position = "%d:%d:%d" % (x, y, z)
bounds = "0|%d|0|%d" % (bounds_width, bounds_height)
speed = "ns3::UniformRandomVariable[Min=%d|Max=%s]" % (speed, speed)
pause = "ns3::ConstantRandomVariable[Constant=1.0]"
mobility = self.ec.register_resource("ns3::RandomDirection2dMobilityModel")
self.ec.set(mobility, "Position", position)
self.ec.set(mobility, "Bounds", bounds)
self.ec.set(mobility, "Speed", speed)
self.ec.set(mobility, "Pause", pause)
self.ec.register_connection(node, mobility)
return mobility
def add_constant_mobility(self, node, x, y, z):
"""
Create a mobility model for node with a constant position
"""
mobility = self.ec.register_resource("ns3::ConstantPositionMobilityModel")
position = "%d:%d:%d" % (x, y, z)
self.ec.set(mobility, "Position", position)
self.ec.register_connection(node, mobility)
return mobility
def create_simulated_node(self, ip, prefix, channel, access_point, x, y):
"""
Create a simulated node connected on a WiFi channel
"""
# Create the ns node that will run the application
nsnode = self.add_nsnode()
# Add a WiFi interface to the node
dev, phy = self.add_wifi_device(nsnode, ip, prefix, access_point)
#
# Connect the access point to the WiFi network
self.ec.register_connection(channel, phy)
# Specify that the node mobility
#
# access point is not mobile
if access_point:
mobility = self.add_constant_mobility(nsnode, x, y, 0)
# other nodes have random mobility pattern
else:
mobility = self.add_random_mobility(nsnode, x, y, 0, self.speed, self.bounds_width, self.bounds_height)
return nsnode
def add_route(self, nsnode, netblock, prefix, nexthop):
"""
add a route on ns-3 node nsnode for netblock/prefix via nexthop
"""
route = self.ec.register_resource("ns3::Route")
self.ec.set(route, "network", netblock)
self.ec.set(route, "prefix", prefix)
self.ec.set(route, "nexthop", nexthop)
self.ec.register_connection(route, nsnode)
print "route %s/%s via %s added on nsnode %s (%s)" % (netblock, prefix, nexthop, nsnode, self)
return route
def add_vroute(self, dev, netblock, prefix, nexthop):
"""
Add a route on Planetlab node for netblock/prefix via nexthop
"""
route = self.ec.register_resource("planetlab::Vroute")
self.ec.set(route, "network", netblock)
self.ec.set(route, "prefix", prefix)
self.ec.set(route, "nexthop", nexthop)
self.ec.register_connection(route, dev)
print "Vroute %s/%s via %s added on nsnode %s (%s)" % (netblock, prefix, nexthop, dev, self)
return route
def add_agent(self, nsnode):
"""
Add a agent application
"""
# Create a DCE application running the agent code
app = self.ec.register_resource("linux::ns3::dce::Application")
self.ec.set(app, "sources", "code/agent.c")
self.ec.set(app "build", "gcc -fPIC -pie -rdynamic ${SRC}/agent.c -o ${BIN_DCE}/agent")
self.ec.set(app, "binary", "agent")
self.ec.set(app, "arguments", "45005")
self.ec.set(app, "stackSize", 1<<20)
self.ec.set(app, "StartTime", "10s")
self.ec.set(app, "StopTime", "200s")
# Associate the application with the simulated node
self.ec.register_connection(app, nsnode)
# Make the application start only once the simulated node is started
self.ec.register_condition(app, ResourceAction.START,
nsnode, ResourceState.STARTED, time="5s")
return app
def add_transmitter(self, nsnode):
"""
Add a transmitter application
"""
# Create a DCE application running the transmitter code
app = self.ec.register_resource("linux::ns3::dce::Application")
self.ec.set(app, "sources", "code/transmitter.c")
self.ec.set(app, "build", "gcc -fPIC -pie -rdynamic ${SRC}/transmitter.c -o ${BIN_DCE}/transmitter")
self.ec.set(app, "binary", "transmitter")
self.ec.set(app, "arguments", "%s;45005" % target)
self.ec.set(app, "stackSize", 1<<20)
self.ec.set(app, "StartTime", "10s")
self.ec.set(app, "StopTime", "200s")
# Associate the application with the simulated node
self.ec.register_connection(app, nsnode)
#
# Make the application start only once the simulated node and the serer are started
self.ec.register_condition(app, ResourceAction.START,
[nsnode, self.apps[0]], ResourceState.STARTED, time="10s")
return app
def add_planetlab_transmitter(self, target):
"""
Add a planetlab transmitter application
"""
# Create an application running the transmitter code
app = self.ec.register_resource("linux::Application")
self.ec.set(app, "sources", "code/transmitter.c")
self.ec.set(app, "build", "make ${SRC}/transmitter")
self.ec.set(app, "command", "${SRC}/transmitter %s 45005" % target)
# Associate the application with the Planetlab node
self.ec.register_connection(app, self.node)
# Make the application start only once the simulated agent and the node are started
self.ec.register_condition(app, ResourceAction.START,
[self.apps[0], self.node], ResourceState.STARTED, time="10s")
return app
# == Topology construction ================================================
def build_topology(self, netblock, prefix, target):
"""
Builds a topology composed of one fixed access point and nb_nodes
mobile nodes
"""
# Rember network parameters
self.netblock = netblock
self.prefix = prefix
# Create the WiFi network via which nodes are connected
chan = self.add_wifi_channel()
# == Access point
# Geographical position of the access point
x=50
y=0
# the IP address of the access point is the first in the prefix
self.ip_ap = str(ipaddr.IPv4Address(self.netblock) + 1)
print "IP AP: %s " % (self.ip_ap)
# Create the ns node that will run the access point
nsnode = self.create_simulated_node(self.ip_ap, self.prefix, chan, True, x, y)
# add the node in the collection of simulated nodes
self.nsnodes.append(nsnode)
# Run a agent application on the access point
agent = self.add_agent(nsnode)
# add the agent application in the collection of applications
self.apps.append(agent)
# Add nb_nodes mobile nodes in the WiFi network
for i in range(1, self.nb_nodes + 1):
# pic a random initial location
x = randint(0, self.bounds_width)
y = randint(0, self.bounds_height)
# define the appropriate IP address of the node (sequential IP in what remains after the access point IP)
ip = str(ipaddr.IPv4Address(self.ip_ap) + i)
print "IP mobile: " , ip
# Create the ns node that will run the mobile node
nsnode = self.create_simulated_node(ip, self.prefix, chan, False, x, y)
#
# add the node in the collection of simulated nodes
self.nsnodes.append(nsnode)
if target:
# Run a transmitter application on the mobile node
transmitter = self.add_transmitter(nsnode)
# add the trasmitter application in the collection of applications
self.apps.append(transmitter)
# Add a default route via the access point
self.add_route(nsnode, "0.0.0.0", "0", self.ip_ap)
# for sanity check
self.topology_built = True