You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
lokinet/contrib/py/pylokinet/pylokinet/instance.py

225 lines
6.8 KiB
Python

#!/usr/bin/env python3
#
# lokinet runtime wrapper
#
from ctypes import *
import configparser
import signal
import time
import threading
import os
import sys
import requests
from pylokinet import rc
lib_file = os.path.join(os.path.realpath('.'), 'liblokinet-shared.so')
def log(msg):
sys.stderr.write("lokinet: {}\n".format(msg))
sys.stderr.flush()
class LokiNET(threading.Thread):
lib = None
ctx = 0
failed = False
up = False
asRouter = True
def configure(self, lib, conf, ip=None, port=None, ifname=None, seedfile=None, lokid_host=None, lokid_port=None):
log("configure lib={} conf={}".format(lib, conf))
if not os.path.exists(os.path.dirname(conf)):
os.mkdir(os.path.dirname(conf))
try:
self.lib = CDLL(lib)
except OSError as ex:
log("failed to load library: {}".format(ex))
return False
if self.lib.llarp_ensure_config(conf.encode('utf-8'), os.path.dirname(conf).encode('utf-8'), True, self.asRouter):
config = configparser.ConfigParser()
config.read(conf)
log('overwrite ip="{}" port="{}" ifname="{}" seedfile="{}" lokid=("{}", "{}")'.format(
ip, port, ifname, seedfile, lokid_host, lokid_port))
if seedfile and lokid_host and lokid_port:
if not os.path.exists(seedfile):
log('cannot access service node seed at "{}"'.format(seedfile))
return False
config['lokid'] = {
'service-node-seed': seedfile,
'enabled': "true",
'jsonrpc': "{}:{}".format(lokid_host, lokid_port)
}
if ip:
config['router']['public-address'] = '{}'.format(ip)
if port:
config['router']['public-port'] = '{}'.format(port)
if ifname and port:
config['bind'] = {
ifname: '{}'.format(port)
}
with open(conf, "w") as f:
config.write(f)
self.ctx = self.lib.llarp_main_init(conf.encode('utf-8'))
else:
return False
return self.lib.llarp_main_setup(self.ctx, False) == 0
def inform_fail(self):
"""
inform lokinet crashed
"""
self.failed = True
self._inform()
def inform_up(self):
self.up = True
self._inform()
def _inform(self):
"""
inform waiter
"""
def wait_for_up(self, timeout):
"""
wait for lokinet to go up for :timeout: seconds
:return True if we are up and running otherwise False:
"""
# return self._up.wait(timeout)
def signal(self, sig):
if self.ctx and self.lib:
self.lib.llarp_main_signal(self.ctx, int(sig))
def run(self):
# self._up.acquire()
self.up = True
code = self.lib.llarp_main_run(self.ctx)
log("llarp_main_run exited with status {}".format(code))
if code:
self.inform_fail()
self.up = False
# self._up.release()
def close(self):
if self.lib and self.ctx:
self.lib.llarp_main_free(self.ctx)
def getconf(name, fallback=None):
return name in os.environ and os.environ[name] or fallback
def run_main(args):
seedfile = getconf("LOKI_SEED_FILE")
if seedfile is None:
print("LOKI_SEED_FILE was not set")
return
lokid_host = getconf("LOKI_RPC_HOST", "127.0.0.1")
lokid_port = getconf("LOKI_RPC_PORT", "22023")
root = getconf("LOKINET_ROOT")
if root is None:
print("LOKINET_ROOT was not set")
return
rc_callback = getconf("LOKINET_SUBMIT_URL")
if rc_callback is None:
print("LOKINET_SUBMIT_URL was not set")
return
bootstrap = getconf("LOKINET_BOOTSTRAP_URL")
if bootstrap is None:
print("LOKINET_BOOTSTRAP_URL was not set")
lib = getconf("LOKINET_LIB", lib_file)
if not os.path.exists(lib):
lib = "liblokinet-shared.so"
timeout = int(getconf("LOKINET_TIMEOUT", "5"))
ping_interval = int(getconf("LOKINET_PING_INTERVAL", "60"))
ping_callback = getconf("LOKINET_PING_URL")
ip = getconf("LOKINET_IP")
port = getconf("LOKINET_PORT")
ifname = getconf("LOKINET_IFNAME")
if ping_callback is None:
print("LOKINET_PING_URL was not set")
return
conf = os.path.join(root, "daemon.ini")
log("going up")
loki = LokiNET()
log("bootstrapping...")
try:
r = requests.get(bootstrap)
if r.status_code == 404:
log("bootstrap gave no RCs, we are probably the seed node")
elif r.status_code != 200:
raise Exception("http {}".format(r.status_code))
else:
data = r.content
if rc.validate(data):
log("valid RC obtained")
with open(os.path.join(root, "bootstrap.signed"), "wb") as f:
f.write(data)
else:
raise Exception("invalid RC")
except Exception as ex:
log("failed to bootstrap: {}".format(ex))
loki.close()
return
if loki.configure(lib, conf, ip, port, ifname, seedfile, lokid_host, lokid_port):
log("configured")
loki.start()
try:
log("waiting for spawn")
while timeout > 0:
time.sleep(1)
if loki.failed:
log("failed")
break
log("waiting {}".format(timeout))
timeout -= 1
if loki.up:
log("submitting rc")
try:
with open(os.path.join(root, 'self.signed'), 'rb') as f:
r = requests.put(rc_callback, data=f.read(), headers={
"content-type": "application/octect-stream"})
log('submit rc reply: HTTP {}'.format(r.status_code))
except Exception as ex:
log("failed to submit rc: {}".format(ex))
loki.signal(signal.SIGINT)
time.sleep(2)
else:
while loki.up:
time.sleep(ping_interval)
try:
r = requests.get(ping_callback)
log("ping reply: HTTP {}".format(r.status_code))
except Exception as ex:
log("failed to submit ping: {}".format(ex))
else:
log("failed to go up")
loki.signal(signal.SIGINT)
except KeyboardInterrupt:
loki.signal(signal.SIGINT)
time.sleep(2)
finally:
loki.close()
else:
loki.close()
def main():
run_main(sys.argv[1:])
if __name__ == "__main__":
main()