Skip to content

Commit

Permalink
Installs the srsran snap instead of building the project from source (#…
Browse files Browse the repository at this point in the history
  • Loading branch information
gruyaume authored Mar 21, 2023
1 parent bc31def commit 7e8aa10
Show file tree
Hide file tree
Showing 10 changed files with 62 additions and 327 deletions.
4 changes: 2 additions & 2 deletions .github/workflows/codeql-analysis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,10 @@ name: "CodeQL"

on:
push:
branches: [ "master" ]
branches: [ "main" ]
pull_request:
# The branches below must be a subset of the branches above
branches: [ "master" ]
branches: [ "main" ]
schedule:
- cron: '25 13 * * 0'

Expand Down
6 changes: 3 additions & 3 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ on:
jobs:
lint-report:
name: Lint report
runs-on: ubuntu-20.04
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v2
- name: Install tox
Expand All @@ -16,7 +16,7 @@ jobs:

static-analysis:
name: Static analysis
runs-on: ubuntu-20.04
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v2
- name: Install tox
Expand All @@ -26,7 +26,7 @@ jobs:

unit-tests-with-coverage:
name: Unit tests
runs-on: ubuntu-20.04
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v2
- name: Install tox
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/upload.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ jobs:
- name: Checkout
uses: actions/checkout@v2

- uses: canonical/charming-actions/upload-charm@2.0.0-rc
- uses: canonical/charming-actions/upload-charm@2.2.2
with:
credentials: "${{ secrets.CHARMCRAFT_AUTH }}"
github-token: "${{ secrets.GITHUB_TOKEN }}"
Expand Down
2 changes: 1 addition & 1 deletion config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ options:
type: string
description: |
NAS Access Point Name (APN).
default: oai.ipv4
default: default
ue-device-name:
type: string
description: |
Expand Down
2 changes: 0 additions & 2 deletions metadata.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,6 @@ description: |
Open-source 4G EnodeB and User emulators developed by [Software Radio Systems (SRS)](https://www.srslte.com/).
summary: |
Open-source 4G EnodeB and User emulators developed by [Software Radio Systems (SRS)](https://www.srslte.com/).
series:
- focal
requires:
lte-core:
Expand Down
4 changes: 2 additions & 2 deletions requirements.txt
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
ops == 1.5.2
ops
psutil
netifaces
netaddr
jinja2
jsonschema
jsonschema
107 changes: 23 additions & 84 deletions src/charm.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,9 @@
# Copyright 2022 Canonical Ltd.
# See LICENSE file for licensing details.

"""Charm for the SRS RAN simulator."""
"""Charm for the srsRAN simulator."""

import logging
import os
import shutil
from typing import Optional, Union

from charms.lte_core_interface.v0.lte_core_interface import (
Expand All @@ -25,10 +23,7 @@
from ops.model import ActiveStatus, BlockedStatus, MaintenanceStatus, WaitingStatus

from utils import (
copy_files,
get_iface_ip_address,
git_clone,
install_apt_packages,
ip_from_default_iface,
service_active,
service_enable,
Expand All @@ -41,62 +36,21 @@

logger = logging.getLogger(__name__)

APT_REQUIREMENTS = [
"git",
"libzmq3-dev",
"cmake",
"build-essential",
"libmbedtls-dev",
"libboost-program-options-dev",
"libsctp-dev",
"libconfig++-dev",
"libfftw3-dev",
"net-tools",
]

GIT_REPO = "https://github.com/srsLTE/srsLTE.git"
GIT_REPO_TAG = "release_20_10"

SRC_PATH = "/srsLTE"
BUILD_PATH = "/build"
CONFIG_PATH = "/config"
SERVICE_PATH = "/service"

CONFIG_PATHS = {
"enb": f"{CONFIG_PATH}/enb.conf",
"drb": f"{CONFIG_PATH}/drb.conf",
"rr": f"{CONFIG_PATH}/rr.conf",
"sib": f"{CONFIG_PATH}/sib.conf",
"sib.mbsfn": f"{CONFIG_PATH}/sib.mbsfn.conf",
"ue": f"{CONFIG_PATH}/ue.conf",
}

CONFIG_ORIGIN_PATHS = {
"enb": f"{SRC_PATH}/srsenb/enb.conf.example",
"drb": f"{SRC_PATH}/srsenb/drb.conf.example",
"rr": f"{SRC_PATH}/srsenb/rr.conf.example",
"sib": f"{SRC_PATH}/srsenb/sib.conf.example",
"sib.mbsfn": f"{SRC_PATH}/srsenb/sib.conf.mbsfn.example",
"ue": f"{SRC_PATH}/srsue/ue.conf.example",
}
CONFIG_PATH = "/snap/srsran/current/config"

SRS_ENB_SERVICE = "srsenb"
SRS_ENB_BINARY = f"{BUILD_PATH}/srsenb/src/srsenb"
SRS_ENB_SERVICE_TEMPLATE = "./templates/srsenb.service"
SRS_ENB_SERVICE_PATH = "/etc/systemd/system/srsenb.service"

SRS_UE_SERVICE = "srsue"
SRS_UE_BINARY = f"{BUILD_PATH}/srsue/src/srsue"
SRS_UE_SERVICE_TEMPLATE = "./templates/srsue.service"
SRS_UE_SERVICE_PATH = "/etc/systemd/system/srsue.service"

SRS_ENB_UE_BUILD_COMMAND = f"cd {BUILD_PATH} && cmake {SRC_PATH} && make -j `nproc` srsenb srsue"

WAIT_FOR_UE_IP_TIMEOUT = 10


class SrsLteCharm(CharmBase):
"""srsRAN LTE charm."""
class SrsRANCharm(CharmBase):
"""srsRAN charm."""

def __init__(self, *args):
"""Observes various events."""
Expand All @@ -123,18 +77,13 @@ def _on_install(self, _: InstallEvent) -> None:
if not self.unit.is_leader():
return
self.unit.status = MaintenanceStatus("Installing srsRAN")
install_apt_packages(APT_REQUIREMENTS)
self._reset_environment()
self._build_srsran()
copy_files(origin=CONFIG_ORIGIN_PATHS, destination=CONFIG_PATHS)
self._install_srsran()

def _on_stop(self, _: StopEvent) -> None:
"""Triggered on stop event."""
if not self.unit.is_leader():
return
self._reset_environment()
service_stop(SRS_ENB_SERVICE)
self.unit.status = BlockedStatus("Unit is down, service has stopped")
self._uninstall_srsran()

def _on_config_changed(self, _: Union[ConfigChangedEvent, LTECoreAvailableEvent]) -> None:
"""Triggered on config changed event."""
Expand Down Expand Up @@ -211,10 +160,16 @@ def _configure_srsenb_service(self) -> None:
)

@staticmethod
def _build_srsran() -> None:
"""Build srsRAN."""
git_clone(GIT_REPO, output_folder=SRC_PATH, branch=GIT_REPO_TAG, depth=1)
shell(SRS_ENB_UE_BUILD_COMMAND)
def _install_srsran() -> None:
"""Installs srsRAN snap."""
shell("snap install srsran --edge --devmode")
logger.info("Installed srsRAN snap")

@staticmethod
def _uninstall_srsran() -> None:
"""Removes srsRAN snap."""
shell("snap remove srsran --purge")
logger.info("Removed srsRAN snap")

def _configure_srsue_service(
self, ue_usim_imsi: str, ue_usim_k: str, ue_usim_opc: str
Expand Down Expand Up @@ -260,7 +215,7 @@ def _configure_service(

def _get_srsenb_command(self) -> str:
"""Returns srs enb command."""
srsenb_command = [SRS_ENB_BINARY]
srsenb_command = ["/snap/bin/srsran.srsenb"]
srsenb_command.extend(
(
f"--enb.mme_addr={self._mme_address}",
Expand All @@ -273,10 +228,9 @@ def _get_srsenb_command(self) -> str:
f'--enb.name={self.config.get("enb-name")}',
f'--enb.mcc={self.config.get("enb-mcc")}',
f'--enb.mnc={self.config.get("enb-mnc")}',
f'--enb_files.rr_config={CONFIG_PATHS["rr"]}',
f'--enb_files.sib_config={CONFIG_PATHS["sib"]}',
f'--enb_files.drb_config={CONFIG_PATHS["drb"]}',
CONFIG_PATHS["enb"],
f"--enb_files.rr_config={CONFIG_PATH}/rr.conf",
f"--enb_files.sib_config={CONFIG_PATH}/sib.conf",
f"{CONFIG_PATH}/enb.conf",
f'--rf.device_name={self.config.get("enb-rf-device-name")}',
f'--rf.device_args={self.config.get("enb-rf-device-args")}',
)
Expand All @@ -285,7 +239,7 @@ def _get_srsenb_command(self) -> str:

def _get_srsue_command(self, ue_usim_imsi: str, ue_usim_k: str, ue_usim_opc: str) -> str:
"""Returns srs ue command."""
srsue_command = [SRS_UE_BINARY]
srsue_command = ["sudo", "/snap/bin/srsran.srsue"]
srsue_command.extend(
(
f"--usim.imsi={ue_usim_imsi}",
Expand All @@ -299,26 +253,11 @@ def _get_srsue_command(self, ue_usim_imsi: str, ue_usim_k: str, ue_usim_opc: str
f'--nas.apn={self.config.get("ue-nas-apn")}',
f'--rf.device_name={self.config.get("ue-device-name")}',
f'--rf.device_args={self.config.get("ue-device-args")}',
CONFIG_PATHS["ue"],
f"{CONFIG_PATH}/ue.conf",
)
)
return " ".join(srsue_command)

@staticmethod
def _reset_environment() -> None:
"""Resets environment.
Remove old folders (if they exist) and create needed ones.
"""
shutil.rmtree(SRC_PATH, ignore_errors=True)
shutil.rmtree(BUILD_PATH, ignore_errors=True)
shutil.rmtree(CONFIG_PATH, ignore_errors=True)
shutil.rmtree(SERVICE_PATH, ignore_errors=True)
os.mkdir(SRC_PATH)
os.mkdir(BUILD_PATH)
os.mkdir(CONFIG_PATH)
os.mkdir(SERVICE_PATH)

@property
def _mme_address(self) -> Optional[str]:
"""Returns the ipv4 address of the mme interface.
Expand Down Expand Up @@ -346,4 +285,4 @@ def _bind_address(self) -> Optional[str]:


if __name__ == "__main__":
main(SrsLteCharm)
main(SrsRANCharm)
32 changes: 2 additions & 30 deletions src/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,14 @@
"""Set of utils functions used in charm."""

import logging
import shutil
import subprocess
import time
from subprocess import CalledProcessError
from typing import Callable, Dict, List, Optional
from typing import Callable, List, Optional

import netifaces # type: ignore[import]
from netaddr import IPAddress, IPNetwork # type: ignore[import]
from netaddr.core import AddrFormatError # type: ignore[import]
from netifaces import AF_INET

logger = logging.getLogger(__name__)

Expand All @@ -27,39 +25,13 @@ def service_active(service_name: str) -> bool:
return False


def install_apt_packages(package_list: List[str]) -> None:
"""Installs a given list of packages."""
package_list_str = " ".join(package_list)
shell("sudo apt -qq update")
shell(f"sudo apt -y install {package_list_str}")
logger.info(f"Installed packages: {package_list_str}")


def git_clone(
repo: str,
output_folder: str,
branch: str,
depth: int,
) -> None:
"""Runs git clone of a given repo."""
shell(f"git clone --branch={branch} --depth={depth} {repo} {output_folder}")
logger.info("Cloned git repository")


def shell(command: str) -> str:
"""Runs a shell command."""
response = subprocess.run(command, shell=True, stdout=subprocess.PIPE, encoding="utf-8")
response.check_returncode()
return response.stdout


def copy_files(origin: Dict[str, str], destination: Dict[str, str]) -> None:
"""Copy files from source to destination."""
for config, origin_path in origin.items():
destination_path = destination[config]
shutil.copy(origin_path, destination_path)


def get_local_ipv4_networks() -> List[IPNetwork]:
"""Returns list of IPv4 networks."""
networks = []
Expand Down Expand Up @@ -146,7 +118,7 @@ def get_iface_ip_address(iface: str) -> Optional[str]:
str: UE's IP address.
"""
try:
return netifaces.ifaddresses(iface)[AF_INET][0]["addr"]
return netifaces.ifaddresses(iface)[netifaces.AF_INET][0]["addr"]
except ValueError:
logging.error(f"Could not get IP address. {iface} is not a valid interface.")
return None
Expand Down
2 changes: 1 addition & 1 deletion templates/srsue.service
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ Type=simple
Restart=always
RestartSec=1
ExecStart={{ command }}
User=root
User=ubuntu
KillSignal=SIGINT
TimeoutStopSec=10
ExecStopPost=service srsenb restart
Expand Down
Loading

0 comments on commit 7e8aa10

Please sign in to comment.