# # NEPI, a framework to manage network experiments # Copyright (C) 2014 INRIA # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # 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: Alina Quereilhac from nepi.execution.attribute import Attribute, Flags, Types from nepi.execution.resource import ResourceManager, ResourceState, \ clsinit_copy import os import socket import struct import fcntl @clsinit_copy class PlanetlabTunTapFdLink(ResourceManager): """ Interconnects a TAP or TUN Linux device to a FdNetDevice """ _rtype = "planetlab::ns3::TunTapFdLink" def __init__(self, ec, guid): super(PlanetlabTunTapFdLink, self).__init__(ec, guid) self._tap = None self._fdnetdevice = None self._fd_sock_address = None @property def fdnetdevice(self): if not self._fdnetdevice: from nepi.resources.ns3.ns3fdnetdevice import NS3BaseFdNetDevice devices = self.get_connected(NS3BaseFdNetDevice.get_rtype()) if not devices or len(devices) != 1: msg = "TapFdLink must be connected to exactly one FdNetDevices" self.error(msg) raise RuntimeError, msg self._fdnetdevice = devices[0] return self._fdnetdevice @property def tap(self): if not self._tap: from nepi.resources.planetlab.tap import PlanetlabTap devices = self.get_connected(PlanetlabTap.get_rtype()) if not devices or len(devices) != 1: msg = "TapFdLink must be connected to exactly one PlanetlabTap" self.error(msg) raise RuntimeError, msg self._tap = devices[0] return self._tap @property def fd_sock_address(self): return self._fd_sock_address @property def node(self): return self.tap.node def upload_sources(self): scripts = [] # vif-passfd python script pl_vif_passfd = os.path.join(os.path.dirname(__file__), "scripts", "pl-vif-passfd.py") scripts.append(pl_vif_passfd) # Upload scripts scripts = ";".join(scripts) self.node.upload(scripts, os.path.join(self.node.src_dir), overwrite = False) def upload_start_command(self): if self.tap.node.get("hostname") != \ self.fdnetdevice.node.get("hostname"): msg = "Tap and FdNetDevice are not in the same host" self.error(msg) raise RuntimeError, msg self._fd_sock_address = self.fdnetdevice.recv_fd() self.set("command", self._start_command) command = self.get("command") env = self.get("env") # We want to make sure the ccnd is running # before the experiment starts. # Run the command as a bash script in background, # in the host ( but wait until the command has # finished to continue ) env = self.replace_paths(env) command = self.replace_paths(command) shfile = os.path.join(self.app_home, "start.sh") self.node.run_and_wait(command, self.run_home, shfile = shfile, overwrite = True) def do_deploy(self): if self.tap.state < ResourceState.READY or \ self.fdnetdevice.state < ResourceState.READY: self.ec.schedule(self.reschedule_delay, self.deploy) else: self.do_discover() self.do_provision() super(PlanetlabTunTapFdLink, self).do_deploy() def do_start(self): if self.state == ResourceState.READY: command = self.get("command") self.info("Starting command '%s'" % command) self.set_started() else: msg = " Failed to execute command '%s'" % command self.error(msg, out, err) raise RuntimeError, msg @property def _start_command(self): command = [] # Use pl-vif-passfd.py to send fd from TAP to FdNetDevice command.append("sudo -S") command.append("PYTHONPATH=$PYTHONPATH:${SRC}") command.append("python ${SRC}/pl-vif-passfd") command.append("-a %s" % self.fd_sock_address) command.append("-S %s " % self.tap.sock_name) command = " ".join(command) command = self.replace_paths(command) return command