From 57808ae1bede9fbe038c4cfc968c2e6efe759220 Mon Sep 17 00:00:00 2001 From: Alina Quereilhac Date: Sun, 6 Feb 2011 21:19:10 +0100 Subject: [PATCH 1/1] new NEPI version. Improve modularity and remove rpyc. --- src/nepi/core/attributes.py | 81 +++++++++++++++ src/nepi/core/backend.py | 196 ++++++++++++++++++++++++++++++++++++ src/nepi/core/controller.py | 40 ++++++++ src/nepi/core/experiment.py | 17 ++++ src/nepi/core/models.py | 33 ++++++ src/nepi/core/testbed.py | 69 +++++++++++++ src/nepi/utils/server.py | 121 ++++++++++++++++++++++ 7 files changed, 557 insertions(+) create mode 100644 src/nepi/core/attributes.py create mode 100644 src/nepi/core/backend.py create mode 100644 src/nepi/core/controller.py create mode 100644 src/nepi/core/experiment.py create mode 100644 src/nepi/core/models.py create mode 100644 src/nepi/core/testbed.py create mode 100644 src/nepi/utils/server.py diff --git a/src/nepi/core/attributes.py b/src/nepi/core/attributes.py new file mode 100644 index 00000000..53560ba3 --- /dev/null +++ b/src/nepi/core/attributes.py @@ -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 index 00000000..918eca6f --- /dev/null +++ b/src/nepi/core/backend.py @@ -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 index 00000000..d7005477 --- /dev/null +++ b/src/nepi/core/controller.py @@ -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 index 00000000..e50c6fb9 --- /dev/null +++ b/src/nepi/core/experiment.py @@ -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 index 00000000..1f3b5aa0 --- /dev/null +++ b/src/nepi/core/models.py @@ -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 index 00000000..dd326c5c --- /dev/null +++ b/src/nepi/core/testbed.py @@ -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 index 00000000..1571c50a --- /dev/null +++ b/src/nepi/utils/server.py @@ -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') -- 2.47.0