added a text spinner while installing/uninstalling

This commit is contained in:
Blake Harnden 2020-07-14 22:09:00 -07:00
parent be2f7e1cae
commit 1cadf8362f

111
tasks.py
View file

@ -1,9 +1,14 @@
import inspect import inspect
import itertools
import os import os
import sys import sys
import threading
import time
from contextlib import contextmanager
from enum import Enum from enum import Enum
from pathlib import Path from pathlib import Path
from tempfile import NamedTemporaryFile from tempfile import NamedTemporaryFile
from typing import Optional
from invoke import task, Context from invoke import task, Context
@ -11,6 +16,40 @@ DAEMON_DIR: str = "daemon"
DEFAULT_PREFIX: str = "/usr/local" DEFAULT_PREFIX: str = "/usr/local"
class Progress:
cycles = itertools.cycle(["-", "/", "|", "\\"])
def __init__(self, verbose: bool) -> None:
self.verbose: bool = verbose
self.thread: Optional[threading.Thread] = None
self.running: bool = False
@contextmanager
def start(self, message: str) -> None:
if not self.verbose:
print(f"{message} ... ", end="")
self.running = True
self.thread = threading.Thread(target=self.run, daemon=True)
self.thread.start()
yield
self.stop()
def run(self) -> None:
while self.running:
sys.stdout.write(next(self.cycles))
sys.stdout.flush()
sys.stdout.write("\b")
time.sleep(0.1)
def stop(self) -> None:
if not self.verbose:
print("done")
if self.thread:
self.running = False
self.thread.join()
self.thread = None
class OsName(Enum): class OsName(Enum):
UBUNTU = "ubuntu" UBUNTU = "ubuntu"
CENTOS = "centos" CENTOS = "centos"
@ -52,7 +91,7 @@ def get_os() -> OsInfo:
if not line: if not line:
continue continue
key, value = line.split("=") key, value = line.split("=")
d[key] = value.strip('"') d[key] = value.strip("\"")
name_value = d["ID"] name_value = d["ID"]
like_value = d["ID_LIKE"] like_value = d["ID_LIKE"]
version_value = d["VERSION_ID"] version_value = d["VERSION_ID"]
@ -69,27 +108,28 @@ def get_os() -> OsInfo:
def install_system(c: Context, os_info: OsInfo, hide: bool) -> None: def install_system(c: Context, os_info: OsInfo, hide: bool) -> None:
print("installing system dependencies...")
if os_info.like == OsLike.DEBIAN: if os_info.like == OsLike.DEBIAN:
c.run( c.run(
"sudo apt install -y automake pkg-config gcc libev-dev ebtables iproute2 " "sudo apt install -y automake pkg-config gcc libev-dev ebtables "
"ethtool tk python3-tk", "iproute2 ethtool tk python3-tk",
hide=hide hide=hide
) )
elif os_info.like == OsLike.REDHAT: elif os_info.like == OsLike.REDHAT:
c.run( c.run(
"sudo yum install -y automake pkgconf-pkg-config gcc gcc-c++ libev-devel " "sudo yum install -y automake pkgconf-pkg-config gcc gcc-c++ "
"iptables-ebtables iproute python3-devel python3-tkinter tk ethtool make", "libev-devel iptables-ebtables iproute python3-devel python3-tkinter "
"tk ethtool make",
hide=hide hide=hide
) )
# centos 8+ does not support netem by default # centos 8+ does not support netem by default
if os_info.name == OsName.CENTOS and os_info.version >= 8: if os_info.name == OsName.CENTOS and os_info.version >= 8:
c.run("sudo yum install -y kernel-modules-extra", hide=hide) c.run("sudo yum install -y kernel-modules-extra", hide=hide)
if not c.run("sudo modprobe sch_netem", warn=True, hide=hide): if not c.run("sudo modprobe sch_netem", warn=True, hide=hide):
print("ERROR: you need to install the latest kernel") print("\nERROR: you need to install the latest kernel")
print("run the following, restart, and try again") print("run the following, restart, and try again")
print("sudo yum update") print("sudo yum update")
sys.exit(1) sys.exit(1)
# attempt to setup legacy ebtables when an nftables based version is found # attempt to setup legacy ebtables when an nftables based version is found
r = c.run("ebtables -V", hide=hide) r = c.run("ebtables -V", hide=hide)
if "nf_tables" in r.stdout: if "nf_tables" in r.stdout:
@ -99,35 +139,31 @@ def install_system(c: Context, os_info: OsInfo, hide: bool) -> None:
hide=hide hide=hide
): ):
print( print(
"WARNING: unable to setup required ebtables-legacy, WLAN will not work" "\nWARNING: unable to setup ebtables-legacy, WLAN will not work"
) )
def install_grpcio(c: Context, hide: bool) -> None: def install_grpcio(c: Context, hide: bool) -> None:
print("installing grpcio-tools...")
c.run( c.run(
"python3 -m pip install --user grpcio==1.27.2 grpcio-tools==1.27.2", hide=hide "python3 -m pip install --user grpcio==1.27.2 grpcio-tools==1.27.2",
hide=hide,
) )
def build(c: Context, hide: bool, prefix: str = DEFAULT_PREFIX) -> None: def build_core(c: Context, hide: bool, prefix: str = DEFAULT_PREFIX) -> None:
print("building core...")
c.run("./bootstrap.sh", hide=hide) c.run("./bootstrap.sh", hide=hide)
c.run(f"./configure --prefix={prefix}", hide=hide) c.run(f"./configure --prefix={prefix}", hide=hide)
c.run("make -j$(nproc)", hide=hide) c.run("make -j$(nproc)", hide=hide)
def install_core(c: Context, hide: bool) -> None: def install_core(c: Context, hide: bool) -> None:
print("installing core vcmd...")
c.run("sudo make install", hide=hide) c.run("sudo make install", hide=hide)
def install_poetry(c: Context, dev: bool, hide: bool) -> None: def install_poetry(c: Context, dev: bool, hide: bool) -> None:
print("installing poetry...")
c.run("pipx install poetry", hide=hide) c.run("pipx install poetry", hide=hide)
args = "" if dev else "--no-dev" args = "" if dev else "--no-dev"
with c.cd(DAEMON_DIR): with c.cd(DAEMON_DIR):
print("installing core environment using poetry...")
c.run(f"poetry install {args}", hide=hide) c.run(f"poetry install {args}", hide=hide)
if dev: if dev:
c.run("poetry run pre-commit install", hide=hide) c.run("poetry run pre-commit install", hide=hide)
@ -135,21 +171,20 @@ def install_poetry(c: Context, dev: bool, hide: bool) -> None:
def install_ospf_mdr(c: Context, os_info: OsInfo, hide: bool) -> None: def install_ospf_mdr(c: Context, os_info: OsInfo, hide: bool) -> None:
if c.run("which zebra", warn=True, hide=hide): if c.run("which zebra", warn=True, hide=hide):
print("quagga already installed, skipping ospf mdr") print("\nquagga already installed, skipping ospf mdr")
return return
print("installing ospf mdr dependencies...") p = Progress(not hide)
with p.start("installing ospf mdr dependencies"):
if os_info.like == OsLike.DEBIAN: if os_info.like == OsLike.DEBIAN:
c.run("sudo apt install -y libtool gawk libreadline-dev git", hide=hide) c.run("sudo apt install -y libtool gawk libreadline-dev git", hide=hide)
elif os_info.like == OsLike.REDHAT: elif os_info.like == OsLike.REDHAT:
c.run("sudo yum install -y libtool gawk readline-devel git", hide=hide) c.run("sudo yum install -y libtool gawk readline-devel git", hide=hide)
print("cloning ospf mdr...")
clone_dir = "/tmp/ospf-mdr" clone_dir = "/tmp/ospf-mdr"
c.run( c.run(
f"git clone https://github.com/USNavalResearchLaboratory/ospf-mdr {clone_dir}", f"git clone https://github.com/USNavalResearchLaboratory/ospf-mdr {clone_dir}",
hide=hide hide=hide
) )
with c.cd(clone_dir): with c.cd(clone_dir):
print("building ospf mdr...")
c.run("./bootstrap.sh", hide=hide) c.run("./bootstrap.sh", hide=hide)
c.run( c.run(
"./configure --disable-doc --enable-user=root --enable-group=root " "./configure --disable-doc --enable-user=root --enable-group=root "
@ -158,7 +193,6 @@ def install_ospf_mdr(c: Context, os_info: OsInfo, hide: bool) -> None:
hide=hide hide=hide
) )
c.run("make -j$(nproc)", hide=hide) c.run("make -j$(nproc)", hide=hide)
print("installing ospf mdr...")
c.run("sudo make install", hide=hide) c.run("sudo make install", hide=hide)
@ -172,7 +206,6 @@ def install_service(c, verbose=False, prefix=DEFAULT_PREFIX):
systemd_dir = Path("/lib/systemd/system/") systemd_dir = Path("/lib/systemd/system/")
service_file = systemd_dir.joinpath("core-daemon.service") service_file = systemd_dir.joinpath("core-daemon.service")
if systemd_dir.exists(): if systemd_dir.exists():
print(f"installing core-daemon.service for systemd to {service_file}")
service_data = inspect.cleandoc(f""" service_data = inspect.cleandoc(f"""
[Unit] [Unit]
Description=Common Open Research Emulator Service Description=Common Open Research Emulator Service
@ -204,7 +237,6 @@ def install_scripts(c, verbose=False, prefix=DEFAULT_PREFIX):
bin_dir = Path(prefix).joinpath("bin") bin_dir = Path(prefix).joinpath("bin")
for script in Path("daemon/scripts").iterdir(): for script in Path("daemon/scripts").iterdir():
dest = bin_dir.joinpath(script.name) dest = bin_dir.joinpath(script.name)
print(f"installing {script} to {dest}")
with open(script, "r") as f: with open(script, "r") as f:
lines = f.readlines() lines = f.readlines()
first = lines[0].strip() first = lines[0].strip()
@ -224,7 +256,6 @@ def install_scripts(c, verbose=False, prefix=DEFAULT_PREFIX):
# install core configuration file # install core configuration file
config_dir = "/etc/core" 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 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/core.conf {config_dir}", hide=hide)
c.run(f"sudo cp -n daemon/data/logging.conf {config_dir}", hide=hide) c.run(f"sudo cp -n daemon/data/logging.conf {config_dir}", hide=hide)
@ -235,22 +266,28 @@ def install(c, dev=False, verbose=False, prefix=DEFAULT_PREFIX):
""" """
install core, poetry, scripts, service, and ospf mdr install core, poetry, scripts, service, and ospf mdr
""" """
c.run("sudo -v", hide=True)
print(f"installing core with prefix: {prefix}") print(f"installing core with prefix: {prefix}")
p = Progress(verbose)
hide = not verbose hide = not verbose
os_info = get_os() os_info = get_os()
with p.start("installing system dependencies"):
install_system(c, os_info, hide) install_system(c, os_info, hide)
with p.start("installing system grpcio-tools"):
install_grpcio(c, hide) install_grpcio(c, hide)
build(c, hide, prefix) with p.start("building core"):
build_core(c, hide, prefix)
with p.start("installing vcmd/gui"):
install_core(c, hide) install_core(c, hide)
with p.start("installing poetry virtual environment"):
install_poetry(c, dev, hide) install_poetry(c, dev, hide)
with p.start("installing scripts and /etc/core"):
install_scripts(c, hide, prefix) install_scripts(c, hide, prefix)
with p.start("installing systemd service"):
install_service(c, hide, prefix) install_service(c, hide, prefix)
with p.start("installing ospf mdr"):
install_ospf_mdr(c, os_info, hide) install_ospf_mdr(c, os_info, hide)
print("please open a new terminal or re-login to leverage invoke for running core") print("\nyou may need to open a new terminal to leverage invoke for running core")
print("# run daemon")
print("inv daemon")
print("# run gui")
print("inv gui")
@task @task
@ -259,25 +296,29 @@ def uninstall(c, dev=False, verbose=False, prefix=DEFAULT_PREFIX):
uninstall core uninstall core
""" """
hide = not verbose hide = not verbose
print("uninstalling core") p = Progress(verbose)
c.run("sudo -v", hide=True)
with p.start("uninstalling core"):
c.run("sudo make uninstall", hide=hide) c.run("sudo make uninstall", hide=hide)
print("cleaning build directory")
with p.start("cleaning build directory"):
c.run("make clean", hide=hide) c.run("make clean", hide=hide)
c.run("./bootstrap.sh clean", hide=hide) c.run("./bootstrap.sh clean", hide=hide)
python = get_python(c, warn=True) python = get_python(c, warn=True)
if python: if python:
with c.cd(DAEMON_DIR): with c.cd(DAEMON_DIR):
if dev: if dev:
print("uninstalling pre-commit") with p.start("uninstalling pre-commit"):
c.run("poetry run pre-commit uninstall", hide=hide) c.run("poetry run pre-commit uninstall", hide=hide)
print("uninstalling poetry virtual environment") with p.start("uninstalling poetry virtual environment"):
c.run(f"poetry env remove {python}", hide=hide) c.run(f"poetry env remove {python}", hide=hide)
# remove installed files # remove installed files
bin_dir = Path(prefix).joinpath("bin") bin_dir = Path(prefix).joinpath("bin")
with p.start("uninstalling script files"):
for script in Path("daemon/scripts").iterdir(): for script in Path("daemon/scripts").iterdir():
dest = bin_dir.joinpath(script.name) dest = bin_dir.joinpath(script.name)
print(f"uninstalling {dest}")
c.run(f"sudo rm -f {dest}", hide=hide) c.run(f"sudo rm -f {dest}", hide=hide)
# install service # install service
@ -285,7 +326,7 @@ def uninstall(c, dev=False, verbose=False, prefix=DEFAULT_PREFIX):
service_name = "core-daemon.service" service_name = "core-daemon.service"
service_file = systemd_dir.joinpath(service_name) service_file = systemd_dir.joinpath(service_name)
if service_file.exists(): if service_file.exists():
print(f"uninstalling service {service_file}") with p.start(f"uninstalling service {service_file}"):
c.run(f"sudo systemctl disable {service_name}", hide=hide) c.run(f"sudo systemctl disable {service_name}", hide=hide)
c.run(f"sudo rm -f {service_file}", hide=hide) c.run(f"sudo rm -f {service_file}", hide=hide)