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
ROUT LIST 200 serialised data ip route list
ROUT ADD route_spec 200/500 ip route add
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 SOUT 354+200/500 (3)
PROC SERR 354+200/500 (3)
......@@ -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>
(2) After PROC CRTE, only secondary PROC cmds are accepted until finished.
Server waits for serialized data (lenght pre-specified) specifying complex
arguments: cwd, env, argv.
Server waits for serialized data (lenght pre-specified, or empty line to
finish) specifying complex arguments: cwd, env, argv.
After receiving the arguments, answers with 200 or 500.
(3) Secondary PROC commands, only valid after PROC CRTE. Server reply 354 and
......
......@@ -2,29 +2,28 @@
# vim:ts=4:sw=4:et:ai:sts=4
import os
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)
from netns.protocol import Server
class __Config(object):
def __init__(self):
self.run_as = None
config = __Config()
__nodes = set()
def get_nodes():
return set()
return set(__nodes)
def set_cleanup_hooks(on_exit = False, on_signals = []):
pass
class Node(object):
def __init__(self):
self.slave = SlaveNode()
self.slave_pid, self.slave_fd = spawn_slave()
self.valid = True
@property
def pid(self):
return self.slave_pid
def add_if(self, mac_address = None, mtu = None):
return Interface(mac_address, mtu)
def add_route(self, prefix, prefix_len, nexthop = None, interface = None):
......@@ -58,36 +57,33 @@ class Process(object):
self.pid = os.getpid()
self.valid = True
import os, socket, sys, unshare
class SlaveNode(object):
def __init__(self):
(s0, s1) = socket.socketpair(socket.AF_UNIX, socket.SOCK_STREAM, 0)
ppid = os.getpid()
pid = os.fork()
if pid:
helo = s0.recv(4096).rstrip().split(None, 1)
if int(helo[0]) / 100 != 2:
raise RuntimeError("Failed to start slave node: %s" % helo[1])
self.pid = pid
self.sock = s0
s1.close()
return
try:
s0.close()
#unshare.unshare(unshare.CLONE_NEWNET)
self.sock = s1.makefile("r+")
self.ppid = ppid
self.run()
except BaseException, e:
s1.send("500 %s\n" % str(e))
sys.stderr.write("Error starting slave node: %s\n" % str(e))
os._exit(1)
import os, socket, sys, traceback, unshare
def spawn_slave():
(s0, s1) = socket.socketpair(socket.AF_UNIX, socket.SOCK_STREAM, 0)
#ppid = os.getpid()
pid = os.fork()
if pid:
helo = s0.recv(4096).rstrip().split(None, 1)
if int(helo[0]) / 100 != 2:
raise RuntimeError("Failed to start slave node: %s" % helo[1])
s1.close()
return (pid, s0)
srv = Server(s1.fileno())
try:
s0.close()
#unshare.unshare(unshare.CLONE_NEWNET)
except BaseException, e:
srv.abort(str(e))
# Try block just in case...
try:
srv.run()
except:
traceback.print_exc(file = sys.stderr)
os._exit(1)
else:
os._exit(0)
def run(self):
self.sock.write("220 Hello.\n");
while True:
line = self.sock.readline()
if not line:
break
self.sock.write("ECHO: %s\n" % line.rstrip())
# NOTREACHED
#!/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