# Attribute is not modifiable by the user during runtime
ExecReadOnly = 0x02
# Attribute is an access credential
+ # TODO REMOVE!!!
Credential = 0x04
# Attribute is a filter used to discover resources
+ # TODO REMOVE!!!
Filter = 0x08
class Attribute(object):
--- /dev/null
+#
+# 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 <http://www.gnu.org/licenses/>.
+#
+# Author: Alina Quereilhac <alina.quereilhac@inria.fr>
+
+from nepi.execution.attribute import Attribute, Flags, Types
+from nepi.execution.trace import Trace, TraceAttr
+from nepi.execution.resource import ResourceManager, clsinit_copy, \
+ ResourceState, reschedule_delay
+from nepi.resources.linux.application import LinuxApplication
+from nepi.resources.linux.node import OSType
+from nepi.util.timefuncs import tnow, tdiffsec
+from nepi.resources.ns3.simulator import NS3Simulator
+
+import os
+
+@clsinit_copy
+class LinuxNS3Simulator(LinuxApplication, NS3Simulator):
+ _rtype = "LinuxSimulator"
+
+ @classmethod
+ def _register_attributes(cls):
+ max_rte = Attribute("maxRteMicrosec",
+ "Sets the CCND_MAX_RTE_MICROSEC environmental variable. ",
+ flags = Flags.ExecReadOnly)
+
+ keystore = Attribute("keyStoreDirectory",
+ "Sets the CCND_KEYSTORE_DIRECTORY environmental variable. ",
+ flags = Flags.ExecReadOnly)
+
+ cls._register_attribute(debug)
+ cls._register_attribute(port)
+
+ @classmethod
+ def _register_traces(cls):
+ log = Trace("log", "CCND log output")
+ status = Trace("status", "ccndstatus output")
+
+ cls._register_trace(log)
+ cls._register_trace(status)
+
+ def __init__(self, ec, guid):
+ super(LinuxCCND, self).__init__(ec, guid)
+ self._home = "ccnd-%s" % self.guid
+ self._version = "ccnx"
+
+ @property
+ def version(self):
+ return self._version
+
+ @property
+ def path(self):
+ return "PATH=$PATH:${BIN}/%s/" % self.version
+
+ def do_deploy(self):
+ if not self.node or self.node.state < ResourceState.READY:
+ self.debug("---- RESCHEDULING DEPLOY ---- node state %s " % self.node.state )
+
+ # ccnd needs to wait until node is deployed and running
+ self.ec.schedule(reschedule_delay, self.deploy)
+ else:
+ if not self.get("command"):
+ self.set("command", self._start_command)
+
+ if not self.get("depends"):
+ self.set("depends", self._dependencies)
+
+ if not self.get("sources"):
+ self.set("sources", self._sources)
+
+ sources = self.get("sources")
+ source = sources.split(" ")[0]
+ basename = os.path.basename(source)
+ self._version = ( basename.strip().replace(".tar.gz", "")
+ .replace(".tar","")
+ .replace(".gz","")
+ .replace(".zip","") )
+
+ if not self.get("build"):
+ self.set("build", self._build)
+
+ if not self.get("install"):
+ self.set("install", self._install)
+
+ if not self.get("env"):
+ self.set("env", self._environment)
+
+ command = self.get("command")
+
+ self.info("Deploying command '%s' " % command)
+
+ self.do_discover()
+ self.do_provision()
+
+ self.debug("----- READY ---- ")
+ self.set_ready()
+
+ def upload_start_command(self):
+ 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 = False,
+ env = env,
+ raise_on_error = True)
+
+ 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
+
+ def do_stop(self):
+ command = self.get('command') or ''
+
+ if self.state == ResourceState.STARTED:
+ self.info("Stopping command '%s'" % command)
+
+ command = "ccndstop"
+ env = self.get("env")
+
+ # replace application specific paths in the command
+ command = self.replace_paths(command)
+ env = env and self.replace_paths(env)
+
+ # Upload the command to a file, and execute asynchronously
+ shfile = os.path.join(self.app_home, "stop.sh")
+ self.node.run_and_wait(command, self.run_home,
+ shfile = shfile,
+ overwrite = False,
+ env = env,
+ pidfile = "ccndstop_pidfile",
+ ecodefile = "ccndstop_exitcode",
+ stdout = "ccndstop_stdout",
+ stderr = "ccndstop_stderr")
+
+ self.set_stopped()
+
+ @property
+ def state(self):
+ # First check if the ccnd has failed
+ state_check_delay = 0.5
+ if self._state == ResourceState.STARTED and \
+ tdiffsec(tnow(), self._last_state_check) > state_check_delay:
+ (out, err), proc = self._ccndstatus()
+
+ retcode = proc.poll()
+
+ if retcode == 1 and err.find("No such file or directory") > -1:
+ # ccnd is not running (socket not found)
+ self.set_stopped()
+ elif retcode:
+ # other errors ...
+ msg = " Failed to execute command '%s'" % self.get("command")
+ self.error(msg, out, err)
+ self.fail()
+
+ self._last_state_check = tnow()
+
+ return self._state
+
+ def _ccndstatus(self):
+ env = self.get('env') or ""
+ environ = self.node.format_environment(env, inline = True)
+ command = environ + " ccndstatus"
+ command = self.replace_paths(command)
+
+ return self.node.execute(command)
+
+ @property
+ def _start_command(self):
+ return "ccndstart"
+
+ @property
+ def _dependencies(self):
+ if self.node.use_rpm:
+ return ( " autoconf openssl-devel expat-devel libpcap-devel "
+ " ecryptfs-utils-devel libxml2-devel automake gawk "
+ " gcc gcc-c++ git pcre-devel make ")
+ elif self.node.use_deb:
+ return ( " autoconf libssl-dev libexpat-dev libpcap-dev "
+ " libecryptfs0 libxml2-utils automake gawk gcc g++ "
+ " git-core pkg-config libpcre3-dev make ")
+ return ""
+
+ @property
+ def _sources(self):
+ return "http://www.ccnx.org/releases/ccnx-0.7.2.tar.gz"
+
+ @property
+ def _build(self):
+ sources = self.get("sources").split(" ")[0]
+ sources = os.path.basename(sources)
+
+ return (
+ # Evaluate if ccnx binaries are already installed
+ " ( "
+ " test -f ${BIN}/%(version)s/ccnd && "
+ " echo 'binaries found, nothing to do' "
+ " ) || ( "
+ # If not, untar and build
+ " ( "
+ " mkdir -p ${SRC}/%(version)s && "
+ " tar xf ${SRC}/%(sources)s --strip-components=1 -C ${SRC}/%(version)s "
+ " ) && "
+ "cd ${SRC}/%(version)s && "
+ # Just execute and silence warnings...
+ " ( ./configure && make ) "
+ " )") % ({ 'sources': sources,
+ 'version': self.version
+ })
+
+ @property
+ def _install(self):
+ return (
+ # Evaluate if ccnx binaries are already installed
+ " ( "
+ " test -f ${BIN}/%(version)s/ccnd && "
+ " echo 'binaries found, nothing to do' "
+ " ) || ( "
+ # If not, install
+ " mkdir -p ${BIN}/%(version)s && "
+ " mv ${SRC}/%(version)s/bin/* ${BIN}/%(version)s/ "
+ " )"
+ ) % ({ 'version': self.version
+ })
+
+ @property
+ def _environment(self):
+ envs = dict({
+ "debug": "CCND_DEBUG",
+ "port": "CCN_LOCAL_PORT",
+ "sockname" : "CCN_LOCAL_SOCKNAME",
+ "capacity" : "CCND_CAP",
+ "mtu" : "CCND_MTU",
+ "dataPauseMicrosec" : "CCND_DATA_PAUSE_MICROSEC",
+ "defaultTimeToStale" : "CCND_DEFAULT_TIME_TO_STALE",
+ "maxTimeToStale" : "CCND_MAX_TIME_TO_STALE",
+ "maxRteMicrosec" : "CCND_MAX_RTE_MICROSEC",
+ "keyStoreDirectory" : "CCND_KEYSTORE_DIRECTORY",
+ "listenOn" : "CCND_LISTEN_ON",
+ "autoreg" : "CCND_AUTOREG",
+ "prefix" : "CCND_PREFIX",
+ })
+
+ env = self.path
+ env += " ".join(map(lambda k: "%s=%s" % (envs.get(k), str(self.get(k))) \
+ if self.get(k) else "", envs.keys()))
+
+ return env
+
+ def valid_connection(self, guid):
+ # TODO: Validate!
+ return True
+
--- /dev/null
+#
+# 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 <http://www.gnu.org/licenses/>.
+#
+# Author: Alina Quereilhac <alina.quereilhac@inria.fr>
+
+from nepi.execution.resource import ResourceManager, clsinit_copy, \
+ ResourceState, reschedule_delay
+
+from nepi.resources.ns3.simulator import NS3Simulator
+
+@clsinit_copy
+class NS3Base(ResourceManager):
+ _rtype = "NS3Base"
+
+ @property
+ def simulator(self):
+ simulator = self.get_connected(NS3Simulator.get_rtype())
+ if simulator: return simulator[0]
+ return None
+
+ def do_deploy(self):
+ if not self.simulator or self.simulator.state < ResourceState.READY:
+ self.debug("---- RESCHEDULING DEPLOY ---- node state %s " % self.simulator.state )
+
+ # ccnd needs to wait until node is deployed and running
+ self.ec.schedule(reschedule_delay, self.deploy)
+ else:
+ # TODO: CREATE AND CONFIGURE NS-3 C++ OBJECT
+ self.do_discover()
+ self.do_provision()
+
+ self.debug("----- READY ---- ")
+ self.set_ready()
+
+ def do_start(self):
+ if self.state == ResourceState.READY:
+ ## TODO!!!
+ self.info("Starting ...")
+
+ self.set_started()
+ else:
+ msg = " Failed "
+ self.error(msg, out, err)
+ raise RuntimeError, msg
+
+ def do_stop(self):
+ if self.state == ResourceState.STARTED:
+ self.info("Stopping command '%s'" % command)
+ ## TODO!!!
+
+ self.set_stopped()
+
+ @property
+ def state(self):
+ # First check if the ccnd has failed
+ # TODO!!!
+ return self._state
+
SINGLETON = "singleton::"
+def load_ns3_module():
+ import ctypes
+ import imp
+ import re
+ import pkgutil
+
+ bindings = os.environ.get("NS3BINDINGS")
+ libdir = os.environ.get("NS3LIBRARIES")
+
+ # Load the ns-3 modules shared libraries
+ if libdir:
+ files = os.listdir(libdir)
+ regex = re.compile("(.*\.so)$")
+ libs = [m.group(1) for filename in files for m in [regex.search(filename)] if m]
+
+ libscp = list(libs)
+ while len(libs) > 0:
+ for lib in libs:
+ libfile = os.path.join(libdir, lib)
+ try:
+ ctypes.CDLL(libfile, ctypes.RTLD_GLOBAL)
+ libs.remove(lib)
+ except:
+ pass
+
+ # if did not load any libraries in the last iteration break
+ # to prevent infinit loop
+ if len(libscp) == len(libs):
+ raise RuntimeError("Imposible to load shared libraries %s" % str(libs))
+ libscp = list(libs)
+
+ # import the python bindings for the ns-3 modules
+ if bindings:
+ sys.path.append(bindings)
+
+ # create a module to add all ns3 classes
+ ns3mod = imp.new_module("ns3")
+ sys.modules["ns3"] = ns3mod
+
+ # retrieve all ns3 classes and add them to the ns3 module
+ import ns
+
+ for importer, modname, ispkg in pkgutil.iter_modules(ns.__path__):
+ fullmodname = "ns.%s" % modname
+ module = __import__(fullmodname, globals(), locals(), ['*'])
+
+ for sattr in dir(module):
+ if sattr.startswith("_"):
+ continue
+
+ attr = getattr(module, sattr)
+
+ # netanim.Config and lte.Config singleton overrides ns3::Config
+ if sattr == "Config" and modname in ['netanim', 'lte']:
+ sattr = "%s.%s" % (modname, sattr)
+
+ setattr(ns3mod, sattr, attr)
+
+ return ns3mod
+
class NS3Wrapper(object):
def __init__(self, homedir = None):
super(NS3Wrapper, self).__init__()
# Python module to refernce all ns-3 classes and types
self._ns3 = None
-
- # Load ns-3 shared libraries and import modules
- self._load_ns3_module()
@property
def ns3(self):
+ if not self._ns3:
+ self._ns3 = load_ns3_module()
+
return self._ns3
@property
str(arg).startswith(SINGLETON) else arg for arg in realargs]
return realargs
-
- def _load_ns3_module(self):
- if self.ns3:
- return
-
- import ctypes
- import imp
- import re
- import pkgutil
-
- bindings = os.environ.get("NS3BINDINGS")
- libdir = os.environ.get("NS3LIBRARIES")
-
- # Load the ns-3 modules shared libraries
- if libdir:
- files = os.listdir(libdir)
- regex = re.compile("(.*\.so)$")
- libs = [m.group(1) for filename in files for m in [regex.search(filename)] if m]
-
- libscp = list(libs)
- while len(libs) > 0:
- for lib in libs:
- libfile = os.path.join(libdir, lib)
- try:
- ctypes.CDLL(libfile, ctypes.RTLD_GLOBAL)
- libs.remove(lib)
- except:
- pass
-
- # if did not load any libraries in the last iteration break
- # to prevent infinit loop
- if len(libscp) == len(libs):
- raise RuntimeError("Imposible to load shared libraries %s" % str(libs))
- libscp = list(libs)
-
- # import the python bindings for the ns-3 modules
- if bindings:
- sys.path.append(bindings)
-
- # create a module to add all ns3 classes
- ns3mod = imp.new_module("ns3")
- sys.modules["ns3"] = ns3mod
-
- # retrieve all ns3 classes and add them to the ns3 module
- import ns
-
- for importer, modname, ispkg in pkgutil.iter_modules(ns.__path__):
- fullmodname = "ns.%s" % modname
- module = __import__(fullmodname, globals(), locals(), ['*'])
-
- for sattr in dir(module):
- if sattr.startswith("_"):
- continue
-
- attr = getattr(module, sattr)
-
- # netanim.Config and lte.Config singleton overrides ns3::Config
- if sattr == "Config" and modname in ['netanim', 'lte']:
- sattr = "%s.%s" % (modname, sattr)
-
- setattr(ns3mod, sattr, attr)
-
- self._ns3 = ns3mod
--- /dev/null
+#
+# 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 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 <http://www.gnu.org/licenses/>.
+#
+# Author: Alina Quereilhac <alina.quereilhac@inria.fr>
+
+from nepi.resources.ns3.ns3wrapper import load_ns3_module
+
+import os
+import re
+
+def dump_ns3_rms():
+ ns3 = load_ns3_module()
+
+ type_id = ns3.TypeId()
+
+ tid_count = type_id.GetRegisteredN()
+ base = type_id.LookupByName("ns3::Object")
+
+ # Create a .py file using the ns-3 RM template for each ns-3 TypeId
+ for i in xrange(tid_count):
+ tid = type_id.GetRegistered(i)
+
+ if tid.MustHideFromDocumentation() or \
+ not tid.HasConstructor() or \
+ not tid.IsChildOf(base):
+ continue
+
+ attributes = template_attributes(ns3, tid)
+ traces = template_traces(ns3, tid)
+ ptid = tid
+ while ptid.HasParent():
+ ptid = ptid.GetParent()
+ attributes += template_attributes(ns3, ptid)
+ traces += template_traces(ns3, ptid)
+
+ attributes = "\n" + attributes if attributes else "pass"
+ traces = "\n" + traces if traces else "pass"
+
+ rtype = tid.GetName()
+ category = tid.GetGroupName()
+ classname = rtype.replace("ns3::", "NS3").replace("::","")
+ uncamm_rtype = re.sub('([a-z])([A-Z])', r'\1-\2', rtype).lower()
+ short_rtype = uncamm_rtype.replace("::","-")
+
+ d = os.path.dirname(os.path.realpath(__file__))
+ ftemp = open(os.path.join(d, "templates", "rm_template.txt"), "r")
+ template = ftemp.read()
+ ftemp.close()
+
+ template = template. \
+ replace("<CLASS_NAME>", classname). \
+ replace("<RTYPE>", rtype). \
+ replace("<ATTRIBUTES>", attributes). \
+ replace("<TRACES>", traces). \
+ replace("<SHORT-RTYPE>", short_rtype)
+
+ fname = uncamm_rtype.replace('ns3::', ''). \
+ replace('::', ''). \
+ replace("-","_").lower() + ".py"
+
+ f = open(os.path.join(d, fname), "w")
+ print os.path.join(d, fname)
+ print template
+ #f.write(template)
+ f.close()
+
+def template_attributes(ns3, tid):
+ d = os.path.dirname(os.path.realpath(__file__))
+ ftemp = open(os.path.join(d, "templates", "attribute_template.txt"), "r")
+ template = ftemp.read()
+ ftemp.close()
+
+ attributes = ""
+
+ attr_count = tid.GetAttributeN()
+ for i in xrange(attr_count):
+ attr_info = tid.GetAttribute(i)
+ if not attr_info.accessor.HasGetter():
+ continue
+
+ attr_flags = "None"
+ flags = attr_info.flags
+ if (flags & ns3.TypeId.ATTR_SET) != ns3.TypeId.ATTR_SET:
+ attr_flags = "Types.ExecReadOnly"
+
+ attr_name = attr_info.name
+ checker = attr_info.checker
+ attr_help = attr_info.help
+ value = attr_info.initialValue
+ attr_value = value.SerializeToString(checker)
+ attr_allowed = "None"
+ attr_range = "None"
+ attr_type = "Types.STRING"
+
+ if isinstance(value, ns3.ObjectVectorValue):
+ continue
+ elif isinstance(value, ns3.PointerValue):
+ continue
+ elif isinstance(value, ns3.WaypointValue):
+ continue
+ elif isinstance(value, ns3.BooleanValue):
+ attr_type = "Types.BOOL"
+ attr_value = "True" if attr_value == "true" else "False"
+ elif isinstance(value, ns3.EnumValue):
+ attr_type = "Types.ENUM"
+ attr_allowed = "[%s]"% checker.GetUnderlyingTypeInformation().replace("|", ",")
+ elif isinstance(value, ns3.DoubleValue):
+ attr_type = "Types.DOUBLE"
+ # TODO: range
+ elif isinstance(value, ns3.UintegerValue):
+ attr_type = "Types.INTEGER"
+ # TODO: range
+
+ attr_id = attr_name.lower()
+ attributes += template.replace("<ATTR_ID>", attr_id) \
+ .replace("<ATTR_NAME>", attr_name) \
+ .replace("<ATTR_HELP>", attr_help) \
+ .replace("<ATTR_TYPE>", attr_type) \
+ .replace("<ATTR_DEFAULT>", attr_value) \
+ .replace("<ATTR_ALLOWED>", attr_allowed) \
+ .replace("<ATTR_RANGE>", attr_range) \
+ .replace("<ATTR_FLAGS>", attr_flags)
+
+ return attributes
+
+def template_traces(ns3, tid):
+ d = os.path.dirname(os.path.realpath(__file__))
+ ftemp = open(os.path.join(d, "templates", "trace_template.txt"), "r")
+ template = ftemp.read()
+ ftemp.close()
+
+ traces = ""
+
+ trace_count = tid.GetTraceSourceN()
+ for i in xrange(trace_count):
+ trace_info = tid.GetTraceSource(i)
+ trace_name = trace_info.name
+ trace_help = trace_info.help
+
+ trace_id = trace_name.lower()
+ traces += template.replace("<TRACE_ID>", trace_id) \
+ .replace("<TRACE_NAME>", trace_name) \
+ .replace("<TRACE_HELP>", trace_help)
+
+ return traces
+
+if __name__ == "__main__":
+ dump_ns3_rms()
--- /dev/null
+ <ATTR_ID> = Attribute("<ATTR_NAME>",
+ "<ATTR_HELP>",
+ type = <ATTR_TYPE>,
+ default = <ATTR_DEFAULT>,
+ allowed = <ATTR_ALLOWED>,
+ range = <ATTR_RANGE>,
+ flags = <ATTR_FLAGS>)
+
+ cls._register_attribute(<ATTR_ID>)
+
--- /dev/null
+#
+# 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 <http://www.gnu.org/licenses/>.
+#
+
+from nepi.execution.attribute import Attribute, Flags, Types
+from nepi.execution.trace import Trace, TraceAttr
+from nepi.execution.resource import ResourceManager, clsinit_copy, \
+ ResourceState, reschedule_delay
+from nepi.resources.ns3.ns3_base import NS3Base
+
+@clsinit_copy
+class <CLASS_NAME>(NS3Base):
+ _rtype = "<RTYPE>"
+
+ @classmethod
+ def _register_attributes(cls):
+ <ATTRIBUTES>
+
+ @classmethod
+ def _register_traces(cls):
+ <TRACES>
+
+ def __init__(self, ec, guid):
+ super(<CLASS_NAME>, self).__init__(ec, guid)
+ self._home = "<SHORT-RTYPE>-%s" % self.guid
+ # TODO!
+ self._version = None
+
+ @property
+ def version(self):
+ return self._version
+
--- /dev/null
+ <TRACE_ID> = Trace("<TRACE_NAME>", "<TRACE_HELP>")
+
+ cls._register_trace(<TRACE_ID>)
+