From f016f78902c0b21f8669e402a9874cc73ec1106b Mon Sep 17 00:00:00 2001
From: clerie <git@clerie.de>
Date: Wed, 6 Jul 2022 13:05:06 +0200
Subject: [PATCH] Integrate ywsd

---
 fieldpoc/config.py   |  8 +++++
 fieldpoc/fieldpoc.py | 19 ++++++++++-
 fieldpoc/run.py      |  6 ++++
 fieldpoc/ywsd.py     | 75 ++++++++++++++++++++++++++++++++++++++++++++
 fieldpoc_config.json | 10 ++++++
 5 files changed, 117 insertions(+), 1 deletion(-)
 create mode 100644 fieldpoc/ywsd.py

diff --git a/fieldpoc/config.py b/fieldpoc/config.py
index 4bf47c4..cc63765 100644
--- a/fieldpoc/config.py
+++ b/fieldpoc/config.py
@@ -15,6 +15,9 @@ class ControllerConfig(ConfigBase):
     def __init__(self, c):
         self._c = c
 
+class DatabaseConfig(ConfigBase):
+    pass
+
 
 class DectConfig(ConfigBase):
     def __init__(self, c):
@@ -23,12 +26,17 @@ class DectConfig(ConfigBase):
     def check(self):
         return True
 
+class YateConfig(ConfigBase):
+    pass
+
 
 class Config:
     def __init__(self, c):
         self._c = c
         self.controller = ControllerConfig(c.get("controller", {}))
+        self.database = DatabaseConfig(c.get("database", {}))
         self.dect = DectConfig(c.get("dect", {}))
+        self.yate = YateConfig(c.get("yate", {}))
 
     def check(self):
         return self.dect.check()
diff --git a/fieldpoc/fieldpoc.py b/fieldpoc/fieldpoc.py
index eb8131c..8ebc00b 100644
--- a/fieldpoc/fieldpoc.py
+++ b/fieldpoc/fieldpoc.py
@@ -9,6 +9,7 @@ import threading
 from . import config
 from . import controller
 from . import dect
+from . import ywsd
 
 logger = logging.getLogger("fieldpoc.fieldpoc")
 
@@ -32,6 +33,7 @@ class FieldPOC:
         logger.info("initialising components")
         self._controller = controller.Controller(self)
         self._dect = dect.Dect(self)
+        self._ywsd = ywsd.Ywsd(self)
 
     def queue_all(self, msg):
         """
@@ -41,8 +43,20 @@ class FieldPOC:
         for queue in self.queues.values():
             queue.put(msg)
 
+    def init(self):
+        """
+            Prepare some data structures.
+            Run this once before using FieldPOC.
+        """
+
+        logger.info("initialize datastructures")
+
+        self._ywsd.init()
+
+        logger.info("initialization complete")
+
     def run(self):
-        logger.info("stating components")
+        logger.info("starting components")
 
         self._controller_thread = threading.Thread(target=self._controller.run)
         self._controller_thread.start()
@@ -50,6 +64,9 @@ class FieldPOC:
         self._dect_thread = threading.Thread(target=self._dect.run)
         self._dect_thread.start()
 
+        self._ywsd_thread = threading.Thread(target=self._ywsd.run, daemon=True)
+        self._ywsd_thread.start()
+
         logger.info("started components")
 
     def _load_config(self):
diff --git a/fieldpoc/run.py b/fieldpoc/run.py
index c0e4f60..811f2cc 100644
--- a/fieldpoc/run.py
+++ b/fieldpoc/run.py
@@ -13,6 +13,7 @@ logger = logging.getLogger("fieldpoc.run")
 ap = argparse.ArgumentParser(prog="fieldpoc")
 ap.add_argument("-c", "--config", dest="config_file_path", default="fieldpoc_config.json", help="Path to the fieldpoc config file")
 ap.add_argument("-e", "--extensions", dest="extensions_file_path", default="fieldpoc_extensions.json", help="Path to the fieldpoc extensions file")
+ap.add_argument('--init', dest="init", action='store_true')
 ap.add_argument('--debug', dest="debug", action='store_true')
 
 def run():
@@ -33,6 +34,11 @@ def run():
     logger.info("prepared signal handling")
 
     fp = fieldpoc.FieldPOC(config_file_path=args.config_file_path, extensions_file_path=args.extensions_file_path)
+
+    if args.init:
+        fp.init()
+        exit()
+
     fp.run()
 
     logger.info("handle signals")
diff --git a/fieldpoc/ywsd.py b/fieldpoc/ywsd.py
new file mode 100644
index 0000000..bcca4af
--- /dev/null
+++ b/fieldpoc/ywsd.py
@@ -0,0 +1,75 @@
+#!/usr/bin/env python3
+
+import aiopg.sa
+import asyncio
+import logging
+import ywsd.engine
+import ywsd.objects
+import ywsd.settings
+
+import yate.asyncio
+
+logger = logging.getLogger("fieldpoc.ywsd")
+
+class Ywsd:
+    def __init__(self, fp):
+        self.fp = fp
+
+        self.event_loop = asyncio.SelectorEventLoop()
+
+    def settings(self):
+        class Settings(ywsd.settings.Settings):
+            def __init__(self, config):
+                self.config = config
+
+        return Settings({
+            "RINGBACK_TOP_DIRECTORY": "/opt/sounds",
+            "DB_CONFIG": {
+                "host": self.fp.config.database.hostname,
+                "user": self.fp.config.database.username,
+                "password": self.fp.config.database.password,
+                "database": self.fp.config.database.database,
+            },
+            "STAGE2_DB_CONFIG":{
+                "host": self.fp.config.database.hostname,
+                "user": self.fp.config.database.username,
+                "password": self.fp.config.database.password,
+                "database": self.fp.config.database.database,
+             },
+            "LOCAL_YATE_ID": 1,
+            "INTERNAL_YATE_LISTENER": "voip",
+            "CACHE_IMPLEMENTATION": "ywsd.routing_cache.PythonDictRoutingCache",
+            "YATE_CONNECTION":{
+                "host": self.fp.config.yate.host,
+                "port": self.fp.config.yate.port,
+            },
+        })
+
+    def init(self):
+        async def amain(settings):
+            async with aiopg.sa.create_engine(**settings.DB_CONFIG) as engine:
+                async with engine.acquire() as conn:
+                    await ywsd.objects.regenerate_database_objects(conn)
+
+        asyncio.run(amain(self.settings()))
+
+
+    def run(self):
+        # Mokey patch python-yate so I don't have to fork it yet
+        async def setup_for_tcp(self, host, port):
+            self.reader, self.writer = await asyncio.open_connection(host, port)
+            self.send_connect()
+        yate.asyncio.YateAsync.setup_for_tcp = setup_for_tcp
+
+        # Mokey patch ywsd so I don't have to fork it yet
+        asyncio.set_event_loop(self.event_loop)
+        def add_signal_handler(*args, **kwargs):
+            raise NotImplementedError("Disable signal handling so we can run ywsd in a thread")
+        asyncio.get_event_loop().add_signal_handler = add_signal_handler
+
+        logger.info("starting ywsd")
+        self.app = ywsd.engine.YateRoutingEngine(settings=self.settings(), web_only=False, **self.settings().YATE_CONNECTION)
+        self.app.event_loop = self.event_loop
+        self.app.run()
+
+        logger.info("stopped ywsd")
diff --git a/fieldpoc_config.json b/fieldpoc_config.json
index 85747b0..d50da62 100644
--- a/fieldpoc_config.json
+++ b/fieldpoc_config.json
@@ -7,5 +7,15 @@
     "host": "192.168.0.100",
     "username": "omm",
     "password": "xxx"
+  },
+  "yate": {
+    "host": "127.0.0.1",
+    "port": 5039
+  },
+  "database": {
+    "hostname": "127.0.0.1",
+    "username": "fieldpoc",
+    "password": "fieldpoc",
+    "database": "fieldpoc"
   }
 }