From 561719f7cb8a42f139905c33e75ec5071c321170 Mon Sep 17 00:00:00 2001 From: Alina Quereilhac Date: Fri, 1 Aug 2014 09:45:27 +0200 Subject: [PATCH] Added serialization to EC --- setup.py | 4 +- src/nepi/design/__init__.py | 1 - src/nepi/design/box.py | 121 ------------ src/nepi/execution/attribute.py | 1 + src/nepi/execution/ec.py | 50 ++++- src/nepi/execution/resource.py | 2 +- src/nepi/util/parser.py | 165 ----------------- src/nepi/util/parsers/__init__.py | 0 src/nepi/util/parsers/xml_parser.py | 274 ++++++++++++++++++++++++++++ src/nepi/util/serializer.py | 61 +++++++ test/design/box.py | 69 ------- test/util/parser.py | 57 ------ test/util/serializer.py | 143 +++++++++++++++ 13 files changed, 528 insertions(+), 420 deletions(-) delete mode 100644 src/nepi/design/__init__.py delete mode 100644 src/nepi/design/box.py delete mode 100644 src/nepi/util/parser.py create mode 100644 src/nepi/util/parsers/__init__.py create mode 100644 src/nepi/util/parsers/xml_parser.py create mode 100644 src/nepi/util/serializer.py delete mode 100755 test/design/box.py delete mode 100755 test/util/parser.py create mode 100755 test/util/serializer.py diff --git a/setup.py b/setup.py index 00001d8b..c8247ec2 100755 --- a/setup.py +++ b/setup.py @@ -13,7 +13,6 @@ setup( platforms = "Linux, OSX", packages = [ "nepi", - "nepi.design", "nepi.execution", "nepi.resources", "nepi.resources.all", @@ -27,7 +26,8 @@ setup( "nepi.resources.omf", "nepi.resources.planetlab", "nepi.resources.planetlab.openvswitch", - "nepi.util"], + "nepi.util", + "nepi.util.parsers"], package_dir = {"": "src"}, package_data = { "nepi.resources.planetlab" : [ "scripts/*.py" ], diff --git a/src/nepi/design/__init__.py b/src/nepi/design/__init__.py deleted file mode 100644 index 013e4b7e..00000000 --- a/src/nepi/design/__init__.py +++ /dev/null @@ -1 +0,0 @@ -#!/usr/bin/python diff --git a/src/nepi/design/box.py b/src/nepi/design/box.py deleted file mode 100644 index 2da0710a..00000000 --- a/src/nepi/design/box.py +++ /dev/null @@ -1,121 +0,0 @@ -# -# 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 . -# -# -# Author: Alina Quereilhac - -from nepi.util import guid - -guid_gen = guid.GuidGenerator() - -class Attributes(object): - def __init__(self): - super(Attributes, self).__init__() - self._attributes = dict() - - def __getattr__(self, name): - try: - return self._attributes[name] - except: - return super(Attributes, self).__getattribute__(name) - - def __setattr__(self, name, value): - try: - if value == None: - old = self._attributes[name] - del self._attributes[name] - return old - - self._attributes[name] = value - return value - except: - return super(Attributes, self).__setattr__(name, value) - -class Connections(object): - def __init__(self): - super(Connections, self).__init__() - self._connections = set() - - def __getattr__(self, guid_or_label): - try: - for b in self._connections: - if guid_or_label in [b.guid, b.label]: - return b - except: - return super(Connections, self).__getattribute__(guid_or_label) - -class Box(object): - def __init__(self, label = None, guid = None): - super(Box, self).__init__() - self._guid = guid_gen.next(guid) - self._a = Attributes() - self._c = Connections() - self._tags = set() - self.label = label or self._guid - - # Graphical information to draw box - self.x = 0 - self.y = 0 - self.width = 4 - self.height = 4 - - @property - def tags(self): - return self._tags - - @property - def attributes(self): - return self._a._attributes.keys() - - @property - def a(self): - return self._a - - @property - def c(self): - return self._c - - @property - def guid(self): - return self._guid - - @property - def connections(self): - return set(self._c._connections) - - def tadd(self, name): - self._tags.add(name) - - def tdel(self, name): - self._tags.remove(name) - - def thas(self, name): - return name in self._tags - - def connect(self, box, cascade = True): - self._c._connections.add(box) - if cascade: - box.connect(self, cascade = False) - - def disconnect(self, box, cascade = True): - self._c._connections.remove(box) - if cascade: - box.disconnect(self, cascade = False) - - def is_connected(self, box): - return box in self.connections - diff --git a/src/nepi/execution/attribute.py b/src/nepi/execution/attribute.py index fa2b1043..19fbfc2c 100644 --- a/src/nepi/execution/attribute.py +++ b/src/nepi/execution/attribute.py @@ -190,6 +190,7 @@ class Attribute(object): adequate validation""" return True + @property def has_changed(self): """ Returns true if the value has changed from the default """ return self.value != self.default diff --git a/src/nepi/execution/ec.py b/src/nepi/execution/ec.py index 257742ee..cf419838 100644 --- a/src/nepi/execution/ec.py +++ b/src/nepi/execution/ec.py @@ -24,6 +24,7 @@ from nepi.execution.resource import ResourceFactory, ResourceAction, \ ResourceState, ResourceState2str from nepi.execution.scheduler import HeapScheduler, Task, TaskStatus from nepi.execution.trace import TraceAttr +from nepi.util.serializer import ECSerializer, SFormats # TODO: use multiprocessing instead of threading # TODO: Allow to reconnect to a running experiment instance! (reconnect mode vs deploy mode) @@ -151,6 +152,12 @@ class ExperimentController(object): """ + @classmethod + def load(cls, path, format = SFormats.XML): + serializer = ECSerializer() + ec = serializer.load(path) + return ec + def __init__(self, exp_id = None): super(ExperimentController, self).__init__() @@ -200,8 +207,8 @@ class ExperimentController(object): # The runner is a pool of threads used to parallelize # execution of tasks - nthreads = int(os.environ.get("NEPI_NTHREADS", "20")) - self._runner = ParallelRun(maxthreads = nthreads) + self._nthreads = 20 + self._runner = None # Event processing thread self._cond = threading.Condition() @@ -246,6 +253,14 @@ class ExperimentController(object): """ return self._run_id + @property + def nthreads(self): + """ Returns the number of processing nthreads used + + """ + return self._nthreads + + @property def abort(self): """ Returns True if the experiment has failed and should be interrupted, @@ -365,7 +380,17 @@ class ExperimentController(object): guids.append(guid) time.sleep(0.5) - + + def serialize(self, format = SFormats.XML): + serializer = ECSerializer() + sec = serializer.load(self, format = format) + return sec + + def save(self, path, format = SFormats.XML): + serializer = ECSerializer() + path = serializer.save(self, path, format = format) + return path + def get_task(self, tid): """ Returns a task by its id @@ -380,7 +405,7 @@ class ExperimentController(object): def get_resource(self, guid): """ Returns a registered ResourceManager by its guid - :param guid: Id of the task + :param guid: Id of the resource :type guid: int :rtype: ResourceManager @@ -389,6 +414,21 @@ class ExperimentController(object): rm = self._resources.get(guid) return rm + def get_resources_by_type(self, rtype): + """ Returns a registered ResourceManager by its guid + + :param rtype: Resource type + :type rtype: string + + :rtype: list of ResourceManagers + + """ + rms = [] + for guid, rm in self._resources.iteritems(): + if rm.get_rtype() == type: + rms.append(rm) + return rms + def remove_resource(self, guid): del self._resources[guid] @@ -1000,6 +1040,8 @@ class ExperimentController(object): """ + self._nthreads = int(os.environ.get("NEPI_NTHREADS", str(self._nthreads))) + self._runner = ParallelRun(maxthreads = self.nthreads) self._runner.start() while not self._stop: diff --git a/src/nepi/execution/resource.py b/src/nepi/execution/resource.py index 5ec0be38..49a690e4 100644 --- a/src/nepi/execution/resource.py +++ b/src/nepi/execution/resource.py @@ -615,7 +615,7 @@ class ResourceManager(Logger): :rtype: str """ attr = self._attrs[name] - return attr.has_changed() + return attr.has_changed def has_flag(self, name, flag): """ Returns true if the attribute has the flag 'flag' diff --git a/src/nepi/util/parser.py b/src/nepi/util/parser.py deleted file mode 100644 index 58cb79b9..00000000 --- a/src/nepi/util/parser.py +++ /dev/null @@ -1,165 +0,0 @@ -# -# 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 . -# -# Author: Alina Quereilhac - -from nepi.design.box import Box - -from xml.dom import minidom -import sys - -STRING = "string" -BOOL = "bool" -INTEGER = "integer" -DOUBLE = "float" - -def xmlencode(s): - if isinstance(s, str): - rv = s.decode("latin1") - elif not isinstance(s, unicode): - rv = unicode(s) - else: - rv = s - return rv.replace(u'\x00',u'�') - -def xmldecode(s): - return s.replace(u'�',u'\x00').encode("utf8") - -def from_type(value): - if isinstance(value, str): - return STRING - if isinstance(value, bool): - return BOOL - if isinstance(value, int): - return INTEGER - if isinstance(value, float): - return DOUBLE - -def to_type(type, value): - if type == STRING: - return str(value) - if type == BOOL: - return value == "True" - if type == INTEGER: - return int(value) - if type == DOUBLE: - return float(value) - -class XMLParser(object): - def to_xml(self, box): - doc = minidom.Document() - - root = doc.createElement("boxes") - doc.appendChild(root) - - traversed = dict() - self._traverse_boxes(doc, traversed, box) - - # Keep the order - for guid in sorted(traversed.keys()): - bnode = traversed[guid] - root.appendChild(bnode) - - try: - xml = doc.toprettyxml(indent=" ", encoding="UTF-8") - except: - print >>sys.stderr, "Oops: generating XML from %s" % (data,) - raise - - return xml - - def _traverse_boxes(self, doc, traversed, box): - bnode = doc.createElement("box") - bnode.setAttribute("guid", xmlencode(box.guid)) - bnode.setAttribute("label", xmlencode(box.label)) - bnode.setAttribute("x", xmlencode(box.x)) - bnode.setAttribute("y", xmlencode(box.y)) - bnode.setAttribute("width", xmlencode(box.width)) - bnode.setAttribute("height", xmlencode(box.height)) - - traversed[box.guid] = bnode - - anode = doc.createElement("attributes") - bnode.appendChild(anode) - for name in sorted(box.attributes): - value = getattr(box.a, name) - aanode = doc.createElement("attribute") - anode.appendChild(aanode) - aanode.setAttribute("name", xmlencode(name)) - aanode.setAttribute("value", xmlencode(value)) - aanode.setAttribute("type", from_type(value)) - - tnode = doc.createElement("tags") - bnode.appendChild(tnode) - for tag in sorted(box.tags): - ttnode = doc.createElement("tag") - tnode.appendChild(ttnode) - ttnode.setAttribute("name", xmlencode(tag)) - - cnode = doc.createElement("connections") - bnode.appendChild(cnode) - for b in sorted(box.connections): - ccnode = doc.createElement("connection") - cnode.appendChild(ccnode) - ccnode.setAttribute("guid", xmlencode(b.guid)) - if b.guid not in traversed: - self._traverse_boxes(doc, traversed, b) - - def from_xml(self, xml): - doc = minidom.parseString(xml) - bnode_list = doc.getElementsByTagName("box") - - boxes = dict() - connections = dict() - - for bnode in bnode_list: - if bnode.nodeType == doc.ELEMENT_NODE: - guid = int(bnode.getAttribute("guid")) - label = xmldecode(bnode.getAttribute("label")) - x = float(bnode.getAttribute("x")) - y = float(bnode.getAttribute("y")) - height = float(bnode.getAttribute("height")) - width = float(bnode.getAttribute("width")) - box = Box(label=label, guid=guid) - boxes[guid] = box - - anode_list = bnode.getElementsByTagName("attribute") - for anode in anode_list: - name = xmldecode(anode.getAttribute("name")) - value = xmldecode(anode.getAttribute("value")) - type = xmldecode(anode.getAttribute("type")) - value = to_type(type, value) - setattr(box.a, name, value) - - tnode_list = bnode.getElementsByTagName("tag") - for tnode in tnode_list: - value = xmldecode(tnode.getAttribute("name")) - box.tadd(value) - - connections[box] = set() - cnode_list = bnode.getElementsByTagName("connection") - for cnode in cnode_list: - guid = int(cnode.getAttribute("guid")) - connections[box].add(guid) - - for box, conns in connections.iteritems(): - for guid in conns: - b = boxes[guid] - box.connect(b) - - return box - diff --git a/src/nepi/util/parsers/__init__.py b/src/nepi/util/parsers/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/src/nepi/util/parsers/xml_parser.py b/src/nepi/util/parsers/xml_parser.py new file mode 100644 index 00000000..7dc00c0a --- /dev/null +++ b/src/nepi/util/parsers/xml_parser.py @@ -0,0 +1,274 @@ +# +# 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 . +# +# Author: Alina Quereilhac + +from xml.dom import minidom + +import sys +import os + +STRING = "string" +BOOL = "bool" +INTEGER = "integer" +DOUBLE = "float" + +def xmlencode(s): + if isinstance(s, str): + rv = s.decode("latin1") + elif not isinstance(s, unicode): + rv = unicode(s) + else: + rv = s + return rv.replace(u'\x00',u'�') + +def xmldecode(s): + return s.replace(u'�',u'\x00').encode("utf8") + +def from_type(value): + if value == None: + return str("None") + + if isinstance(value, str): + return STRING + if isinstance(value, bool): + return BOOL + if isinstance(value, int): + return INTEGER + if isinstance(value, float): + return DOUBLE + +def to_type(type, value): + if type == STRING: + if value == "None": + return None + return str(value) + if type == BOOL: + return value == "True" + if type == INTEGER: + return int(value) + if type == DOUBLE: + return float(value) + +class ECXMLParser(object): + def to_xml(self, ec): + + doc = minidom.Document() + + self._ec_to_xml(doc, ec) + + try: + xml = doc.toprettyxml(indent=" ", encoding="UTF-8") + except: + print >>sys.stderr, "Oops: generating XML from %s" % (data,) + raise + + return xml + + def _ec_to_xml(self, doc, ec): + ecnode = doc.createElement("experiment") + ecnode.setAttribute("exp_id", xmlencode(ec.exp_id)) + ecnode.setAttribute("run_id", xmlencode(ec.run_id)) + ecnode.setAttribute("nthreads", xmlencode(ec.nthreads)) + doc.appendChild(ecnode) + + for guid, rm in ec._resources.iteritems(): + self._rm_to_xml(doc, ecnode, ec, guid, rm) + + return doc + + def _rm_to_xml(self, doc, ecnode, ec, guid, rm): + rmnode = doc.createElement("rm") + rmnode.setAttribute("guid", xmlencode(guid)) + rmnode.setAttribute("rtype", xmlencode(rm._rtype)) + rmnode.setAttribute("state", xmlencode(rm._state)) + if rm._start_time: + rmnode.setAttribute("start_time", xmlencode(rm._start_time)) + if rm._stop_time: + rmnode.setAttribute("stop_time", xmlencode(rm._stop_time)) + if rm._discover_time: + rmnode.setAttribute("discover_time", xmlencode(rm._discover_time)) + if rm._provision_time: + rmnode.setAttribute("provision_time", xmlencode(rm._provision_time)) + if rm._ready_time: + rmnode.setAttribute("ready_time", xmlencode(rm._ready_time)) + if rm._release_time: + rmnode.setAttribute("release_time", xmlencode(rm._release_time)) + if rm._failed_time: + rmnode.setAttribute("failed_time", xmlencode(rm._failed_time)) + ecnode.appendChild(rmnode) + + anode = doc.createElement("attributes") + attributes = False + + for attr in rm._attrs.values(): + if attr.has_changed: + attributes = True + aanode = doc.createElement("attribute") + aanode.setAttribute("name", xmlencode(attr.name)) + aanode.setAttribute("value", xmlencode(attr.value)) + aanode.setAttribute("type", from_type(attr.value)) + anode.appendChild(aanode) + + if attributes: + rmnode.appendChild(anode) + + cnode = doc.createElement("connections") + connections = False + + for guid in rm._connections: + connections = True + ccnode = doc.createElement("connection") + ccnode.setAttribute("guid", xmlencode(guid)) + cnode.appendChild(ccnode) + + if connections: + rmnode.appendChild(cnode) + + cnnode = doc.createElement("conditions") + conditions = False + + for action, conds in rm._conditions.iteritems(): + conditions = True + for (group, state, time) in conds: + cnnode = doc.createElement("condition") + ccnnode.setAttribute("action", xmlencode(action)) + ccnnode.setAttribute("group", xmlencode(group)) + ccnnode.setAttribute("state", xmlencode(state)) + ccnnode.setAttribute("time", xmlencode(time)) + cnnode.appendChild(ccnnode) + + if conditions: + rmnode.appendChild(cnnode) + + tnode = doc.createElement("traces") + traces = False + + for trace in rm._trcs.values(): + if trace.enabled: + traces = True + ttnode = doc.createElement("trace") + ttnode.setAttribute("name", xmlencode(trace.name)) + tnode.appendChild(ttnode) + + if traces: + rmnode.appendChild(tnode) + + def from_xml(self, xml): + doc = minidom.parseString(xml) + return self._ec_from_xml(doc) + + def _ec_from_xml(self, doc): + from nepi.execution.ec import ExperimentController + ec = None + + ecnode_list = doc.getElementsByTagName("experiment") + for ecnode in ecnode_list: + if ecnode.nodeType == doc.ELEMENT_NODE: + exp_id = ecnode.getAttribute("exp_id") + run_id = ecnode.getAttribute("run_id") + nthreads = int(ecnode.getAttribute("nthreads")) + + os.environ["NEPI_NTHREADS"] = str(nthreads) + ec = ExperimentController(exp_id = exp_id) + + connections = set() + + rmnode_list = ecnode.getElementsByTagName("rm") + for rmnode in rmnode_list: + if rmnode.nodeType == doc.ELEMENT_NODE: + self._rm_from_xml(doc, rmnode, ec, connections) + + for (guid1, guid2) in connections: + ec.register_connection(guid1, guid2) + + break + + return ec + + def _rm_from_xml(self, doc, rmnode, ec, connections): + start_time = None + stop_time = None + discover_time = None + provision_time = None + ready_time = None + release_time = None + failed_time = None + + guid = int(rmnode.getAttribute("guid")) + rtype = xmldecode(rmnode.getAttribute("rtype")) + state = int(rmnode.getAttribute("state")) + + if rmnode.hasAttribute("start_time"): + start_time = xmldecode(rmnode.getAttribute("start_time")) + if rmnode.hasAttribute("stop_time"): + stop_time = xmldecode(rmnode.getAttribute("stop_time")) + if rmnode.hasAttribute("discover_time"): + dicover_time = xmldecode(rmnode.getAttribute("discover_time")) + if rmnode.hasAttribute("provision_time"): + provision_time = xmldecode(rmnode.getAttribute("provision_time")) + if rmnode.hasAttribute("ready_time"): + ready_time = xmldecode(rmnode.getAttribute("ready_time")) + if rmnode.hasAttribute("release_time"): + release_time = xmldecode(rmnode.getAttribute("release_time")) + if rmnode.hasAttribute("failed_time"): + failed_time = xmldecode(rmnode.getAttribute("failed_time")) + + ec.register_resource(rtype, guid = guid) + rm = ec.get_resource(guid) + rm.set_state_time(state, "_start_time", start_time) + rm.set_state_time(state, "_stop_time", stop_time) + rm.set_state_time(state, "_discover_time", discover_time) + rm.set_state_time(state, "_provision_time", provision_time) + rm.set_state_time(state, "_ready_time", ready_time) + rm.set_state_time(state, "_release_time", release_time) + rm.set_state_time(state, "_failed_time", failed_time) + + anode_list = rmnode.getElementsByTagName("attributes") + if anode_list: + aanode_list = anode_list[0].getElementsByTagName("attribute") + for aanode in aanode_list: + name = xmldecode(aanode.getAttribute("name")) + value = xmldecode(aanode.getAttribute("value")) + type = xmldecode(aanode.getAttribute("type")) + value = to_type(type, value) + rm.set(name, value) + + cnode_list = rmnode.getElementsByTagName("connections") + if cnode_list: + ccnode_list = cnode_list[0].getElementsByTagName("connection") + for ccnode in ccnode_list: + guid2 = int(ccnode.getAttribute("guid")) + connections.add((guid, guid2)) + + tnode_list = rmnode.getElementsByTagName("traces") + if tnode_list: + ttnode_list = tnode_list[0].getElementsByTagName("trace") + for ttnode in ttnode_list: + name = xmldecode(ttnode.getAttribute("name")) + ec.enable_trace(guid, name) + + cnnode_list = rmnode.getElementsByTagName("conditions") + if cnnode_list: + ccnnode_list = cnnode_list[0].getElementsByTagName("condition") + for ccnnode in ccnnode_list: + action = int(ccnnode.getAttribute("action")) + group = int(ccnnode.getAttribute("group")) + state = int(ccnnode.getAttribute("state")) + time = ccnnode.getAttribute("time") + ec.register_condition(guid, action, group, state, time = time) + diff --git a/src/nepi/util/serializer.py b/src/nepi/util/serializer.py new file mode 100644 index 00000000..9ed216f2 --- /dev/null +++ b/src/nepi/util/serializer.py @@ -0,0 +1,61 @@ +# +# 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 . +# +# Author: Alina Quereilhac + +import datetime +import os + +class SFormats: + XML = "xml" + +class ECSerializer(object): + def load(self, path, format = SFormats.XML): + if format == SFormats.XML: + from nepi.util.parsers.xml_parser import ECXMLParser + + parser = ECXMLParser() + f = open(path, "r") + xml = f.read() + f.close() + + ec = parser.from_xml(xml) + + return ec + + def serialize(self, ec, format = SFormats.XML): + if format == SFormats.XML: + from nepi.util.parsers.xml_parser import ECXMLParser + + parser = ECXMLParser() + sec = parser.to_xml(ec) + + return sec + + def save(self, ec, path, format = SFormats.XML): + date = datetime.datetime.now().strftime('%Y%m%d%H%M%S') + filename = "%s_%s" % (ec.exp_id, date) + + if format == SFormats.XML: + path = os.path.join(path, "%s.xml" % filename) + sec = self.serialize(ec, format = format) + f = open(path, "w") + f.write(sec) + f.close() + + return path + diff --git a/test/design/box.py b/test/design/box.py deleted file mode 100755 index d1b9c031..00000000 --- a/test/design/box.py +++ /dev/null @@ -1,69 +0,0 @@ -#!/usr/bin/env python -# -# 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 . -# -# Author: Alina Quereilhac - - -from nepi.design.box import Box - -import unittest - -class BoxDesignTestCase(unittest.TestCase): - def test_simple_design(self): - node1 = Box() - node2 = Box() - - node1.label = "uno" - node2.label = "dos" - - node1.tadd('nodo') - node2.tadd('mynodo') - - self.assertEquals(node1.tags, set(['nodo'])) - self.assertEquals(node2.tags, set(['mynodo'])) - - node1.a.hola = "chau" - node2.a.hello = "bye" - - self.assertEquals(node1.a.hola, "chau") - self.assertEquals(node2.a.hello, "bye") - - node1.connect(node2) - - self.assertEquals(node1.connections, set([node2])) - self.assertEquals(node2.connections, set([node1])) - self.assertTrue(node1.is_connected(node2)) - self.assertTrue(node2.is_connected(node1)) - - self.assertEquals(node1.c.dos.a.hello, "bye") - self.assertEquals(node2.c.uno.a.hola, "chau") - - node2.disconnect(node1) - - self.assertEquals(node1.connections, set([])) - self.assertEquals(node2.connections, set([])) - self.assertFalse(node1.is_connected(node2)) - self.assertFalse(node2.is_connected(node1)) - - self.assertRaises(AttributeError, node1.c.dos) - self.assertRaises(AttributeError, node2.c.uno) - - -if __name__ == '__main__': - unittest.main() - diff --git a/test/util/parser.py b/test/util/parser.py deleted file mode 100755 index e9fce6b2..00000000 --- a/test/util/parser.py +++ /dev/null @@ -1,57 +0,0 @@ -#!/usr/bin/env python -# -# 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 . -# -# Author: Alina Quereilhac - - -from nepi.design.box import Box -from nepi.util.parser import XMLParser - -import unittest - -class BoxDesignTestCase(unittest.TestCase): - def test_to_xml(self): - node1 = Box() - node2 = Box() - - node1.label = "node1" - node2.label = "node2" - - node1.connect(node2) - - node1.a.dog = "cat" - node1.a.one = "two" - node1.a.t = "q" - - node1.c.node2.a.sky = "sea" - node2.a.bee = "honey" - - node1.tadd("unooo") - node2.tadd("dosss") - - parser = XMLParser() - xml = parser.to_xml(node1) - - node = parser.from_xml(xml) - xml2 = parser.to_xml(node) - - self.assertEquals(xml, xml2) - -if __name__ == '__main__': - unittest.main() - diff --git a/test/util/serializer.py b/test/util/serializer.py new file mode 100755 index 00000000..7d7cf6a8 --- /dev/null +++ b/test/util/serializer.py @@ -0,0 +1,143 @@ +#!/usr/bin/env python +# +# 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 . +# +# Author: Alina Quereilhac + +from nepi.execution.ec import ExperimentController +from nepi.execution.resource import ResourceManager, ResourceState, \ + clsinit_copy, ResourceAction, ResourceFactory + +import tempfile +import time +import unittest + +reschedule_delay = "0.5s" +deploy_time = 0 +run_time = 0 + +class Link(ResourceManager): + _rtype = "dummy::Link" + def do_deploy(self): + time.sleep(deploy_time) + super(Link, self).do_deploy() + self.logger.debug(" -------- DEPLOYED ------- ") + +class Interface(ResourceManager): + _rtype = "dummy::Interface" + + def do_deploy(self): + node = self.get_connected(Node.get_rtype())[0] + link = self.get_connected(Link.get_rtype())[0] + + if node.state < ResourceState.READY or \ + link.state < ResourceState.READY: + self.ec.schedule(reschedule_delay, self.deploy) + self.logger.debug(" -------- RESCHEDULING ------- ") + else: + time.sleep(deploy_time) + super(Interface, self).do_deploy() + self.logger.debug(" -------- DEPLOYED ------- ") + +class Node(ResourceManager): + _rtype = "dummy::Node" + + def do_deploy(self): + self.logger.debug(" -------- DO_DEPLOY ------- ") + time.sleep(deploy_time) + super(Node, self).do_deploy() + self.logger.debug(" -------- DEPLOYED ------- ") + +class Application(ResourceManager): + _rtype = "dummy::Application" + + def do_deploy(self): + node = self.get_connected(Node.get_rtype())[0] + + if node.state < ResourceState.READY: + self.ec.schedule(reschedule_delay, self.deploy) + self.logger.debug(" -------- RESCHEDULING ------- ") + else: + time.sleep(deploy_time) + super(Application, self).do_deploy() + self.logger.debug(" -------- DEPLOYED ------- ") + + def do_start(self): + super(Application, self).do_start() + time.sleep(run_time) + self.ec.schedule("0s", self.stop) + +ResourceFactory.register_type(Application) +ResourceFactory.register_type(Node) +ResourceFactory.register_type(Interface) +ResourceFactory.register_type(Link) + +class SerializeTestCase(unittest.TestCase): + def test_serialize(self): + node_count = 4 + app_count = 2 + + dirpath = tempfile.mkdtemp() + + ec = ExperimentController(exp_id = "serialize-test") + + # Add simulated nodes and applications + nodes = list() + apps = list() + ifaces = list() + + for i in xrange(node_count): + node = ec.register_resource("dummy::Node") + nodes.append(node) + + iface = ec.register_resource("dummy::Interface") + ec.register_connection(node, iface) + ifaces.append(iface) + + for i in xrange(app_count): + app = ec.register_resource("dummy::Application") + ec.register_connection(node, app) + apps.append(app) + + link = ec.register_resource("dummy::Link") + + for iface in ifaces: + ec.register_connection(link, iface) + + filepath = ec.save(dirpath) + print filepath + + ec.deploy() + + # Wait until nodes and apps are deployed + ec.wait_finished(apps) + + # Do the experiment controller shutdown + ec.shutdown() + + ec2 = ExperimentController.load(filepath) + apps = ec2.get_resources_by_type("dummy::Application") + ec2.deploy() + ec2.wait_finished(apps) + ec2.shutdown() + + self.assertEquals(len(ec.resources), len(ec2.resources)) + + +if __name__ == '__main__': + unittest.main() + -- 2.43.0