2020-07-13 21:14:13 +01:00
|
|
|
import inspect
|
2020-07-10 07:01:28 +01:00
|
|
|
import os
|
2020-07-10 16:32:47 +01:00
|
|
|
import sys
|
|
|
|
from enum import Enum
|
2020-07-13 21:14:13 +01:00
|
|
|
from pathlib import Path
|
|
|
|
from tempfile import NamedTemporaryFile
|
2020-07-10 07:01:28 +01:00
|
|
|
|
2020-07-10 16:32:47 +01:00
|
|
|
from invoke import task, Context
|
2020-06-02 22:48:57 +01:00
|
|
|
|
2020-07-10 16:32:47 +01:00
|
|
|
DAEMON_DIR: str = "daemon"
|
|
|
|
VCMD_DIR: str = "netns"
|
|
|
|
GUI_DIR: str = "gui"
|
2020-07-10 07:01:28 +01:00
|
|
|
|
|
|
|
|
2020-07-10 16:32:47 +01:00
|
|
|
class OsName(Enum):
|
|
|
|
UBUNTU = "ubuntu"
|
|
|
|
CENTOS = "centos"
|
|
|
|
|
|
|
|
|
|
|
|
class OsLike(Enum):
|
|
|
|
DEBIAN = "debian"
|
2020-07-10 18:45:03 +01:00
|
|
|
REDHAT = "rhel fedora"
|
2020-07-10 16:32:47 +01:00
|
|
|
|
|
|
|
|
|
|
|
class OsInfo:
|
2020-07-10 18:45:03 +01:00
|
|
|
def __init__(self, name: OsName, like: OsLike, version: float) -> None:
|
2020-07-10 16:32:47 +01:00
|
|
|
self.name: OsName = name
|
|
|
|
self.like: OsLike = like
|
2020-07-10 18:45:03 +01:00
|
|
|
self.version: float = version
|
2020-07-10 16:32:47 +01:00
|
|
|
|
|
|
|
|
2020-07-13 21:48:27 +01:00
|
|
|
def get_python(c: Context, warn: bool = False) -> str:
|
2020-07-10 07:01:28 +01:00
|
|
|
with c.cd(DAEMON_DIR):
|
2020-07-13 21:48:27 +01:00
|
|
|
r = c.run("poetry env info -p", warn=warn, hide=True)
|
|
|
|
if r.ok:
|
|
|
|
venv = r.stdout.strip()
|
|
|
|
return os.path.join(venv, "bin", "python")
|
|
|
|
else:
|
|
|
|
return ""
|
2020-07-10 07:01:28 +01:00
|
|
|
|
|
|
|
|
2020-07-10 16:32:47 +01:00
|
|
|
def get_pytest(c: Context) -> str:
|
2020-07-10 07:01:28 +01:00
|
|
|
with c.cd(DAEMON_DIR):
|
|
|
|
venv = c.run("poetry env info -p", hide=True).stdout.strip()
|
|
|
|
return os.path.join(venv, "bin", "pytest")
|
|
|
|
|
|
|
|
|
2020-07-10 16:32:47 +01:00
|
|
|
def get_os() -> OsInfo:
|
2020-07-10 07:01:28 +01:00
|
|
|
d = {}
|
|
|
|
with open("/etc/os-release", "r") as f:
|
|
|
|
for line in f.readlines():
|
|
|
|
line = line.strip()
|
2020-07-10 18:39:14 +01:00
|
|
|
if not line:
|
|
|
|
continue
|
2020-07-10 07:01:28 +01:00
|
|
|
key, value = line.split("=")
|
2020-07-10 16:32:47 +01:00
|
|
|
d[key] = value.strip('"')
|
|
|
|
name_value = d["ID"]
|
|
|
|
like_value = d["ID_LIKE"]
|
2020-07-10 18:45:03 +01:00
|
|
|
version_value = d["VERSION_ID"]
|
2020-07-10 16:32:47 +01:00
|
|
|
try:
|
|
|
|
name = OsName(name_value)
|
|
|
|
like = OsLike(like_value)
|
2020-07-10 18:45:03 +01:00
|
|
|
version = float(version_value)
|
2020-07-10 16:32:47 +01:00
|
|
|
except ValueError:
|
2020-07-10 18:45:03 +01:00
|
|
|
print(
|
|
|
|
f"unsupported os({name_value}) like({like_value}) version({version_value}"
|
|
|
|
)
|
2020-07-10 16:32:47 +01:00
|
|
|
sys.exit(1)
|
|
|
|
return OsInfo(name, like, version)
|
|
|
|
|
|
|
|
|
2020-07-10 16:51:40 +01:00
|
|
|
def install_system(c: Context, os_info: OsInfo, hide: bool) -> None:
|
2020-07-10 07:01:28 +01:00
|
|
|
print("installing system dependencies...")
|
2020-07-10 16:32:47 +01:00
|
|
|
if os_info.like == OsLike.DEBIAN:
|
2020-07-10 07:01:28 +01:00
|
|
|
c.run(
|
|
|
|
"sudo apt install -y automake pkg-config gcc libev-dev ebtables iproute2 "
|
2020-07-10 19:20:27 +01:00
|
|
|
"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 "
|
2020-07-11 00:50:37 +01:00
|
|
|
"iptables-ebtables iproute python3-devel python3-tkinter tk ethtool make "
|
|
|
|
"kernel-modules-extra",
|
2020-07-10 19:20:27 +01:00
|
|
|
hide=hide
|
2020-07-10 07:01:28 +01:00
|
|
|
)
|
2020-07-11 00:50:37 +01:00
|
|
|
# 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)
|
2020-07-11 01:56:30 +01:00
|
|
|
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)
|
2020-07-11 00:50:37 +01:00
|
|
|
# attempt to setup legacy ebtables when an nftables based version is found
|
2020-07-10 20:41:39 +01:00
|
|
|
r = c.run("ebtables -V", hide=hide)
|
|
|
|
if "nf_tables" in r.stdout:
|
2020-07-11 00:50:37 +01:00
|
|
|
if not c.run(
|
2020-07-10 20:41:39 +01:00
|
|
|
"sudo update-alternatives --set ebtables /usr/sbin/ebtables-legacy",
|
2020-07-11 00:50:37 +01:00
|
|
|
warn=True,
|
2020-07-10 20:41:39 +01:00
|
|
|
hide=hide
|
2020-07-11 00:50:37 +01:00
|
|
|
):
|
2020-07-11 01:57:10 +01:00
|
|
|
print(
|
|
|
|
"WARNING: unable to setup required ebtables-legacy, WLAN will not work"
|
|
|
|
)
|
2020-07-10 16:32:47 +01:00
|
|
|
|
|
|
|
|
2020-07-10 19:20:27 +01:00
|
|
|
def install_grpcio(c: Context, hide: bool) -> None:
|
2020-07-10 07:01:28 +01:00
|
|
|
print("installing grpcio-tools...")
|
2020-07-10 20:09:04 +01:00
|
|
|
c.run(
|
2020-07-13 17:01:55 +01:00
|
|
|
"python3 -m pip install --user grpcio==1.27.2 grpcio-tools==1.27.2", hide=hide
|
2020-07-10 20:09:04 +01:00
|
|
|
)
|
2020-07-10 16:32:47 +01:00
|
|
|
|
|
|
|
|
2020-07-10 20:20:27 +01:00
|
|
|
def build(c: Context, os_info: OsInfo, hide: bool) -> None:
|
2020-07-10 07:01:28 +01:00
|
|
|
print("building core...")
|
2020-07-10 16:51:40 +01:00
|
|
|
c.run("./bootstrap.sh", hide=hide)
|
2020-07-10 20:20:27 +01:00
|
|
|
prefix = "--prefix=/usr" if os_info.like == OsLike.REDHAT else ""
|
|
|
|
c.run(f"./configure {prefix}", hide=hide)
|
|
|
|
c.run("make -j$(nproc)", hide=hide)
|
2020-07-10 16:32:47 +01:00
|
|
|
|
|
|
|
|
2020-07-10 16:51:40 +01:00
|
|
|
def install_core(c: Context, hide: bool) -> None:
|
2020-07-10 17:55:30 +01:00
|
|
|
print("installing core vcmd...")
|
2020-07-10 07:01:28 +01:00
|
|
|
with c.cd(VCMD_DIR):
|
2020-07-10 16:51:40 +01:00
|
|
|
c.run("sudo make install", hide=hide)
|
2020-07-10 17:55:30 +01:00
|
|
|
print("installing core gui...")
|
2020-07-10 07:01:28 +01:00
|
|
|
with c.cd(GUI_DIR):
|
2020-07-10 16:51:40 +01:00
|
|
|
c.run("sudo make install", hide=hide)
|
2020-07-10 16:32:47 +01:00
|
|
|
|
|
|
|
|
2020-07-10 16:51:40 +01:00
|
|
|
def install_poetry(c: Context, dev: bool, hide: bool) -> None:
|
2020-07-10 07:01:28 +01:00
|
|
|
print("installing poetry...")
|
2020-07-10 16:51:40 +01:00
|
|
|
c.run("pipx install poetry", hide=hide)
|
2020-07-10 16:32:47 +01:00
|
|
|
args = "" if dev else "--no-dev"
|
2020-07-10 07:01:28 +01:00
|
|
|
with c.cd(DAEMON_DIR):
|
|
|
|
print("installing core environment using poetry...")
|
2020-07-10 16:51:40 +01:00
|
|
|
c.run(f"poetry install {args}", hide=hide)
|
2020-07-10 16:32:47 +01:00
|
|
|
if dev:
|
2020-07-10 20:20:27 +01:00
|
|
|
c.run("poetry run pre-commit install", hide=hide)
|
2020-07-10 16:32:47 +01:00
|
|
|
|
|
|
|
|
2020-07-10 16:51:40 +01:00
|
|
|
def install_ospf_mdr(c: Context, os_info: OsInfo, hide: bool) -> None:
|
|
|
|
if c.run("which zebra", warn=True, hide=hide):
|
2020-07-10 16:32:47 +01:00
|
|
|
print("quagga already installed, skipping ospf mdr")
|
|
|
|
return
|
2020-07-10 17:20:13 +01:00
|
|
|
print("installing ospf mdr dependencies...")
|
2020-07-10 16:32:47 +01:00
|
|
|
if os_info.like == OsLike.DEBIAN:
|
2020-07-10 21:00:38 +01:00
|
|
|
c.run("sudo apt install -y libtool gawk libreadline-dev git", hide=hide)
|
2020-07-10 19:30:12 +01:00
|
|
|
elif os_info.like == OsLike.REDHAT:
|
2020-07-10 21:00:38 +01:00
|
|
|
c.run("sudo yum install -y libtool gawk readline-devel git", hide=hide)
|
2020-07-10 17:20:13 +01:00
|
|
|
print("cloning ospf mdr...")
|
2020-07-10 16:32:47 +01:00
|
|
|
clone_dir = "/tmp/ospf-mdr"
|
|
|
|
c.run(
|
2020-07-10 16:51:40 +01:00
|
|
|
f"git clone https://github.com/USNavalResearchLaboratory/ospf-mdr {clone_dir}",
|
|
|
|
hide=hide
|
2020-07-10 16:32:47 +01:00
|
|
|
)
|
|
|
|
with c.cd(clone_dir):
|
2020-07-10 17:20:13 +01:00
|
|
|
print("building ospf mdr...")
|
2020-07-10 16:51:40 +01:00
|
|
|
c.run("./bootstrap.sh", hide=hide)
|
2020-07-10 16:32:47 +01:00
|
|
|
c.run(
|
|
|
|
"./configure --disable-doc --enable-user=root --enable-group=root "
|
|
|
|
"--with-cflags=-ggdb --sysconfdir=/usr/local/etc/quagga --enable-vtysh "
|
2020-07-10 16:51:40 +01:00
|
|
|
"--localstatedir=/var/run/quagga",
|
|
|
|
hide=hide
|
2020-07-10 16:32:47 +01:00
|
|
|
)
|
2020-07-10 20:20:27 +01:00
|
|
|
c.run("make -j$(nproc)", hide=hide)
|
2020-07-10 17:20:13 +01:00
|
|
|
print("installing ospf mdr...")
|
2020-07-10 16:51:40 +01:00
|
|
|
c.run("sudo make install", hide=hide)
|
2020-07-10 16:32:47 +01:00
|
|
|
|
|
|
|
|
2020-07-13 21:48:27 +01:00
|
|
|
def install_files(c: Context, hide: bool, prefix: str = "/usr/local") -> None:
|
2020-07-13 21:14:13 +01:00
|
|
|
# install all scripts
|
|
|
|
python = get_python(c)
|
|
|
|
bin_dir = Path(prefix).joinpath("bin")
|
|
|
|
for script in Path("daemon/scripts").iterdir():
|
|
|
|
dest = bin_dir.joinpath(script.name)
|
|
|
|
print(f"installing {script} to {dest}")
|
|
|
|
with open(script, "r") as f:
|
|
|
|
lines = f.readlines()
|
|
|
|
first = lines[0].strip()
|
|
|
|
# modify python scripts to point to virtual environment
|
|
|
|
if first == "#!/usr/bin/env python3":
|
|
|
|
lines[0] = f"#!{python}\n"
|
|
|
|
temp = NamedTemporaryFile("w", delete=False)
|
|
|
|
for line in lines:
|
|
|
|
temp.write(line)
|
|
|
|
temp.close()
|
|
|
|
c.run(f"sudo cp {temp.name} {dest}", hide=hide)
|
|
|
|
c.run(f"sudo chmod 755 {dest}", hide=hide)
|
|
|
|
os.unlink(temp.name)
|
|
|
|
# copy normal links
|
|
|
|
else:
|
|
|
|
c.run(f"sudo cp {script} {dest}", hide=hide)
|
|
|
|
|
|
|
|
# install core configuration file
|
|
|
|
config_dir = "/etc/core"
|
|
|
|
print(f"installing core configuration files under {config_dir}")
|
|
|
|
c.run(f"sudo mkdir -p {config_dir}", hide=hide)
|
|
|
|
c.run(f"sudo cp -n daemon/data/core.conf {config_dir}", hide=hide)
|
|
|
|
c.run(f"sudo cp -n daemon/data/logging.conf {config_dir}", hide=hide)
|
|
|
|
|
|
|
|
# install service
|
|
|
|
systemd_dir = Path("/lib/systemd/system/")
|
|
|
|
service_file = systemd_dir.joinpath("core-daemon.service")
|
|
|
|
if systemd_dir.exists():
|
|
|
|
print(f"installing core-daemon.service for systemd to {service_file}")
|
|
|
|
service_data = inspect.cleandoc(f"""
|
|
|
|
[Unit]
|
|
|
|
Description=Common Open Research Emulator Service
|
|
|
|
After=network.target
|
|
|
|
|
|
|
|
[Service]
|
|
|
|
Type=simple
|
|
|
|
ExecStart={bin_dir}/core-daemon
|
|
|
|
TasksMax=infinity
|
|
|
|
|
|
|
|
[Install]
|
|
|
|
WantedBy=multi-user.target
|
|
|
|
""")
|
|
|
|
temp = NamedTemporaryFile("w", delete=False)
|
|
|
|
temp.write(service_data)
|
|
|
|
temp.close()
|
|
|
|
c.run(f"sudo cp {temp.name} {service_file}", hide=hide)
|
|
|
|
|
|
|
|
|
2020-07-10 16:32:47 +01:00
|
|
|
@task
|
2020-07-10 16:51:40 +01:00
|
|
|
def install(c, dev=False, verbose=False):
|
2020-07-10 16:32:47 +01:00
|
|
|
"""
|
|
|
|
install core
|
|
|
|
"""
|
2020-07-10 16:51:40 +01:00
|
|
|
hide = not verbose
|
2020-07-10 16:32:47 +01:00
|
|
|
os_info = get_os()
|
2020-07-10 16:51:40 +01:00
|
|
|
install_system(c, os_info, hide)
|
2020-07-10 19:20:27 +01:00
|
|
|
install_grpcio(c, hide)
|
2020-07-10 20:23:26 +01:00
|
|
|
build(c, os_info, hide)
|
2020-07-10 16:51:40 +01:00
|
|
|
install_core(c, hide)
|
|
|
|
install_poetry(c, dev, hide)
|
2020-07-13 21:14:13 +01:00
|
|
|
install_files(c, hide)
|
2020-07-10 16:51:40 +01:00
|
|
|
install_ospf_mdr(c, os_info, hide)
|
2020-07-10 18:27:17 +01:00
|
|
|
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")
|
2020-07-10 07:01:28 +01:00
|
|
|
|
2020-06-02 22:48:57 +01:00
|
|
|
|
2020-07-13 21:48:27 +01:00
|
|
|
@task
|
|
|
|
def uninstall(c, dev=False, verbose=False, prefix="/usr/local"):
|
|
|
|
"""
|
|
|
|
uninstall core
|
|
|
|
"""
|
|
|
|
hide = not verbose
|
|
|
|
print("uninstalling core-gui")
|
|
|
|
with c.cd(GUI_DIR):
|
|
|
|
c.run("sudo make uninstall", hide=hide)
|
|
|
|
print("uninstalling vcmd")
|
|
|
|
with c.cd(VCMD_DIR):
|
|
|
|
c.run("sudo make uninstall", hide=hide)
|
|
|
|
print("cleaning build directory")
|
|
|
|
c.run("make clean", hide=hide)
|
|
|
|
c.run("./bootstrap.sh clean", hide=hide)
|
|
|
|
python = get_python(c, warn=True)
|
|
|
|
if python:
|
|
|
|
with c.cd(DAEMON_DIR):
|
|
|
|
if dev:
|
|
|
|
print("uninstalling pre-commit")
|
|
|
|
c.run("poetry run pre-commit uninstall", hide=hide)
|
|
|
|
print("uninstalling poetry virtual environment")
|
|
|
|
c.run(f"poetry env remove {python}", hide=hide)
|
|
|
|
|
|
|
|
# remove installed files
|
|
|
|
bin_dir = Path(prefix).joinpath("bin")
|
|
|
|
for script in Path("daemon/scripts").iterdir():
|
|
|
|
dest = bin_dir.joinpath(script.name)
|
|
|
|
print(f"uninstalling {dest}")
|
|
|
|
c.run(f"sudo rm -f {dest}", hide=hide)
|
|
|
|
|
|
|
|
# install service
|
|
|
|
systemd_dir = Path("/lib/systemd/system/")
|
|
|
|
service_name = "core-daemon.service"
|
|
|
|
service_file = systemd_dir.joinpath(service_name)
|
|
|
|
if service_file.exists():
|
|
|
|
print(f"uninstalling service {service_file}")
|
|
|
|
c.run(f"sudo systemctl disable {service_name}", hide=hide)
|
|
|
|
c.run(f"sudo rm -f {service_file}", hide=hide)
|
|
|
|
|
|
|
|
|
2020-06-02 22:48:57 +01:00
|
|
|
@task
|
2020-07-08 07:38:12 +01:00
|
|
|
def daemon(c):
|
|
|
|
"""
|
2020-07-10 07:01:28 +01:00
|
|
|
start core-daemon
|
2020-07-08 07:38:12 +01:00
|
|
|
"""
|
2020-07-10 07:01:28 +01:00
|
|
|
python = get_python(c)
|
|
|
|
with c.cd(DAEMON_DIR):
|
2020-07-08 07:38:12 +01:00
|
|
|
c.run(
|
2020-07-10 07:01:28 +01:00
|
|
|
f"sudo {python} scripts/core-daemon "
|
|
|
|
"-f data/core.conf -l data/logging.conf",
|
|
|
|
pty=True
|
2020-07-08 07:38:12 +01:00
|
|
|
)
|
2020-06-02 22:48:57 +01:00
|
|
|
|
|
|
|
|
|
|
|
@task
|
2020-07-08 07:38:12 +01:00
|
|
|
def gui(c):
|
|
|
|
"""
|
2020-07-10 07:01:28 +01:00
|
|
|
start core-pygui
|
2020-07-08 07:38:12 +01:00
|
|
|
"""
|
2020-07-10 07:01:28 +01:00
|
|
|
with c.cd(DAEMON_DIR):
|
|
|
|
c.run("poetry run scripts/core-pygui", pty=True)
|
2020-07-08 07:38:12 +01:00
|
|
|
|
|
|
|
|
2020-07-11 04:29:47 +01:00
|
|
|
@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)
|
|
|
|
|
|
|
|
|
2020-07-10 20:52:12 +01:00
|
|
|
@task
|
|
|
|
def cleanup(c):
|
|
|
|
"""
|
|
|
|
run core-cleanup removing leftover core nodes, bridges, directories
|
|
|
|
"""
|
2020-07-10 20:53:21 +01:00
|
|
|
print("running core-cleanup...")
|
2020-07-10 20:52:12 +01:00
|
|
|
c.run(f"sudo daemon/scripts/core-cleanup", pty=True)
|
|
|
|
|
|
|
|
|
2020-07-08 07:38:12 +01:00
|
|
|
@task
|
|
|
|
def test(c):
|
|
|
|
"""
|
2020-07-10 07:01:28 +01:00
|
|
|
run core tests
|
2020-07-08 07:38:12 +01:00
|
|
|
"""
|
2020-07-10 07:01:28 +01:00
|
|
|
pytest = get_pytest(c)
|
|
|
|
with c.cd(DAEMON_DIR):
|
|
|
|
c.run(f"sudo {pytest} -v --lf -x tests", pty=True)
|
2020-07-08 07:38:12 +01:00
|
|
|
|
|
|
|
|
|
|
|
@task
|
|
|
|
def test_mock(c):
|
|
|
|
"""
|
2020-07-10 07:01:28 +01:00
|
|
|
run core tests using mock to avoid running as sudo
|
2020-07-08 07:38:12 +01:00
|
|
|
"""
|
2020-07-10 07:01:28 +01:00
|
|
|
with c.cd(DAEMON_DIR):
|
2020-07-08 07:38:12 +01:00
|
|
|
c.run("poetry run pytest -v --mock --lf -x tests", pty=True)
|
|
|
|
|
|
|
|
|
|
|
|
@task
|
|
|
|
def test_emane(c):
|
|
|
|
"""
|
2020-07-10 07:01:28 +01:00
|
|
|
run core emane tests
|
2020-07-08 07:38:12 +01:00
|
|
|
"""
|
2020-07-10 07:01:28 +01:00
|
|
|
pytest = get_pytest(c)
|
|
|
|
with c.cd(DAEMON_DIR):
|
|
|
|
c.run(f"{pytest} -v --lf -x tests/emane", pty=True)
|