core-extra/tasks.py

252 lines
7.1 KiB
Python

import os
import sys
from enum import Enum
from invoke import task, Context
DAEMON_DIR: str = "daemon"
VCMD_DIR: str = "netns"
GUI_DIR: str = "gui"
class OsName(Enum):
UBUNTU = "ubuntu"
CENTOS = "centos"
class OsLike(Enum):
DEBIAN = "debian"
REDHAT = "rhel fedora"
class OsInfo:
def __init__(self, name: OsName, like: OsLike, version: float) -> None:
self.name: OsName = name
self.like: OsLike = like
self.version: float = version
def get_python(c: Context) -> str:
with c.cd(DAEMON_DIR):
venv = c.run("poetry env info -p", hide=True).stdout.strip()
return os.path.join(venv, "bin", "python")
def get_pytest(c: Context) -> str:
with c.cd(DAEMON_DIR):
venv = c.run("poetry env info -p", hide=True).stdout.strip()
return os.path.join(venv, "bin", "pytest")
def get_os() -> OsInfo:
d = {}
with open("/etc/os-release", "r") as f:
for line in f.readlines():
line = line.strip()
if not line:
continue
key, value = line.split("=")
d[key] = value.strip('"')
name_value = d["ID"]
like_value = d["ID_LIKE"]
version_value = d["VERSION_ID"]
try:
name = OsName(name_value)
like = OsLike(like_value)
version = float(version_value)
except ValueError:
print(
f"unsupported os({name_value}) like({like_value}) version({version_value}"
)
sys.exit(1)
return OsInfo(name, like, version)
def install_system(c: Context, os_info: OsInfo, hide: bool) -> None:
print("installing system dependencies...")
if os_info.like == OsLike.DEBIAN:
c.run(
"sudo apt install -y automake pkg-config gcc libev-dev ebtables iproute2 "
"ethtool tk python3-tk",
hide=hide
)
elif os_info.like == OsLike.REDHAT:
c.run(
"sudo yum install -y automake pkgconf-pkg-config gcc gcc-c++ libev-devel "
"iptables-ebtables iproute python3-devel python3-tkinter tk ethtool make "
"kernel-modules-extra",
hide=hide
)
# centos 8+ does not support netem by default
if os_info.name == OsName.CENTOS and os_info.version >= 8:
c.run("sudo yum install -y kernel-modules-extra", hide=hide)
if not c.run("sudo modprobe sch_netem", warn=True, hide=hide):
print("ERROR: you need to install the latest kernel")
print("run the following, restart, and try again")
print("sudo yum update")
sys.exit(1)
# attempt to setup legacy ebtables when an nftables based version is found
r = c.run("ebtables -V", hide=hide)
if "nf_tables" in r.stdout:
if not c.run(
"sudo update-alternatives --set ebtables /usr/sbin/ebtables-legacy",
warn=True,
hide=hide
):
print(
"WARNING: unable to setup required ebtables-legacy, WLAN will not work"
)
def install_grpcio(c: Context, hide: bool) -> None:
print("installing grpcio-tools...")
c.run(
"python3 -m pip install --user grpcio==12.7.2 grpcio-tools==12.7.2", hide=hide
)
def build(c: Context, os_info: OsInfo, hide: bool) -> None:
print("building core...")
c.run("./bootstrap.sh", hide=hide)
prefix = "--prefix=/usr" if os_info.like == OsLike.REDHAT else ""
c.run(f"./configure {prefix}", hide=hide)
c.run("make -j$(nproc)", hide=hide)
def install_core(c: Context, hide: bool) -> None:
print("installing core vcmd...")
with c.cd(VCMD_DIR):
c.run("sudo make install", hide=hide)
print("installing core gui...")
with c.cd(GUI_DIR):
c.run("sudo make install", hide=hide)
def install_poetry(c: Context, dev: bool, hide: bool) -> None:
print("installing poetry...")
c.run("pipx install poetry", hide=hide)
args = "" if dev else "--no-dev"
with c.cd(DAEMON_DIR):
print("installing core environment using poetry...")
c.run(f"poetry install {args}", hide=hide)
if dev:
c.run("poetry run pre-commit install", hide=hide)
def install_ospf_mdr(c: Context, os_info: OsInfo, hide: bool) -> None:
if c.run("which zebra", warn=True, hide=hide):
print("quagga already installed, skipping ospf mdr")
return
print("installing ospf mdr dependencies...")
if os_info.like == OsLike.DEBIAN:
c.run("sudo apt install -y libtool gawk libreadline-dev git", hide=hide)
elif os_info.like == OsLike.REDHAT:
c.run("sudo yum install -y libtool gawk readline-devel git", hide=hide)
print("cloning ospf mdr...")
clone_dir = "/tmp/ospf-mdr"
c.run(
f"git clone https://github.com/USNavalResearchLaboratory/ospf-mdr {clone_dir}",
hide=hide
)
with c.cd(clone_dir):
print("building ospf mdr...")
c.run("./bootstrap.sh", hide=hide)
c.run(
"./configure --disable-doc --enable-user=root --enable-group=root "
"--with-cflags=-ggdb --sysconfdir=/usr/local/etc/quagga --enable-vtysh "
"--localstatedir=/var/run/quagga",
hide=hide
)
c.run("make -j$(nproc)", hide=hide)
print("installing ospf mdr...")
c.run("sudo make install", hide=hide)
@task
def install(c, dev=False, verbose=False):
"""
install core
"""
hide = not verbose
os_info = get_os()
install_system(c, os_info, hide)
install_grpcio(c, hide)
build(c, os_info, hide)
install_core(c, hide)
install_poetry(c, dev, hide)
install_ospf_mdr(c, os_info, hide)
print("please open a new terminal or re-login to leverage invoke for running core")
print("# run daemon")
print("inv daemon")
print("# run gui")
print("inv gui")
@task
def daemon(c):
"""
start core-daemon
"""
python = get_python(c)
with c.cd(DAEMON_DIR):
c.run(
f"sudo {python} scripts/core-daemon "
"-f data/core.conf -l data/logging.conf",
pty=True
)
@task
def gui(c):
"""
start core-pygui
"""
with c.cd(DAEMON_DIR):
c.run("poetry run scripts/core-pygui", pty=True)
@task
def cli(c, args):
"""
run core-cli used to query and modify a running session
"""
with c.cd(DAEMON_DIR):
c.run(f"poetry run scripts/core-cli {args}", pty=True)
@task
def cleanup(c):
"""
run core-cleanup removing leftover core nodes, bridges, directories
"""
print("running core-cleanup...")
c.run(f"sudo daemon/scripts/core-cleanup", pty=True)
@task
def test(c):
"""
run core tests
"""
pytest = get_pytest(c)
with c.cd(DAEMON_DIR):
c.run(f"sudo {pytest} -v --lf -x tests", pty=True)
@task
def test_mock(c):
"""
run core tests using mock to avoid running as sudo
"""
with c.cd(DAEMON_DIR):
c.run("poetry run pytest -v --mock --lf -x tests", pty=True)
@task
def test_emane(c):
"""
run core emane tests
"""
pytest = get_pytest(c)
with c.cd(DAEMON_DIR):
c.run(f"{pytest} -v --lf -x tests/emane", pty=True)