Commit 0039df43 authored by Martín Ferrari's avatar Martín Ferrari

Skeleton for protocol server code

parent 93b5fe85
...@@ -19,7 +19,7 @@ ADDR DEL if# addr_spec 200/500 ip addr del ...@@ -19,7 +19,7 @@ ADDR DEL if# addr_spec 200/500 ip addr del
ROUT LIST 200 serialised data ip route list ROUT LIST 200 serialised data ip route list
ROUT ADD route_spec 200/500 ip route add ROUT ADD route_spec 200/500 ip route add
ROUT DEL route_spec 200/500 ip route del ROUT DEL route_spec 200/500 ip route del
PROC CRTE args_len 354+200/500 (2) PROC CRTE [args_len] 354+200/500 (2)
PROC SIN 354+200/500 (3) PROC SIN 354+200/500 (3)
PROC SOUT 354+200/500 (3) PROC SOUT 354+200/500 (3)
PROC SERR 354+200/500 (3) PROC SERR 354+200/500 (3)
...@@ -32,8 +32,8 @@ PROC KILL <pid> <signal> 200/500 kill(pid, signal) ...@@ -32,8 +32,8 @@ PROC KILL <pid> <signal> 200/500 kill(pid, signal)
(1) valid arguments: mtu <n>, state <up|down>, name <name>, lladdr <addr> (1) valid arguments: mtu <n>, state <up|down>, name <name>, lladdr <addr>
(2) After PROC CRTE, only secondary PROC cmds are accepted until finished. (2) After PROC CRTE, only secondary PROC cmds are accepted until finished.
Server waits for serialized data (lenght pre-specified) specifying complex Server waits for serialized data (lenght pre-specified, or empty line to
arguments: cwd, env, argv. finish) specifying complex arguments: cwd, env, argv.
After receiving the arguments, answers with 200 or 500. After receiving the arguments, answers with 200 or 500.
(3) Secondary PROC commands, only valid after PROC CRTE. Server reply 354 and (3) Secondary PROC commands, only valid after PROC CRTE. Server reply 354 and
......
...@@ -2,29 +2,28 @@ ...@@ -2,29 +2,28 @@
# vim:ts=4:sw=4:et:ai:sts=4 # vim:ts=4:sw=4:et:ai:sts=4
import os import os
from netns.protocol import Server
try:
from yaml import CLoader as Loader
from yaml import CDumper as Dumper
except ImportError:
from yaml import Loader, Dumper
#yaml.load(stream, Loader = Loader)
class __Config(object): class __Config(object):
def __init__(self): def __init__(self):
self.run_as = None self.run_as = None
config = __Config() config = __Config()
__nodes = set()
def get_nodes(): def get_nodes():
return set() return set(__nodes)
def set_cleanup_hooks(on_exit = False, on_signals = []): def set_cleanup_hooks(on_exit = False, on_signals = []):
pass pass
class Node(object): class Node(object):
def __init__(self): def __init__(self):
self.slave = SlaveNode() self.slave_pid, self.slave_fd = spawn_slave()
self.valid = True self.valid = True
@property
def pid(self):
return self.slave_pid
def add_if(self, mac_address = None, mtu = None): def add_if(self, mac_address = None, mtu = None):
return Interface(mac_address, mtu) return Interface(mac_address, mtu)
def add_route(self, prefix, prefix_len, nexthop = None, interface = None): def add_route(self, prefix, prefix_len, nexthop = None, interface = None):
...@@ -58,36 +57,33 @@ class Process(object): ...@@ -58,36 +57,33 @@ class Process(object):
self.pid = os.getpid() self.pid = os.getpid()
self.valid = True self.valid = True
import os, socket, sys, unshare import os, socket, sys, traceback, unshare
class SlaveNode(object): def spawn_slave():
def __init__(self): (s0, s1) = socket.socketpair(socket.AF_UNIX, socket.SOCK_STREAM, 0)
(s0, s1) = socket.socketpair(socket.AF_UNIX, socket.SOCK_STREAM, 0) #ppid = os.getpid()
ppid = os.getpid() pid = os.fork()
pid = os.fork() if pid:
if pid: helo = s0.recv(4096).rstrip().split(None, 1)
helo = s0.recv(4096).rstrip().split(None, 1) if int(helo[0]) / 100 != 2:
if int(helo[0]) / 100 != 2: raise RuntimeError("Failed to start slave node: %s" % helo[1])
raise RuntimeError("Failed to start slave node: %s" % helo[1]) s1.close()
self.pid = pid return (pid, s0)
self.sock = s0
s1.close() srv = Server(s1.fileno())
return try:
try: s0.close()
s0.close() #unshare.unshare(unshare.CLONE_NEWNET)
#unshare.unshare(unshare.CLONE_NEWNET) except BaseException, e:
self.sock = s1.makefile("r+") srv.abort(str(e))
self.ppid = ppid
self.run() # Try block just in case...
except BaseException, e: try:
s1.send("500 %s\n" % str(e)) srv.run()
sys.stderr.write("Error starting slave node: %s\n" % str(e)) except:
os._exit(1) traceback.print_exc(file = sys.stderr)
os._exit(1)
else:
os._exit(0) os._exit(0)
def run(self): # NOTREACHED
self.sock.write("220 Hello.\n");
while True:
line = self.sock.readline()
if not line:
break
self.sock.write("ECHO: %s\n" % line.rstrip())
#!/usr/bin/env python
# vim:ts=4:sw=4:et:ai:sts=4
try:
from yaml import CLoader as Loader
from yaml import CDumper as Dumper
except ImportError:
from yaml import Loader, Dumper
#yaml.load(stream, Loader = Loader)
# Protocol definition
#
# First key: command
# Second key: sub-command or None
# Value: pair of format strings for mandatory and optional parameters.
# The format string is a chain of "s" for string and "i" for integer
_proto_commands = {
"QUIT": { None: ("", "") },
"IF": {
"LIST": ("", "i"),
"SET": ("iss", ""),
"RTRN": ("ii", "")
},
"ADDR": {
"LIST": ("", "i"),
"ADD": ("isi", "s"),
"DEL": ("iss", "s")
},
"ROUT": {
"LIST": ("", ""),
"ADD": ("sisi", ""),
"DEL": ("sisi", "")
},
"PROC": {
"CRTE": ("", "i"),
"POLL": ("i", ""),
"WAIT": ("i", "")
},
}
# Commands valid only after PROC CRTE
_proc_commands = {
"PROC": {
"SIN": ("", ""),
"SOUT": ("", ""),
"SERR": ("", ""),
"RUN": ("", ""),
"ABRT": ("", ""),
}
}
class Server(object):
def __init__(self, fd):
self.commands = _proto_commands
self.closed = False
if hasattr(fd, "readline"):
self.f = fd
else:
if hasattr(fd, "makefile"):
self.f = fd.makefile(fd, "r+", 1) # line buffered
else:
self.f = os.fdopen(fd, "r+", 1)
def abort(self, str):
# FIXME: this should be aware of the state of the server
self.reply(500, str)
sys.stderr.write("Slave node aborting: %s\n" %str);
os._exit(1)
def reply(self, code, text):
if not hasattr(text, '__iter__'):
text = [ text ]
for i in range(len(text) - 1):
self.f.write(str(code) + "-" + text[i] + "\n")
self.f.write(str(code) + " " + text[-1] + "\n")
return
def readline(self):
line = self.f.readline()
if not line:
self.closed = True
return None
return line.rstrip()
def readchunk(self, size):
read = 0
res = ""
while True:
line = self.f.readline()
if not line:
self.closed = True
return None
if size == None and line == "\n":
break
read += len(line)
res += line
if size != None and read >= size:
break
return res
def readcmd(self):
line = self.readline()
if not line:
return None
args = line.split()
cmd1 = args[0].upper()
if cmd1 not in self.commands:
self.reply(500, "Unknown command %s." % cmd1)
return None
del args[0]
cmd2 = None
subcommands = self.commands[cmd1]
if subcommands.keys() != [ None ]:
if len(args) < 1:
self.reply(500, "Incomplete command.")
return None
cmd2 = args[0].upper()
del args[0]
if cmd2 and cmd2 not in subcommands:
self.reply(500, "Unknown sub-command for %s." % cmd1)
return None
(mandatory, optional) = subcommands[cmd2]
argstemplate = mandatory + optional
if cmd2:
cmdname = "%s %s" % (cmd1, cmd2)
funcname = "do_%s_%s" % (cmd1, cmd2)
else:
cmdname = cmd1
funcname = "do_%s" % cmd1
if not hasattr(self, funcname):
self.reply(500, "Not implemented.")
return None
if len(args) < len(mandatory):
self.reply(500, "Missing mandatory arguments for %s." % cmdname)
return None
if len(args) > len(argstemplate):
self.reply(500, "Too many arguments for %s." % cmdname)
return None
for i in range(len(args)):
if argstemplate[i] == 'i':
try:
args[i] = int(args[i])
except:
self.reply(500, "Invalid parameter %s: must be an integer."
% args[i])
return None
elif argstemplate[i] == 's':
pass
else:
raise RuntimeError("Invalid argument template: %s" % _argstmpl)
func = getattr(self, funcname)
return (func, cmdname, args)
def run(self):
self.reply(220, "Hello.");
while not self.closed:
cmd = self.readcmd()
if cmd == None:
continue
cmd[0](cmd[1], cmd[2])
try:
self.f.close()
except:
pass
def do_QUIT(self, cmdname, args):
self.reply(221, "Sayounara.");
self.closed = True
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment