From 51dfd8256f34b1b46637efd6cfca251066b574eb Mon Sep 17 00:00:00 2001
From: Tomas Peterka <tomas.peterka@nexedi.com>
Date: Tue, 11 Apr 2017 11:57:27 -0400
Subject: [PATCH] WIP: lte-standalone a unprivileged bundle of Amarisoft LTE
 stack

---
 software/lte-standalone/instance.jinja2.cfg | 172 +++++++++++++
 software/lte-standalone/ltelogs.sh          |  25 ++
 software/lte-standalone/ltestart.jinja2.sh  | 266 ++++++++++++++++++++
 software/lte-standalone/readme.rst          |  52 ++++
 software/lte-standalone/software.cfg        |  44 ++++
 5 files changed, 559 insertions(+)
 create mode 100644 software/lte-standalone/instance.jinja2.cfg
 create mode 100755 software/lte-standalone/ltelogs.sh
 create mode 100755 software/lte-standalone/ltestart.jinja2.sh
 create mode 100644 software/lte-standalone/readme.rst
 create mode 100644 software/lte-standalone/software.cfg

diff --git a/software/lte-standalone/instance.jinja2.cfg b/software/lte-standalone/instance.jinja2.cfg
new file mode 100644
index 000000000..7cfdb0980
--- /dev/null
+++ b/software/lte-standalone/instance.jinja2.cfg
@@ -0,0 +1,172 @@
+#
+# Deploy LTE instance
+#
+[buildout]
+parts =
+  directory
+  publish-connection-parameter
+  lte-launcher
+
+eggs-directory = {{ buildout['eggs-directory'] }}
+develop-eggs-directory = {{ buildout['develop-eggs-directory'] }}
+offline = true
+
+
+[instance]
+recipe = slapos.cookbook:slapconfiguration
+computer = ${slap_connection:computer_id}
+partition = ${slap_connection:partition_id}
+url = ${slap_connection:server_url}
+key = ${slap_connection:key_file}
+cert = ${slap_connection:cert_file}
+
+configuration.frequency
+configuration.TRX
+configuration.log_size = 50M
+
+[directory]
+recipe = slapos.cookbook:mkdirectory
+home = ${buildout:directory}
+mme = ${:home}/mme
+ims = ${:home}/ims
+enb = ${:home}/enb
+etc = ${:home}/etc
+var = ${:home}/var
+run = ${:var}/run
+script = ${:etc}/run
+service = ${:etc}/service
+promise = ${:etc}/promise
+log = ${:var}/log
+
+
+### MME ############################################################################################
+[lte-mme-log]
+recipe = slapos.cookbook:wrapper
+command = {{ buildout['directory'] }}/ltelogs.sh {{ directory['log'] }}/mme.log
+destination = ${directory:script}/lte-mme-log
+mode = 0775
+pidfile = {{ directory['log'] }}/mme.log.clean
+remove_pidfile = true
+
+[lte-mme-socket]
+recipe = slapos.cookbook:wrapper
+command = mkfifo {{ directory['run'] }}/mme.sock; cat > {{ directory['run'] }}/mme.sock
+destination = ${directory:service}/lte-mme-socket
+mode = 0775
+cleanup_command = rm -f {{ directory['run'] }}/mme.sock
+
+[lte-mme-service]
+recipe = slapos.cookbook:wrapper
+command = {{ directory['mme'] }}/ltemme \\
+  {{ directory['mme'] }}/config/mme.cfg \\
+  < {{ directory['run'] }}/mme.sock \\
+wrapper-path = ${directory:script}/lte-mme
+mode = 0775
+pidfile = {{ directory['run'] }}/mme.pid
+remove_pidfile = true
+wait-for-files = 
+  {{ directory['run'] }}/mme.sock
+  {{ directory['log'] }}/mme.log.clean
+
+[lte-mme-service-log]
+# MME binary needs upon startup on its STDIN a command where it should log
+recipe = slapos.cookbook:wrapper
+command = echo "log file.rotate={{ instance['configuration.log_size'] }},file.path={{ directory['log'] }}"\\
+  > {{ directory['run'] }}/mme.sock
+destination = ${directory:script}/lte-mme-service-log
+wait-for-files = 
+  {{ directory['run'] }}/mme.sock
+  {{ directory['run'] }}/mme.pid
+
+
+### IMS ############################################################################################
+[lte-ims-service]
+recipe = slapos.cookbook:wrapper
+command = {{ directory['ims'] }}/ims \\
+  {{ directory['ims'] }}/config/ims.cfg \\
+  < {{ directory['run'] }}/ims.sock
+wrapper-path = ${directory:script}/lte-ims
+mode = 0775
+pidfile = {{ directory['run'] }}/ims.pid
+remove_pidfile = true
+wait-for-files = 
+  {{ directory['run'] }}/ims.sock
+  {{ directory['log'] }}/ims.log.clean
+
+[lte-ims-log]
+recipe = slapos.cookbook:wrapper
+command = {{ buildout['directory'] }}/ltelogs.sh {{ directory['log'] }}/ims.log
+destination = ${directory:script}/lte-ims-log
+mode = 0775
+pidfile =  {{ directory['log'] }}/ims.log.clean
+remove_pidfile = true
+
+[lte-ims-socket]
+recipe = slapos.cookbook:wrapper
+command = mkfifo {{ directory['run'] }}/ims.sock; cat > {{ directory['run'] }}/ims.sock
+destination = ${directory:service}/lte-ims-socket
+mode = 0775
+cleanup_command = rm -f {{ directory['run'] }}/ims.sock
+
+[lte-ims-service-log]
+# ims binary needs upon startup on its STDIN a command where it should log
+recipe = slapos.cookbook:wrapper
+command = echo "log file.rotate={{ instance['configuration.log_size'] }},file.path={{ directory['log'] }}"\\
+  > {{ directory['run'] }}/ims.sock
+destination = ${directory:script}/lte-ims-service-log
+wait-for-files = 
+  {{ directory['run'] }}/ims.sock
+  {{ directory['run'] }}/ims.pid
+
+[lte-register-ims-with-mme]
+recipe = slapos.cookbook:wrapper
+command = echo "imsconnect" > {{ directory['run'] }}/mme.sock; echo "t" > {{ directory['run'] }}/ims.sock; 
+destination = ${directory:script}/lte-ims-register
+wait-for-files = 
+  {{ directory['run'] }}/mme.sock
+  {{ directory['run'] }}/ims.sock
+  {{ directory['run'] }}/ims.pid
+
+
+### eNodeB (enb) ###################################################################################
+[lte-enb-service]
+recipe = slapos.cookbook:wrapper
+command = {{ directory['enb'] }}/enb \\
+  {{ directory['enb'] }}/config/enb.cfg \\
+  < {{ directory['run'] }}/enb.sock
+wrapper-path = ${directory:script}/lte-enb
+mode = 0775
+pidfile = {{ directory['run'] }}/enb.pid
+remove_pidfile = true
+wait-for-files = 
+  {{ directory['run'] }}/enb.sock
+  {{ directory['log'] }}/enb.log.clean
+
+[lte-enb-log]
+recipe = slapos.cookbook:wrapper
+command = {{ buildout['directory'] }}/ltelogs.sh {{ directory['log'] }}/enb.log
+destination = ${directory:script}/lte-enb-log
+mode = 0775
+pidfile =  {{ directory['log'] }}/enb.log.clean
+remove_pidfile = true
+
+[lte-enb-socket]
+recipe = slapos.cookbook:wrapper
+command = mkfifo {{ directory['run'] }}/enb.sock; cat > {{ directory['run'] }}/enb.sock
+destination = ${directory:service}/lte-enb-socket
+mode = 0775
+cleanup_command = rm -f {{ directory['run'] }}/enb.sock
+
+[lte-enb-service-log]
+# enb binary needs upon startup on its STDIN a command where it should log
+recipe = slapos.cookbook:wrapper
+command = echo "log file.rotate={{ instance['configuration.log_size'] }},file.path={{ directory['log'] }}"\\
+  > {{ directory['run'] }}/enb.sock; echo "t" > {{ directory['run'] }}/enb.sock
+destination = ${directory:script}/lte-enb-service-log
+wait-for-files = 
+  {{ directory['run'] }}/enb.sock
+  {{ directory['run'] }}/enb.pid
+
+
+#### continue for services MBMS and UE 
+#### using ltestart.jinja2.sh template from line 200
diff --git a/software/lte-standalone/ltelogs.sh b/software/lte-standalone/ltelogs.sh
new file mode 100755
index 000000000..b2d293ecd
--- /dev/null
+++ b/software/lte-standalone/ltelogs.sh
@@ -0,0 +1,25 @@
+#!/bin/bash
+# Copyright (C) 2012-2015 Amarisoft
+# LTE system logger version 2016-10-13
+
+# Path for multi environment support
+export PATH="$PATH:/bin/:/usr/bin/:/usr/local/bin"
+
+source /etc/ltestart.conf
+
+while [ "$1" != "" ] ; do
+
+    if [ -e "$1" ] ; then
+        # Avoid storing logs with comments only
+        HAS_LOG=$(grep -v -l "#" $1)
+        if [ "$HAS_LOG" != "" ] ; then
+            DATE=$(date -u +%Y%m%d.%H:%M:%S | sed -e "s/ /-/g")
+            FILE=$(basename $1)
+            mv $1 "${LOG_PATH}/${FILE}.${DATE}"
+        else
+            rm -f $1
+        fi
+    fi
+    shift
+done
+
diff --git a/software/lte-standalone/ltestart.jinja2.sh b/software/lte-standalone/ltestart.jinja2.sh
new file mode 100755
index 000000000..7607d57f4
--- /dev/null
+++ b/software/lte-standalone/ltestart.jinja2.sh
@@ -0,0 +1,266 @@
+#!/bin/bash
+# Copyright (C) 2012-2016 Amarisoft
+# LTE system starter version 2016-10-13
+
+##################
+# Default config #
+##################
+source {{ directory['etc'] }}/ltestart.conf
+
+
+# Redirect IO
+exec 0>&-
+exec 1>>$LOG_FILE
+exec 2>>$LOG_FILE
+
+
+# Termination
+function EndOfLTE {
+
+    echo "* Stopping LTE service"
+
+    # Quit programs and screen windows
+    for i in 0 1 2 3 ; do
+        sleep 0.2
+        {{ screen_bin }} -S lte -p $i -X stuff $'\nquit\n'
+    done
+
+    # Quit
+    for i in 0 1 2 3 ; do
+        sleep 0.2
+        {{ screen_bin }} -S lte -p $i -X stuff $'exit\n'
+    done
+
+    # Save logs
+    ltelogs.sh {{ directory['log'] }}/ims.log \
+               {{ directory['log'] }}/mme.log \
+               {{ directory['log'] }}/enb0.log \
+               {{ directory['log'] }}/mbmsgw.log
+
+    echo "* LTE service stopped"
+    exit 0
+}
+trap EndOfLTE KILL INT TERM
+
+# Send command to window
+function cmd
+{
+    win="$1"
+
+    {{ screen_bin }} -S lte -p $win -X stuff $'\n' # Empty line in case of
+
+    while [ "$2" != "" ] ; do
+        {{ screen_bin }} -S lte -p $win -X stuff "$2"$'\n'
+        shift
+    done
+}
+
+
+# Path for multi environment support
+export PATH="$PATH:/bin/:/usr/bin/:/usr/local/bin"
+
+echo "* Start LTE service"
+
+# Core dumps
+#< In production we don't really want core dumps
+#< ulimit -c unlimited
+#< echo "/tmp/core" > /proc/sys/kernel/core_pattern
+
+# Logs
+LOG_CFG="file.rotate=$LOG_SIZE,file.path=$LOG_PATH"
+
+# Init state
+MME_STATE=""
+ENB_STATE=""
+MBMS_STATE=""
+UE_STATE=""
+
+# Poll
+while [ 1 ] ; do
+
+    ##########
+    # Screen #
+    ##########
+    SCREEN=$({{ screen_bin }} -ls lte | grep -w lte)
+    if [ "$SCREEN" = "" ] ; then
+        echo "* Create screen and initialize windows"
+
+        # start a new screen session
+        {{ screen_bin }} -dm -S lte
+
+        # Add windows
+        sleep 0.1; {{ screen_bin }} -S lte -X screen
+        sleep 0.1; {{ screen_bin }} -S lte -X screen
+        sleep 0.1; {{ screen_bin }} -S lte -X screen
+
+        cmd 0 'printf "\\033k%s\\033\\\\" MME' 'clear'
+        cmd 1 'printf "\\033k%s\\033\\\\" eNB' 'clear'
+        cmd 2 'printf "\\033k%s\\033\\\\" MBMS' 'clear'
+        cmd 3 'printf "\\033k%s\\033\\\\" IMS' 'clear'
+
+        # Set HOME for UHD to find calibration files
+        cmd $ENB_WIN "export HOME={{ buildout['directory'] }}"
+    fi
+
+    # Update date
+    DATE=$(date -u +%Y%m%d-%H:%M:%S | sed -e "s/ /-/g")
+
+    S1CONNECT=0
+
+    #######
+    # MME #
+    #######
+    if [ -e "$MME_PATH/ltemme" ] ; then
+        # Init
+        if [ "$MME_STATE" != "done" ] ; then
+
+            echo "* Initialize MME with option '$MME_INIT'"
+            $MME_PATH/lte_init.sh $MME_INIT
+            if [ "$?" = "0" ] ; then
+                echo "* MME initialized"
+                MME_STATE="done"
+            else
+                # Configure at least locally to allow LTE local connections
+                if [ "$MME_STATE" != "LOCAL" ] ; then
+                    echo "* Initialize MME with local interface"
+                    $MME_PATH/lte_init.sh lo
+                    MME_STATE="LOCAL"
+                fi
+            fi
+        fi
+
+        MME=$(pgrep ltemme)
+        if [ "$MME" = "" ] ; then
+
+            # "MME not running, start it here"
+            echo "* Starting MME"
+            ltelogs.sh {{ directory['log'] }}/mme.log
+            cmd $MME_WIN "cd $MME_PATH" "./ltemme config/mme.cfg" "log $LOG_CFG"
+
+            # Wait for MME to start
+            sleep 0.5
+
+            S1CONNECT=1
+        fi
+
+        #######
+        # IMS #
+        #######
+        if [ -e "$IMS_PATH/lteims" ] ; then
+            IMS=$(pgrep lteims)
+            if [ "$IMS" = "" ]; then
+                # IMS not running, start it here
+                echo "* Starting IMS"
+                ltelogs.sh {{ directory['log'] }}/ims.log
+                cmd $IMS_WIN "cd $IMS_PATH" "./lteims config/ims.cfg" "log $LOG_CFG"
+
+                sleep 0.5
+                cmd $MME_WIN "imsconnect"
+                cmd $IMS_WIN "t"
+            fi
+        fi
+    fi
+
+    #######
+    # eNB #
+    #######
+    if [ -e "$ENB_PATH/lteenb" ] ; then
+        # Init
+        if [ "$ENB_STATE" != "done" ] ; then
+            echo "* Initialize eNB with option '$ENB_INIT'"
+            $ENB_PATH/lte_init.sh $ENB_INIT
+            if [ "$?" = "0" ] ; then
+                ENB_STATE="done"
+            fi
+        fi
+
+        ENB=$(pgrep lteenb)
+        if [ "$ENB" = "" ]; then
+            # Test if Radio head is running to start eNB
+            if [ -e "${ENB_PATH}/config/rf_driver/rrh_check.sh" ] ; then
+                ${ENB_PATH}/config/rf_driver/rrh_check.sh $RRH_CFG
+                RRH="$?"
+            else
+                RRH="0"
+            fi
+            if [ "$RRH" = "0" ] ; then
+                # "eNodeB not running, start it here"
+                echo "* Starting eNB"
+                ltelogs.sh {{ directory['log'] }}/enb0.log
+                cmd $ENB_WIN "cd $ENB_PATH" "./lteenb config/enb.cfg" "log $LOG_CFG" "t"
+            fi
+
+        else
+            # Force S1 connection ?
+            if [ "$S1CONNECT" = "1" ] ; then
+                cmd "ENB" "cd $ENB_PATH" "s1connect" "t"
+            fi
+        fi
+    fi
+
+    ########
+    # MBMS #
+    ########
+    if [ -d "$MBMS_PATH" ] ; then
+        # Init
+        if [ "$MBMS_STATE" != "done" ] ; then
+            echo "* Initialize MBMSGW with option '$MBMS_INIT'"
+            $MBMS_PATH/lte_init.sh $MBMS_INIT
+            if [ "$?" = "0" ] ; then
+                MBMS_STATE="done"
+            fi
+        fi
+
+        MBMS=$(pgrep ltembmsgw)
+        if [ "$MBMS" = "" ]; then
+            # MBMS not running, start it here
+            echo "* Starting MBMSGW"
+            ltelogs.sh {{ directory['log'] }}/mbmsgw.log
+            cmd $MBMS_WIN "cd $MBMS_PATH" "./ltembmsgw config/mbmsgw.cfg" "log $LOG_CFG"
+        fi
+    fi
+
+    ######
+    # UE #
+    ######
+    if [ -e "$UE_PATH/lteue" ] ; then
+        # Init
+        if [ "$UE_STATE" != "done" ] ; then
+            echo "* Initialize UE"
+            if [ -e "${UE_PATH}/config/rf_driver/rrh_check.sh" ] ; then
+                ${UE_PATH}/config/rf_driver/rrh_check.sh $RRH_CFG
+            fi
+            $UE_PATH/lte_init.sh $UE_INIT
+            if [ "$?" = "0" ] ; then
+                UE_STATE="done"
+            fi
+        fi
+    fi
+
+    # Remove core dumps older than 30min
+    find /tmp/ -name "core*" -mmin 30 | xargs rm -f
+
+    # Compress logs if needed
+    if [ "$LOG_GZIP" = "1" ] ; then
+        LIST=$(find $LOG_PATH -type f -name "*log*" | grep -v "gz$")
+        for i in $LIST ; do
+            gzip $i
+            break; # One by one as it may last a while
+        done
+    fi
+
+    # Remove logs if too much
+    while [ $(du -ks $LOG_PATH | cut -d $'\t' -f1) -gt $LOG_PERSISTENT_SIZE ] ; do
+        FILES=$(ls -a $LOG_PATH)
+        for i in $FILES ; do
+            if [ -f $LOG_PATH/$i ] ; then
+                rm $LOG_PATH/$i;
+                break
+            fi
+        done
+    done
+
+    # 5s polling
+    sleep 5
+
+done
diff --git a/software/lte-standalone/readme.rst b/software/lte-standalone/readme.rst
new file mode 100644
index 000000000..47f01b8d5
--- /dev/null
+++ b/software/lte-standalone/readme.rst
@@ -0,0 +1,52 @@
+LTE eNodeB software release
+###########################
+
+This is a try for standalone unprivileged run of Amarisoft's LTE stack.
+
+The successful setup consists of
+
+  1. Ansible: compilation and installation of necessary kernel module: lte_trx_sdr
+  2. Initialization run as root (either by "format" or manualy)
+  3. Deployment of this Software Release.
+  
+Original install.sh script was replaced by software.cfg and instance.cfg to
+setup the paths so that we can run more instances on one machine.
+
+
+slapos.cookbook:wrapper modification
+------------------------------------
+
+This instance.cfg is using updated slapos.cookbook:wrapper with ``remove_pidfile`` 
+and ``cleanup_command``. Both options add functionality to generated ``sh`` script
+using ``trap`` for INT, TERM and KILL signal. 
+
+ - ``remove_pidfile`` removes pidfile upon exit,
+ - ``cleanup_command``runs arbitrary cleanup command specified by the user.
+
+
+instance.cfg explained
+----------------------
+instance.cfg is rather complicated because Amarisoft LTE stack consists of 4 binaries
+
+ * **lteenb** - eNodeB software is the server accepting connection from UI (user interfaces)
+ * **ltemme** - base (core) network which handles orchestration of eNodeBs in case UI switches from
+   one to another
+ * **lteims** - no idea
+ * **ltembmsgw** - no idea
+ 
+Those binaries are started in foreground, originaly in screen. They *communicate with each other*
+using ``stdin`` based on their inner state. Thus we use *named pipes* to enable the inteprocess
+communication.
+
+We don't want the binaries inside one screen because then we cannot easily control their resource
+usage and we will not see them separately inside webrunner or have separate access to their services.
+
+Every binary expects "log" command after startup. Let's show it on ``mme`` binary.
+
+ # ``lte-mme-log`` cleans up old logs
+ # ``lte-mme-socket`` opens a socket using mkfifo
+ # ``lte-mme-service`` launches the actual mme binary when the socket is available
+ # ``lte-mme-service-log`` writes the "log" command into mme binary when it launches
+
+One good example is script ``lte-register-ims-with-mme`` which registers newly started ``ims``
+within ``mme`` using mentioned socket.
diff --git a/software/lte-standalone/software.cfg b/software/lte-standalone/software.cfg
new file mode 100644
index 000000000..6f8bf8dbb
--- /dev/null
+++ b/software/lte-standalone/software.cfg
@@ -0,0 +1,44 @@
+[buildout]
+
+extends =
+  ../../stack/slapos.cfg
+  ../../component/gzip/buildout.cfg
+#  ../../component/apache-php/buildout.cfg 
+
+parts =
+  slapos-cookbook
+  instance-profile
+#  apache-php
+  gzip
+
+[instance-profile]
+recipe = slapos.recipe.template:jinja2
+template = ${:_profile_base_location_}/instance.jinja2.cfg
+rendered = ${buildout:directory}/instance.cfg
+mode = 0644
+extensions = jinja2.ext.do
+context =
+  section buildout buildout
+
+[copy-to-instance]
+recipe  = slapos.recipe.build:download
+url     = ${:_profile_base_location_}/${:_buildout_section_name_}
+destination = ${buildout:directory}/${:_buildout_section_name_}
+
+[ltestart.jinja2.conf]
+<= copy-to-instance
+
+[lteenb-linux-2016-10-13.tar.gz]
+<= copy-to-instance
+
+[ltembmsgw-linux-2016-10-13.tar.gz]
+<= copy-to-instance
+
+[ltemme-linux-2016-10-13.tar.gz]
+<= copy-to-instance
+
+# lteots-linux-2016-10-13.tar.gz
+# is just install utilities which we will not use
+
+# trx_sdr-linux-2016-10-13.tar.gz
+# is a kernel module thus needs to be installed in advance
-- 
2.30.9