# # 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