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