new NEPI version. Improve modularity and remove rpyc.
authorAlina Quereilhac <alina.quereilhac@inria.fr>
Sun, 6 Feb 2011 20:19:10 +0000 (21:19 +0100)
committerAlina Quereilhac <alina.quereilhac@inria.fr>
Sun, 6 Feb 2011 20:19:10 +0000 (21:19 +0100)
src/nepi/core/attributes.py [new file with mode: 0644]
src/nepi/core/backend.py [new file with mode: 0644]
src/nepi/core/controller.py [new file with mode: 0644]
src/nepi/core/experiment.py [new file with mode: 0644]
src/nepi/core/models.py [new file with mode: 0644]
src/nepi/core/testbed.py [new file with mode: 0644]
src/nepi/utils/server.py [new file with mode: 0644]

diff --git a/src/nepi/core/attributes.py b/src/nepi/core/attributes.py
new file mode 100644 (file)
index 0000000..53560ba
--- /dev/null
@@ -0,0 +1,81 @@
+# -*- coding: utf-8 -*-
+# vim:ts=4:sw=4:et:ai:sts=4
+
+class AttributesMap(object):
+    """AttributesMap is the base class for every object whose attributes 
+    are going to be manipulated by the end-user in a script or GUI.
+    """
+    def __init__(self):
+        self._attributes = dict()
+
+    @property
+    def attributes_name(self):
+        return set(self._attributes.keys())
+
+    def is_valid_attribute_value(self, name, value):
+        raise NotImplementedError
+
+    def set_attribute_value(self, name, value):
+        if self.is_valid_attribute_value(name, value):
+            self._attributes[name].value = value
+            return True
+        return False
+
+    def set_attribute_readonly(self, name, value):
+        self._attributes[name].readonly = value
+
+    def get_attribute_value(self, name):
+        return self._attributes[name].value
+
+    def get_attribute_help(self, name):
+        return self._attributes[name].help
+
+    def get_attribute_type(self, name):
+        return self._attributes[name].type
+
+    def get_attribute_range(self, name):
+        return self._attributes[name].range
+
+    def get_attribute_allowed(self, name):
+        return self._attributes[name].allowed
+
+    def get_attribute_readonly(self, name):
+        return self._attributes[name].readonly
+
+    def add_attribute(self, name, help, type, value = None, range = None,
+        allowed = None, readonly = False):
+        if name in self._attributes:
+            raise AttributeError('Attribute %s already exists' % name))
+        attribute = Attribute(name, help, type, value, range, allowed, readonly)
+        self._attributes[name] = attribute
+
+    def del_attribute(self, name):
+        del self._attributes[name]
+
+    def has_attribute(self, name):
+        return name in self._attributes    
+    
+    def destroy(self):
+        self._attributes = dict()
+
+class Attribute(object):
+    STRING , BOOL, ENUM, DOUBLE, INTEGER, ENDPOINT, TIME = (
+               "STRING", "BOOL", "ENUM", "DOUBLE", "INTEGER", "ENDPOINT", "TIME")
+
+    types = [STRING, BOOL, ENUM, DOUBLE, INTEGER, ENDPOINT, TIME]
+
+    def __init__(self, name, help, type, value = None, range = None,
+        allowed = None, readonly = False):
+        if not type in Attribute.types:
+            raise AttributeError("invalid type %s " % type)
+        self.name = name
+        self.value = value
+        self.type = type
+        self.help = help
+        self.readonly = (readonly == True)
+        self.modified = False
+        # range: max and min possible values
+        self.range = range
+        # list of possible values
+        self.allowed = allowed
+
diff --git a/src/nepi/core/backend.py b/src/nepi/core/backend.py
new file mode 100644 (file)
index 0000000..918eca6
--- /dev/null
@@ -0,0 +1,196 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+# vim:ts=4:sw=4:et:ai:sts=4
+from nepi.core.attributes import AttributesMap
+
+class Trace(AttributesMap):
+    def __init__(self, name, help, enabled=False):
+        super(Trace, self).__init__()
+        self._name = name
+        self._help = help       
+        self._enabled = enabled
+    
+    @property
+    def name(self):
+        return self._name
+
+    @property
+    def help(self):
+        return self._help
+
+    @property
+    def is_enabled(self):
+        return self._enabled
+
+    def read_trace(self):
+        raise NotImplementedError
+
+    def enable(self):
+        self._enabled = True
+
+    def disable(self):
+        self._enabled = False
+
+class Element(AttributesMap):
+    def __init__(self, guid, factory, container = None):
+        super(Element, self).__init__()
+        # general unique id
+        self._guid = guid
+        # factory identifier or name
+        self._factory_id = factory.factory_id
+        # elements can be nested inside other 'container' elements
+        self._container = container
+        # traces for the element
+        self._traces = dict()
+        # connectors for the element
+        self._connectors = dict()
+        from nepi.core.connection import Connector
+        for connector_type in factory.connector_types:
+            connector = Connector(self, connector_type)
+            self._connectors[connector_type.name] = connector
+
+    @property
+    def guid(self):
+        return self._guid
+
+    @property
+    def factory_id(self):
+        return self._factory_id
+
+    @property
+    def container(self):
+        return self._container
+
+    @property
+    def connections(self):
+        r = set()
+        for c in self.connectors:
+            r.update(c.connections)
+        return r
+
+    @property
+    def connectors(self):
+        return self._connectors.values()
+
+    @property
+    def traces(self):
+        return self._traces.values()
+
+    @property
+    def instructions(self):
+        raise NotImplementedError
+
+    def connector(self, name):
+        return self._connectors[name]
+
+    def trace(self, name):
+        return self._traces[name]
+
+    def destroy(self):
+        super(Element, self).destroy()
+        for c in self.connections:
+            c.disconnect()
+        if len(self.connections) != 0:
+            raise AttributeError('Some connections could not be disconnected')
+        for c in self.connectors:
+            c.destroy()         
+        for t in self.traces:
+            t.destroy()
+        self._connectors = self._traces = None
+
+class Factory(AttributesMap):
+    def __init__(self, factory_id, help = None, category = None):
+        super(Factory, self).__init__()
+        self._factory_id = factory_id
+        self._help = help
+        self._category = category
+        self._connector_types = set()
+
+    @property
+    def factory_id(self):
+        return self._factory_id
+
+    @property
+    def help(self):
+        return self._help
+
+    @property
+    def category(self):
+        return self._category
+
+    @property
+    def connector_types(self):
+        return self._connector_types
+
+    def add_connector_type(self, name, help, display_name, max = -1, min = 0):
+        from nepi.core.connection import ConnectorType
+        connector_type = ConnectorType(name, help, display_name, max, min)            
+        self._connector_types.add(connector_type)
+
+    def create(self, guid, backend, container = None):
+        raise NotImplementedError
+
+    def destroy(self):
+        super(Factory, self).destroy()
+        self._connector_types = None
+
+#TODO: Provide some way to identify that the providers and the factories
+# belong to a specific testbed version
+class Provider(object):
+    def __init__(self):
+        super(Provider, self).__init__()
+        self._factories = dict()
+
+    def factory(self, factory_id):
+        return self._factories[factory_id]
+
+    def add_factory(self, factory):
+        self._factories[factory.factory_id] = factory
+
+    def remove_factory(self, factory_id):
+        del self._factories[factory_id]
+
+    def list_factories(self):
+        return self._factories.keys()
+
+class Backend(AttributeMap):
+    def __init__(self, guid_generator, testbed_id, provider):
+        super(Backend, self).__init__()
+        self._guid_generator = guid_generator
+        self._guid = guid_generator.next()
+        self._testbed_id = testbed_id
+        self._provider = provider
+        self._elements = dict()
+
+    @property
+    def guid(self):
+        return self._guid
+
+    @property
+    def testbed_id(self):
+        return self._testbed_id
+
+    @property
+    def elements(self):
+        return self._elements.values()
+
+    def create(self, factory_id):
+        guid = self.guid_generator.next()
+        factory = self._provider.factories(factory_id)
+        element = factory.create(guid, self)
+        self._elements[guid] = element
+
+    def remove(self, guid):
+        element = self._elements[guid]
+        del self._elements[guid]
+        element.destroy()
+
+    def instructions(self):
+        raise NotImplementedError
+
+    def destroy(self):
+        for guid, element in self._elements.iteitems():
+            del self._elements[guid]
+            element.destroy()
+        self._elements = None
+
diff --git a/src/nepi/core/controller.py b/src/nepi/core/controller.py
new file mode 100644 (file)
index 0000000..d700547
--- /dev/null
@@ -0,0 +1,40 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+# vim:ts=4:sw=4:et:ai:sts=4
+
+#TODO: DEF ERRORCODES
+#TODO: DEF PROTOCOL
+
+class Testbed(object):
+    def __init__(self):
+        self._testbeds = dict()
+
+    def create_testbed(self, guid, testbed_id, config, access_config = None):
+        # TODO: proxy
+        # guid: guid of the associated backend
+        self._testbeds[guid] = testbed
+
+    def destroy_testbed(self, guid):
+        tesbed = self._testbeds[guid]
+        tesbed.shutdown()
+        del self._testbeds[guid]
+
+    def forward(self, guid, instruction):
+        #TODO:
+        pass
+
+    def forward_batch(self, guid, batch):
+        raise NotImplementedError
+
+    def start(self):
+        raise NotImplementedError
+
+    def stop(self):
+        raise NotImplementedError
+
+    def status(self):
+        raise NotImplementedError
+
+    def shutdown(self):
+        raise NotImplementedError
+
diff --git a/src/nepi/core/experiment.py b/src/nepi/core/experiment.py
new file mode 100644 (file)
index 0000000..e50c6fb
--- /dev/null
@@ -0,0 +1,17 @@
+# -*- coding: utf-8 -*-
+# vim:ts=4:sw=4:et:ai:sts=4
+
+class Experiment(object):
+    def __init__(self):
+        self._backends = dict()
+
+    def add_backend(self, backend):
+        self._backends[backend.guid] = backend
+
+    def remove_backend(self, backend):
+        del self._backends[backend.guid]
+
+    def instructions(self):
+        #TODO
+        pass
+
diff --git a/src/nepi/core/models.py b/src/nepi/core/models.py
new file mode 100644 (file)
index 0000000..1f3b5aa
--- /dev/null
@@ -0,0 +1,33 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+# vim:ts=4:sw=4:et:ai:sts=4
+
+class Mobile(object):
+    pass
+
+class RoutingTable(object):
+    def entries(self):
+        pass
+
+    def add_entry(self):
+        pass
+
+    def remove_entry(self):
+        pass
+
+class Entry(object):
+    pass
+
+class Interface(object):
+    pass
+    """
+        Link encap:Local Loopback  
+        inet addr:127.0.0.1  
+        Mask:255.0.0.0
+        inet6 addr: ::1/128 Scope:Host
+        UP LOOPBACK RUNNING  
+        MTU:16436  
+        Metric:1
+    """ 
+
+
diff --git a/src/nepi/core/testbed.py b/src/nepi/core/testbed.py
new file mode 100644 (file)
index 0000000..dd326c5
--- /dev/null
@@ -0,0 +1,69 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+# vim:ts=4:sw=4:et:ai:sts=4
+from nepi.core.attributes import AttributesMap
+
+#TODO: DEF ERRORCODES
+#TODO: DEF PROTOCOL
+
+class Configuration(AttributesMap):
+    pass
+
+class Testbed(object):
+    def __init__(self, configuration):
+        pass
+
+    def execute(self, instruction):
+        #TODO:
+        pass
+
+    def execute_batch(self, batch):
+        raise NotImplementedError
+
+    def create(self, time, guid, factory_id, parameters):
+        raise NotImplementedError
+
+    def destroy(self, time, guid):
+        raise NotImplementedError
+
+    def connect(self, time, connection_guid, 
+            object1_guid, object2_guid, connetor1_id, connector2_id): 
+        raise NotImplementedError
+
+    def disconnect(self, time, connection_guid): 
+        raise NotImplementedError
+
+    def set(self, time, guid, name, value):
+        raise NotImplementedError
+
+    def get(self, time, guid, name):
+        raise NotImplementedError
+
+    def start(self, time, guid):
+        raise NotImplementedError
+
+    def stop(self, time, guid):
+        raise NotImplementedError
+
+    def state(self, time, guid):
+        raise NotImplementedError
+
+    def trace_enable(self, time, guid, trace_id):
+        raise NotImplementedError
+
+    def trace_disable(self, time, guid, trace_id):
+        raise NotImplementedError
+
+    def get_trace(self, time, guid, trace_id):
+        raise NotImplementedError
+
+    def add_adddress(self, time, guid):
+        #TODO
+        raise NotImplementedError
+
+    def add_route(self, time, guid):
+        #TODO
+        raise NotImplementedError
+
+    def shutdown(self):
+        raise NotImplementedError
diff --git a/src/nepi/utils/server.py b/src/nepi/utils/server.py
new file mode 100644 (file)
index 0000000..1571c50
--- /dev/null
@@ -0,0 +1,121 @@
+# -*- coding: utf-8 -*-
+# vim:ts=4:sw=4:et:ai:sts=4
+import errno
+import os
+import select
+import socket
+import sys
+import threading
+
+CTRL_SOCK = "ctrl.sock"
+STD_ERR = "stderr.log"
+MAX_FD = 1024
+
+STOP_MSG = "STOP"
+
+class Server(object):
+    def __init__(self):
+        self.stop = False
+        self.ctrl_sock = None
+
+    def run(self):
+        if self.daemonize():
+            self.loop()
+            self.cleanup()
+
+    def daemonize(self):
+        if True:
+            return 1
+
+        pid1 = os.fork()
+        if pid1 > 0:
+            return 0
+
+        # Decouple from parent environment.
+        #os.chdir(?)
+        os.umask(0)
+        os.setsid()
+
+        # fork 2
+        pid2 = os.fork()
+        if pid2 > 0:
+            return 0
+
+        # close all open file descriptors.
+        for fd in range(0, MAX_FD):
+            try:
+                os.close(fd)
+            except OSError:
+                pass
+
+        # Redirect standard file descriptors.
+        stdout = stderr = file(STD_ERR, "a", 0)
+        stdin = open('/dev/null', 'r')
+        os.dup2(stdin.fileno(), sys.stdin.fileno())
+        os.dup2(stdout.fileno(), sys.stdout.fileno())
+        os.dup2(stderr.fileno(), sys.stderr.fileno())
+        return 1
+
+    def loop(self):
+        self.ctrl_sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
+        self.ctrl_sock.bind(CTRL_SOCK)
+        self.ctrl_sock.listen(0)
+        while not self.stop:
+            print 'accept'
+            conn, addr = self.ctrl_sock.accept()
+            conn.settimeout(5)
+            while True:
+                try:
+                    print 'recv'
+                    data = conn.recv(1024)
+                except socket.timeout, e:
+                    print e
+                    break
+                    
+                if data == STOP_MSG:
+                    self.stop = True
+                else:
+                    conn.send("%s received" % data)
+            conn.close()
+        
+    def cleanup(self):
+        self.ctrl_sock.close()
+        try:
+            s.remove(CTRL_SOCK)
+        except:
+            pass
+
+class Forwarder(object):
+    def __init__(self):
+        self.ctrl_sock = None
+
+    def forward(self):
+        self.connect()
+        while True:
+            msg = sys.stdin.readline()
+            self.send(msg)
+            reply = self.ctrl_sock.recv(1024)
+            sys.stdout.write(reply)
+
+    def send(self, msg):
+        try:
+            self.ctrl_sock.send(msg)
+        except IOError, e:
+            if e.errno == errno.EPIPE:
+                self.connect()
+                self.ctrl_sock.send(msg)
+            else:
+                raise e
+    
+    def connect(self):
+        try:
+            self.ctrl_sock.close()
+        except:
+            pass
+        self.ctrl_sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
+        self.ctrl_sock.connect(CTRL_SOCK)
+
+# Client
+# import subprocess
+# s = subprocess.Popen(['python' ,'-c' 'import server;c=server.Forwarder();c.forward()'], stdin = subprocess.PIPE)
+# s.stdin.write('aaaa\n')