diff --git a/example/example.py b/example/example.py index 6d00f3b..f99e2b1 100644 --- a/example/example.py +++ b/example/example.py @@ -1,4 +1,4 @@ -import pyscion as sci +import pyscion as scion import sys import time @@ -7,27 +7,18 @@ def main(): - sci.init() - print('Local Address is {}'.format(sci.local_address())) + scion.init() + print('Local Address is {}'.format(scion.local_address())) for dest_addr, nbytes in parse_tasks_from_stdin(): destination = "{}:12345".format(dest_addr) # send to port 12345 print(' === TASK to destination {} : {}MB ==='.format(destination, int(nbytes/1024/1024))) - paths = really_get_paths(destination) - print('Got %d paths' % len(paths)) - with sci.connect(destination, paths[0]) as fd: + paths = scion.get_paths(destination) + print('Got {} paths'.format(len(paths))) + with scion.connect(destination, paths[0]) as fd: for i in range(int(nbytes / 1000)+1): fd.write(MTU*b'a') -def really_get_paths(destination): - # getting paths is async, so let's just retry until we get some - while True: - try: - return sci.paths(destination) - except sci.SCIONException: - time.sleep(0.1) - - def parse_tasks_from_stdin(): def parse_line(line): dest, nbytes = line.split() diff --git a/example/pyscion.py b/example/pyscion.py index 3031e0e..55c5a6d 100644 --- a/example/pyscion.py +++ b/example/pyscion.py @@ -4,7 +4,7 @@ - set_log_level(level) - init() - addr = local_address() -- paths = paths(destination) +- paths = get_paths(destination) - fd = connect(destination, path) - fd.close() - fd.write(buffer) @@ -60,3 +60,7 @@ def paths(destination): def listen(port): return connect(None, None) + + +def get_paths(destination, loop_till_have_paths=True): + return paths(destination) diff --git a/go/example.py b/go/example.py index 839f79b..3289e58 100644 --- a/go/example.py +++ b/go/example.py @@ -8,7 +8,7 @@ def main(): print('Local Address is %s' % local_address()) destination = '17-ffaa:1:a,[127.0.0.1]:12345' - p = paths(destination) + p = get_paths(destination, loop_till_have_paths=False) print('Got %d paths:' % len(p)) for i in range(len(p)): print('[%d] %s' % (i, str(p))) diff --git a/go/pyscion.py b/go/pyscion.py index 685e736..75f5925 100644 --- a/go/pyscion.py +++ b/go/pyscion.py @@ -1,5 +1,23 @@ +"""Rudimentary Python API for SCION: wraps a simplified Go API. + +To use this, you need to build scion_api.go, which will give you a .so that +this file provides bindings for. Place both the .so and this file into the same +directory, somewhere in your Python path (e.g. into .). + +The API consists of: +- set_log_level(level) +- init() +- addr = local_address() +- paths = get_paths(destination) +- fd = connect(destination, path) +- fd.close() +- fd.write(buffer) +- fd = listen(port) +- addr, n = fd.read(buffer) +""" + __all__ = ['SCIONException', 'HostInfo', 'FwdPathMeta', 'Interface', 'Path', 'connect', - 'set_log_level', 'init', 'local_address', 'paths', 'listen'] + 'set_log_level', 'init', 'local_address', 'get_paths', 'listen'] from ctypes import * from collections import namedtuple @@ -164,7 +182,7 @@ def local_address(): _lib.Paths.restype = c_char_p _lib.FreePathsMemory.argtypes = [POINTER(_PathReplyEntry), c_size_t] _lib.FreePathsMemory.restype = c_char_p -def paths(destination): +def _call_paths(destination): paths_n = c_size_t() paths = (POINTER(_PathReplyEntry))() err = _lib.Paths(byref(paths_n), byref(paths), _str_to_cstr(destination)) @@ -229,3 +247,32 @@ def listen(port): conn = connect(None, None) conn.fd = int(fd.value) return conn + + +def get_paths(destination, loop_till_have_paths=True): + """Returns a list of SCION Paths. + + Warning: This function will change! However, using it with only the + destination (and leaving all other arguments default) will have the same + semantics, i.e. will block until it can return a non-empty list of paths to + the destination. + + Getting paths in SCION can be async/non-blocking: if you pass + loop_till_have_paths=False, this function will return immediately with + whatever paths are in the cache right now. If this is the first time you're + looking up this destination, the cache may be empty (and this will return + []). + + If loop_till_have_paths=True, this function will retry until it gets + at least one path. (Note that if the destination is unreachable, this will + loop forever!) + + Ideally, this function would be able to distinguish between "no paths in + cache" and "unreachable". That is a TODO. + """ + while True: + try: + return _call_paths(destination) + except SCIONException as e: + if not loop_till_have_paths: + raise e