install: updates to support building deb/rpm packages that contain python wheels and install core from a single file, updates to core to install scripts by way of python directly

This commit is contained in:
Blake Harnden 2022-07-27 16:00:10 -07:00
parent cd6bb319ad
commit fcf6f30302
54 changed files with 528 additions and 187 deletions

View file

@ -21,6 +21,7 @@ ACLOCAL_AMFLAGS = -I config
# extra files to include with distribution tarball
EXTRA_DIST = bootstrap.sh \
package \
LICENSE \
README.md \
ASSIGNMENT_OF_COPYRIGHT.pdf \
@ -47,7 +48,7 @@ fpm -s dir -t deb -n core-distributed \
--description "Common Open Research Emulator Distributed Package" \
--url https://github.com/coreemu/core \
--vendor "$(PACKAGE_VENDOR)" \
-p core_distributed_VERSION_ARCH.deb \
-p core-distributed_VERSION_ARCH.deb \
-v $(PACKAGE_VERSION) \
-d "ethtool" \
-d "procps" \
@ -58,7 +59,8 @@ fpm -s dir -t deb -n core-distributed \
-d "libev4" \
-d "openssh-server" \
-d "xterm" \
-C $(DESTDIR)
netns/vnoded=/usr/bin/ \
netns/vcmd=/usr/bin/
endef
define fpm-distributed-rpm =
@ -68,7 +70,7 @@ fpm -s dir -t rpm -n core-distributed \
--description "Common Open Research Emulator Distributed Package" \
--url https://github.com/coreemu/core \
--vendor "$(PACKAGE_VENDOR)" \
-p core_distributed_VERSION_ARCH.rpm \
-p core-distributed_VERSION_ARCH.rpm \
-v $(PACKAGE_VERSION) \
-d "ethtool" \
-d "procps-ng" \
@ -79,12 +81,75 @@ fpm -s dir -t rpm -n core-distributed \
-d "net-tools" \
-d "openssh-server" \
-d "xterm" \
-C $(DESTDIR)
netns/vnoded=/usr/bin/ \
netns/vcmd=/usr/bin/
endef
.PHONY: fpm-distributed
fpm-distributed: clean-local-fpm
$(MAKE) -C netns install DESTDIR=$(DESTDIR)
define fpm-rpm =
fpm -s dir -t rpm -n core \
-m "$(PACKAGE_MAINTAINERS)" \
--license "BSD" \
--description "core vnoded/vcmd and system dependencies" \
--url https://github.com/coreemu/core \
--vendor "$(PACKAGE_VENDOR)" \
-p core_VERSION_ARCH.rpm \
-v $(PACKAGE_VERSION) \
--rpm-init package/core-daemon \
--after-install package/after-install.sh \
--after-remove package/after-remove.sh \
-d "ethtool" \
-d "tk" \
-d "procps-ng" \
-d "bash >= 3.0" \
-d "ebtables" \
-d "iproute" \
-d "libev" \
-d "net-tools" \
-d "nftables" \
netns/vnoded=/usr/bin/ \
netns/vcmd=/usr/bin/ \
package/etc/core.conf=/etc/core/ \
package/etc/logging.conf=/etc/core/ \
package/examples=/opt/core/ \
daemon/dist/core-$(PACKAGE_VERSION)-py3-none-any.whl=/opt/core/
endef
define fpm-deb =
fpm -s dir -t deb -n core \
-m "$(PACKAGE_MAINTAINERS)" \
--license "BSD" \
--description "core vnoded/vcmd and system dependencies" \
--url https://github.com/coreemu/core \
--vendor "$(PACKAGE_VENDOR)" \
-p core_VERSION_ARCH.deb \
-v $(PACKAGE_VERSION) \
--deb-systemd package/core-daemon.service \
--deb-no-default-config-files \
--after-install package/after-install.sh \
--after-remove package/after-remove.sh \
-d "ethtool" \
-d "tk" \
-d "libtk-img" \
-d "procps" \
-d "libc6 >= 2.14" \
-d "bash >= 3.0" \
-d "ebtables" \
-d "iproute2" \
-d "libev4" \
-d "nftables" \
netns/vnoded=/usr/bin/ \
netns/vcmd=/usr/bin/ \
package/etc/core.conf=/etc/core/ \
package/etc/logging.conf=/etc/core/ \
package/examples=/opt/core/ \
daemon/dist/core-$(PACKAGE_VERSION)-py3-none-any.whl=/opt/core/
endef
.PHONY: fpm
fpm: clean-local-fpm
cd daemon && poetry build -f wheel
$(call fpm-deb)
$(call fpm-rpm)
$(call fpm-distributed-deb)
$(call fpm-distributed-rpm)

View file

@ -21,10 +21,7 @@ DISTCLEANFILES = Makefile.in
# files to include with distribution tarball
EXTRA_DIST = core \
data \
doc/conf.py.in \
examples \
scripts \
tests \
setup.cfg \
poetry.lock \

103
daemon/core/scripts/cleanup.py Executable file
View file

@ -0,0 +1,103 @@
import argparse
import os
import subprocess
import sys
import time
def check_root() -> None:
if os.geteuid() != 0:
print("permission denied, run this script as root")
sys.exit(1)
def parse_args() -> argparse.Namespace:
parser = argparse.ArgumentParser(
description="helps cleanup lingering core processes and files",
formatter_class=argparse.ArgumentDefaultsHelpFormatter,
)
parser.add_argument(
"-d", "--daemon", action="store_true", help="also kill core-daemon"
)
return parser.parse_args()
def cleanup_daemon() -> None:
print("killing core-daemon process ... ", end="")
result = subprocess.call("pkill -9 core-daemon", shell=True)
if result:
print("not found")
else:
print("done")
def cleanup_nodes() -> None:
print("killing vnoded processes ... ", end="")
result = subprocess.call("pkill -KILL vnoded", shell=True)
if result:
print("none found")
else:
time.sleep(1)
print("done")
def cleanup_emane() -> None:
print("killing emane processes ... ", end="")
result = subprocess.call("pkill emane", shell=True)
if result:
print("none found")
else:
print("done")
def cleanup_sessions() -> None:
print("removing session directories ... ", end="")
result = subprocess.call("rm -rf /tmp/pycore*", shell=True)
if result:
print("none found")
else:
print("done")
def cleanup_interfaces() -> None:
print("cleaning up devices")
output = subprocess.check_output("ip -o -br link show", shell=True)
lines = output.decode().strip().split("\n")
for line in lines:
values = line.split()
name = values[0]
if (
name.startswith("veth")
or name.startswith("gt.")
or name.startswith("b.")
or name.startswith("ctrl")
):
result = subprocess.call(f"ip link delete {name}", shell=True)
if result:
print(f"failed to remove {name}")
else:
print(f"removed {name}")
if name.startswith("b."):
result = subprocess.call(
f"nft delete table bridge {name}",
stdout=subprocess.DEVNULL,
stderr=subprocess.DEVNULL,
shell=True,
)
if not result:
print(f"cleared nft rules for {name}")
def main() -> None:
check_root()
args = parse_args()
if args.daemon:
cleanup_daemon()
cleanup_nodes()
cleanup_emane()
cleanup_interfaces()
cleanup_sessions()
if __name__ == "__main__":
main()

View file

@ -1,4 +1,3 @@
#!/usr/bin/env python3
import json
import sys
from argparse import (
@ -32,7 +31,9 @@ NODE_TYPES = [x.name for x in NodeType if x != NodeType.PEER_TO_PEER]
def protobuf_to_json(message: Any) -> Dict[str, Any]:
return MessageToDict(message, including_default_value_fields=True, preserving_proto_field_name=True)
return MessageToDict(
message, including_default_value_fields=True, preserving_proto_field_name=True
)
def print_json(data: Any) -> None:
@ -122,18 +123,15 @@ def get_current_session(core: CoreGrpcClient, session_id: Optional[int]) -> int:
return sessions[0].id
def create_iface(iface_id: int, mac: str, ip4_net: IPNetwork, ip6_net: IPNetwork) -> Interface:
def create_iface(
iface_id: int, mac: str, ip4_net: IPNetwork, ip6_net: IPNetwork
) -> Interface:
ip4 = str(ip4_net.ip) if ip4_net else None
ip4_mask = ip4_net.prefixlen if ip4_net else None
ip6 = str(ip6_net.ip) if ip6_net else None
ip6_mask = ip6_net.prefixlen if ip6_net else None
return Interface(
id=iface_id,
mac=mac,
ip4=ip4,
ip4_mask=ip4_mask,
ip6=ip6,
ip6_mask=ip6_mask,
id=iface_id, mac=mac, ip4=ip4, ip4_mask=ip4_mask, ip6=ip6, ip6_mask=ip6_mask
)
@ -216,12 +214,14 @@ def query_session(core: CoreGrpcClient, args: Namespace) -> None:
for node in session.nodes.values():
xy_pos = f"{int(node.position.x)},{int(node.position.y)}"
geo_pos = f"{node.geo.lon:.7f},{node.geo.lat:.7f},{node.geo.alt:f}"
print(f"{node.id:<7} | {node.name[:7]:<7} | {node.type.name[:7]:<7} | {xy_pos:<9} | {geo_pos}")
print(
f"{node.id:<7} | {node.name[:7]:<7} | {node.type.name[:7]:<7} | {xy_pos:<9} | {geo_pos}"
)
print("\nLinks")
for link in session.links:
n1 = session.nodes[link.node1_id].name
n2 = session.nodes[link.node2_id].name
print(f"Node | ", end="")
print("Node | ", end="")
print_iface_header()
print(f"{n1:<6} | ", end="")
if link.iface1:
@ -248,7 +248,9 @@ def query_node(core: CoreGrpcClient, args: Namespace) -> None:
print("ID | Name | Type | XY | Geo")
xy_pos = f"{int(node.position.x)},{int(node.position.y)}"
geo_pos = f"{node.geo.lon:.7f},{node.geo.lat:.7f},{node.geo.alt:f}"
print(f"{node.id:<7} | {node.name[:7]:<7} | {node.type.name[:7]:<7} | {xy_pos:<9} | {geo_pos}")
print(
f"{node.id:<7} | {node.name[:7]:<7} | {node.type.name[:7]:<7} | {xy_pos:<9} | {geo_pos}"
)
if ifaces:
print("Interfaces")
print("Connected To | ", end="")
@ -348,10 +350,14 @@ def add_link(core: CoreGrpcClient, args: Namespace) -> None:
session_id = get_current_session(core, args.session)
iface1 = None
if args.iface1_id is not None:
iface1 = create_iface(args.iface1_id, args.iface1_mac, args.iface1_ip4, args.iface1_ip6)
iface1 = create_iface(
args.iface1_id, args.iface1_mac, args.iface1_ip4, args.iface1_ip6
)
iface2 = None
if args.iface2_id is not None:
iface2 = create_iface(args.iface2_id, args.iface2_mac, args.iface2_ip4, args.iface2_ip6)
iface2 = create_iface(
args.iface2_id, args.iface2_mac, args.iface2_ip4, args.iface2_ip6
)
options = LinkOptions(
bandwidth=args.bandwidth,
loss=args.loss,
@ -432,13 +438,17 @@ def setup_node_parser(parent) -> None:
add_parser.add_argument(
"-t", "--type", choices=NODE_TYPES, default="DEFAULT", help="type of node"
)
add_parser.add_argument("-m", "--model", help="used to determine services, optional")
add_parser.add_argument(
"-m", "--model", help="used to determine services, optional"
)
group = add_parser.add_mutually_exclusive_group(required=True)
group.add_argument("-p", "--pos", type=position_type, help="x,y position")
group.add_argument("-g", "--geo", type=geo_type, help="lon,lat,alt position")
add_parser.add_argument("-ic", "--icon", help="icon to use, optional")
add_parser.add_argument("-im", "--image", help="container image, optional")
add_parser.add_argument("-e", "--emane", help="emane model, only required for emane nodes")
add_parser.add_argument(
"-e", "--emane", help="emane model, only required for emane nodes"
)
add_parser.set_defaults(func=add_node)
edit_parser = subparsers.add_parser("edit", help="edit a node")
@ -449,7 +459,9 @@ def setup_node_parser(parent) -> None:
move_parser = subparsers.add_parser("move", help="move a node")
move_parser.formatter_class = ArgumentDefaultsHelpFormatter
move_parser.add_argument("-i", "--id", type=int, help="id to use, optional", required=True)
move_parser.add_argument(
"-i", "--id", type=int, help="id to use, optional", required=True
)
group = move_parser.add_mutually_exclusive_group(required=True)
group.add_argument("-p", "--pos", type=position_type, help="x,y position")
group.add_argument("-g", "--geo", type=geo_type, help="lon,lat,alt position")
@ -474,19 +486,33 @@ def setup_link_parser(parent) -> None:
add_parser.add_argument("-n1", "--node1", type=int, help="node1 id", required=True)
add_parser.add_argument("-n2", "--node2", type=int, help="node2 id", required=True)
add_parser.add_argument("-i1-i", "--iface1-id", type=int, help="node1 interface id")
add_parser.add_argument("-i1-m", "--iface1-mac", type=mac_type, help="node1 interface mac")
add_parser.add_argument("-i1-4", "--iface1-ip4", type=ip4_type, help="node1 interface ip4")
add_parser.add_argument("-i1-6", "--iface1-ip6", type=ip6_type, help="node1 interface ip6")
add_parser.add_argument(
"-i1-m", "--iface1-mac", type=mac_type, help="node1 interface mac"
)
add_parser.add_argument(
"-i1-4", "--iface1-ip4", type=ip4_type, help="node1 interface ip4"
)
add_parser.add_argument(
"-i1-6", "--iface1-ip6", type=ip6_type, help="node1 interface ip6"
)
add_parser.add_argument("-i2-i", "--iface2-id", type=int, help="node2 interface id")
add_parser.add_argument("-i2-m", "--iface2-mac", type=mac_type, help="node2 interface mac")
add_parser.add_argument("-i2-4", "--iface2-ip4", type=ip4_type, help="node2 interface ip4")
add_parser.add_argument("-i2-6", "--iface2-ip6", type=ip6_type, help="node2 interface ip6")
add_parser.add_argument(
"-i2-m", "--iface2-mac", type=mac_type, help="node2 interface mac"
)
add_parser.add_argument(
"-i2-4", "--iface2-ip4", type=ip4_type, help="node2 interface ip4"
)
add_parser.add_argument(
"-i2-6", "--iface2-ip6", type=ip6_type, help="node2 interface ip6"
)
add_parser.add_argument("-b", "--bandwidth", type=int, help="bandwidth (bps)")
add_parser.add_argument("-l", "--loss", type=float, help="loss (%%)")
add_parser.add_argument("-j", "--jitter", type=int, help="jitter (us)")
add_parser.add_argument("-de", "--delay", type=int, help="delay (us)")
add_parser.add_argument("-du", "--duplicate", type=int, help="duplicate (%%)")
add_parser.add_argument("-u", "--uni", action="store_true", help="is link unidirectional?")
add_parser.add_argument(
"-u", "--uni", action="store_true", help="is link unidirectional?"
)
add_parser.set_defaults(func=add_link)
edit_parser = subparsers.add_parser("edit", help="edit a link")
@ -507,8 +533,12 @@ def setup_link_parser(parent) -> None:
delete_parser = subparsers.add_parser("delete", help="delete a link")
delete_parser.formatter_class = ArgumentDefaultsHelpFormatter
delete_parser.add_argument("-n1", "--node1", type=int, help="node1 id", required=True)
delete_parser.add_argument("-n2", "--node2", type=int, help="node1 id", required=True)
delete_parser.add_argument(
"-n1", "--node1", type=int, help="node1 id", required=True
)
delete_parser.add_argument(
"-n2", "--node2", type=int, help="node1 id", required=True
)
delete_parser.add_argument("-i1", "--iface1", type=int, help="node1 interface id")
delete_parser.add_argument("-i2", "--iface2", type=int, help="node2 interface id")
delete_parser.set_defaults(func=delete_link)
@ -526,20 +556,28 @@ def setup_query_parser(parent) -> None:
session_parser = subparsers.add_parser("session", help="query session")
session_parser.formatter_class = ArgumentDefaultsHelpFormatter
session_parser.add_argument("-i", "--id", type=int, help="session to query", required=True)
session_parser.add_argument(
"-i", "--id", type=int, help="session to query", required=True
)
session_parser.set_defaults(func=query_session)
node_parser = subparsers.add_parser("node", help="query node")
node_parser.formatter_class = ArgumentDefaultsHelpFormatter
node_parser.add_argument("-i", "--id", type=int, help="session to query", required=True)
node_parser.add_argument("-n", "--node", type=int, help="node to query", required=True)
node_parser.add_argument(
"-i", "--id", type=int, help="session to query", required=True
)
node_parser.add_argument(
"-n", "--node", type=int, help="node to query", required=True
)
node_parser.set_defaults(func=query_node)
def setup_xml_parser(parent) -> None:
parser = parent.add_parser("xml", help="open session xml")
parser.formatter_class = ArgumentDefaultsHelpFormatter
parser.add_argument("-f", "--file", type=file_type, help="xml file to open", required=True)
parser.add_argument(
"-f", "--file", type=file_type, help="xml file to open", required=True
)
parser.add_argument("-s", "--start", action="store_true", help="start the session?")
parser.set_defaults(func=open_xml)

View file

@ -1,4 +1,3 @@
#!/usr/bin/env python3
"""
core-daemon: the CORE daemon is a server process that receives CORE API
messages and instantiates emulated nodes and networks within the kernel. Various
@ -61,18 +60,35 @@ def get_merged_config(filename):
defaults = {
"grpcport": default_grpc_port,
"grpcaddress": default_address,
"logfile": default_log
"logfile": default_log,
}
parser = argparse.ArgumentParser(
description=f"CORE daemon v.{COREDPY_VERSION} instantiates Linux network namespace nodes.")
parser.add_argument("-f", "--configfile", dest="configfile",
help=f"read config from specified file; default = {filename}")
parser.add_argument("--ovs", action="store_true", help="enable experimental ovs mode, default is false")
parser.add_argument("--grpc-port", dest="grpcport",
help=f"grpc port to listen on; default {default_grpc_port}")
parser.add_argument("--grpc-address", dest="grpcaddress",
help=f"grpc address to listen on; default {default_address}")
parser.add_argument("-l", "--logfile", help=f"core logging configuration; default {default_log}")
description=f"CORE daemon v.{COREDPY_VERSION} instantiates Linux network namespace nodes."
)
parser.add_argument(
"-f",
"--configfile",
dest="configfile",
help=f"read config from specified file; default = {filename}",
)
parser.add_argument(
"--ovs",
action="store_true",
help="enable experimental ovs mode, default is false",
)
parser.add_argument(
"--grpc-port",
dest="grpcport",
help=f"grpc port to listen on; default {default_grpc_port}",
)
parser.add_argument(
"--grpc-address",
dest="grpcaddress",
help=f"grpc address to listen on; default {default_address}",
)
parser.add_argument(
"-l", "--logfile", help=f"core logging configuration; default {default_log}"
)
# parse command line options
args = parser.parse_args()
# convert ovs to internal format

View file

@ -1,4 +1,3 @@
#!/usr/bin/env python3
import argparse
import logging
from logging.handlers import TimedRotatingFileHandler
@ -9,12 +8,19 @@ from core.gui.app import Application
def main() -> None:
# parse flags
parser = argparse.ArgumentParser(description=f"CORE Python GUI")
parser.add_argument("-l", "--level", choices=["DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL"], default="INFO",
help="logging level")
parser = argparse.ArgumentParser(description="CORE Python GUI")
parser.add_argument(
"-l",
"--level",
choices=["DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL"],
default="INFO",
help="logging level",
)
parser.add_argument("-p", "--proxy", action="store_true", help="enable proxy")
parser.add_argument("-s", "--session", type=int, help="session id to join")
parser.add_argument("--create-dir", action="store_true", help="create gui directory and exit")
parser.add_argument(
"--create-dir", action="store_true", help="create gui directory and exit"
)
args = parser.parse_args()
# check home directory exists and create if necessary
@ -25,9 +31,13 @@ def main() -> None:
# setup logging
log_format = "%(asctime)s - %(levelname)s - %(module)s:%(funcName)s - %(message)s"
stream_handler = logging.StreamHandler()
file_handler = TimedRotatingFileHandler(filename=appconfig.LOG_PATH, when="D", backupCount=5)
file_handler = TimedRotatingFileHandler(
filename=appconfig.LOG_PATH, when="D", backupCount=5
)
log_level = logging.getLevelName(args.level)
logging.basicConfig(level=log_level, format=log_format, handlers=[stream_handler, file_handler])
logging.basicConfig(
level=log_level, format=log_format, handlers=[stream_handler, file_handler]
)
logging.getLogger("PIL").setLevel(logging.ERROR)
# start app

View file

@ -1,5 +1,3 @@
#!/usr/bin/env python3
import argparse
import logging
import sys
@ -31,7 +29,10 @@ def parse_args() -> argparse.Namespace:
"-f", "--file", required=True, type=path_type, help="core file to play"
)
parser.add_argument(
"-s", "--session", type=int, help="session to play to, first found session otherwise"
"-s",
"--session",
type=int,
help="session to play to, first found session otherwise",
)
return parser.parse_args()

View file

@ -1,4 +1,3 @@
#!/usr/bin/env python3
import argparse
import enum
import select

View file

@ -1,4 +1,3 @@
#!/usr/bin/env python3
import argparse
import re
from io import TextIOWrapper
@ -6,9 +5,15 @@ from io import TextIOWrapper
def parse_args() -> argparse.Namespace:
parser = argparse.ArgumentParser(
description=f"Helps transition older CORE services to work with newer versions")
parser.add_argument("-f", "--file", dest="file", type=argparse.FileType("r"),
help=f"service file to update")
description="Helps transition older CORE services to work with newer versions"
)
parser.add_argument(
"-f",
"--file",
dest="file",
type=argparse.FileType("r"),
help="service file to update",
)
return parser.parse_args()
@ -20,17 +25,32 @@ def update_service(service_file: TextIOWrapper) -> None:
# rename dirs to directories
line = re.sub(r"^(\s+)dirs", r"\1directories", line)
# fix import states for service
line = re.sub(r"^.+import.+CoreService.+$",
r"from core.services.coreservices import CoreService", line)
line = re.sub(
r"^.+import.+CoreService.+$",
r"from core.services.coreservices import CoreService",
line,
)
# fix method signatures
line = re.sub(r"def generateconfig\(cls, node, filename, services\)",
r"def generate_config(cls, node, filename)", line)
line = re.sub(r"def getvalidate\(cls, node, services\)",
r"def get_validate(cls, node)", line)
line = re.sub(r"def getstartup\(cls, node, services\)",
r"def get_startup(cls, node)", line)
line = re.sub(r"def getconfigfilenames\(cls, nodenum, services\)",
r"def get_configs(cls, node)", line)
line = re.sub(
r"def generateconfig\(cls, node, filename, services\)",
r"def generate_config(cls, node, filename)",
line,
)
line = re.sub(
r"def getvalidate\(cls, node, services\)",
r"def get_validate(cls, node)",
line,
)
line = re.sub(
r"def getstartup\(cls, node, services\)",
r"def get_startup(cls, node)",
line,
)
line = re.sub(
r"def getconfigfilenames\(cls, nodenum, services\)",
r"def get_configs(cls, node)",
line,
)
# remove unwanted lines
if re.search(r"addservice\(", line):
continue

View file

@ -14,6 +14,14 @@ include = [
]
exclude = ["core/constants.py.in"]
[tool.poetry.scripts]
core-daemon = "core.scripts.daemon:main"
core-cli = "core.scripts.cli:main"
core-gui = "core.scripts.gui:main"
core-player = "core.scripts.player:main"
core-route-monitor = "core.scripts.routemonitor:main"
core-service-update = "core.scripts.serviceupdate:main"
core-cleanup = "core.scripts.cleanup:main"
[tool.poetry.dependencies]
python = "^3.6"

View file

@ -1,71 +0,0 @@
#!/bin/sh
if [ "z$1" = "z-h" -o "z$1" = "z--help" ]; then
echo "usage: $0 [-d [-l]]"
echo -n " Clean up all CORE namespaces processes, bridges, interfaces, "
echo "and session\n directories. Options:"
echo " -h show this help message and exit"
echo " -d also kill the Python daemon"
echo " -l remove the core-daemon.log file"
exit 0
fi
if [ `id -u` != 0 ]; then
echo "Permission denied. Re-run this script as root."
exit 1
fi
PATH="/sbin:/bin:/usr/sbin:/usr/bin"
export PATH
if [ "z$1" = "z-d" ]; then
pypids=`pidof python3 python`
for p in $pypids; do
grep -q core-daemon /proc/$p/cmdline
if [ $? = 0 ]; then
echo "cleaning up core-daemon process: $p"
kill -9 $p
fi
done
fi
if [ "z$2" = "z-l" ]; then
rm -f /var/log/core-daemon.log
fi
kaopts="-v"
killall --help 2>&1 | grep -q namespace
if [ $? = 0 ]; then
kaopts="$kaopts --ns 0"
fi
vnodedpids=`pidof vnoded`
if [ "z$vnodedpids" != "z" ]; then
echo "cleaning up old vnoded processes: $vnodedpids"
killall $kaopts -KILL vnoded
# pause for 1 second for interfaces to disappear
sleep 1
fi
killall -q emane
killall -q emanetransportd
killall -q emaneeventservice
if [ -d /sys/class/net ]; then
ifcommand="ls -1 /sys/class/net"
else
ifcommand="ip -o link show | sed -r -e 's/[0-9]+: ([^[:space:]]+): .*/\1/'"
fi
eval "$ifcommand" | awk '
/^veth[0-9]+\./ {print "removing interface " $1; system("ip link del " $1);}
/tmp\./ {print "removing interface " $1; system("ip link del " $1);}
/gt\./ {print "removing interface " $1; system("ip link del " $1);}
/b\./ {print "removing bridge " $1; system("ip link set " $1 " down; ip link del " $1);}
/ctrl[0-9]+\./ {print "removing bridge " $1; system("ip link set " $1 " down; ip link del " $1);}
'
nft list ruleset | awk '
$3 ~ /^b\./ {print "removing nftables " $3; system("nft delete table bridge " $3);}
'
rm -rf /tmp/pycore*

15
package/Dockerfile.centos Normal file
View file

@ -0,0 +1,15 @@
# syntax=docker/dockerfile:1
FROM centos:7
LABEL Description="CORE CentOS Image"
# define environment
ENV DEBIAN_FRONTEND=noninteractive
# install basic dependencies
RUN yum update -y && yum install -y python3 python3-pip python3-tkinter
RUN python3 -m pip install --upgrade pip
# install core
WORKDIR /opt/core
COPY core_*.rpm .
RUN NO_VENV=1 yum install -y ./core_*.rpm

15
package/Dockerfile.ubuntu Normal file
View file

@ -0,0 +1,15 @@
# syntax=docker/dockerfile:1
FROM ubuntu:20.04
LABEL Description="CORE Docker Ubuntu Image"
# define environment
ENV DEBIAN_FRONTEND=noninteractive
# install basic dependencies
RUN apt-get update && apt-get install -y python3 python3-tk python3-pip python3-venv
RUN python3 -m pip install --upgrade pip
# install core
WORKDIR /opt/core
COPY core_*.deb .
RUN apt-get install -y ./core_*.deb

16
package/after-install.sh Normal file
View file

@ -0,0 +1,16 @@
#!/bin/sh
if [ ! -z "${NO_PYTHON}" ]; then
exit 0
fi
PYTHON="${PYTHON:=python3}"
if [ ! -z "${NO_VENV}" ]; then
${PYTHON} -m pip install /opt/core/core-*.whl
echo "DAEMON=/usr/local/bin/core-daemon" > /opt/core/service
else
${PYTHON} -m venv /opt/core/venv
. /opt/core/venv/bin/activate
pip install --upgrade pip
pip install /opt/core/core-*.whl
echo "DAEMON=/opt/core/venv/bin/core-daemon" > /opt/core/service
fi

13
package/after-remove.sh Normal file
View file

@ -0,0 +1,13 @@
#!/bin/sh
if [ -v NO_PYTHON ]; then
exit 0
fi
PYTHON="${PYTHON:=python3}"
if [ -v NO_VENV ]; then
${PYTHON} -m pip uninstall core
else
${PYTHON} -m venv /opt/core/venv
. /opt/core/venv/bin/activate
pip uninstall core
fi

112
package/core-daemon Normal file
View file

@ -0,0 +1,112 @@
#!/bin/sh
### BEGIN INIT INFO
# Provides: core-daemon
# Required-Start: $network $remote_fs
# Required-Stop: $network $remote_fs
# Default-Start: 2 3 4 5
# Default-Stop: 0 1 6
# Short-Description: Start the core-daemon CORE daemon at boot time
# Description: Starts and stops the core-daemon CORE daemon used to
# provide network emulation services for the CORE GUI
# or scripts.
### END INIT INFO
#
# chkconfig: 35 90 03
# description: Starts and stops the CORE daemon \
# used to provide network emulation services.
#
# config: /etc/core/
. /opt/core/service
NAME=`basename $0`
PIDFILE="/var/$NAME.pid"
LOG="/var/log/$NAME.log"
CMD="$DAEMON"
get_pid() {
cat "$PIDFILE"
}
is_alive() {
[ -f "$PIDFILE" ] && ps -p `get_pid` > /dev/null 2>&1
}
corestart() {
if is_alive; then
echo "$NAME already started"
else
echo "starting $NAME"
$CMD 2>&1 >> "$LOG" &
fi
echo $! > "$PIDFILE"
if ! is_alive; then
echo "unable to start $NAME, see $LOG"
exit 1
fi
}
corestop() {
if is_alive; then
echo -n "stopping $NAME.."
kill `get_pid`
for i in 1 2 3 4 5; do
sleep 1
if ! is_alive; then
break
fi
echo -n "."
done
echo
if is_alive; then
echo "not stopped; may still be shutting down"
exit 1
else
echo "stopped"
if [ -f "$PIDFILE" ]; then
rm -f "$PIDFILE"
fi
fi
else
echo "$NAME not running"
fi
}
corerestart() {
corestop
corestart
}
corestatus() {
if is_alive; then
echo "$NAME is running"
else
echo "$NAME is stopped"
exit 1
fi
}
case "$1" in
start)
corestart
;;
stop)
corestop
;;
restart)
corerestart
;;
force-reload)
corerestart
;;
status)
corestatus
;;
*)
echo "Usage: $0 {start|stop|restart|status}"
exit 1
esac
exit $?

View file

@ -0,0 +1,12 @@
[Unit]
Description=Common Open Research Emulator Service
After=network.target
[Service]
Type=simple
EnvironmentFile=/opt/core/service
ExecStart=$DAEMON
TasksMax=infinity
[Install]
WantedBy=multi-user.target

View file

View file

@ -291,25 +291,6 @@ def install_core_files(c, local=False, verbose=False, prefix=DEFAULT_PREFIX):
hide = not verbose
python = get_python(c)
bin_dir = Path(prefix).joinpath("bin")
# install scripts
for script in Path("daemon/scripts").iterdir():
dest = bin_dir.joinpath(script.name)
with open(script, "r") as f:
lines = f.readlines()
first = lines[0].strip()
# modify python scripts to point to virtual environment
if not local and 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)
# setup core python helper
if not local:
core_python = bin_dir.joinpath("core-python")
@ -325,12 +306,12 @@ def install_core_files(c, local=False, verbose=False, prefix=DEFAULT_PREFIX):
# install core configuration file
config_dir = "/etc/core"
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)
c.run(f"sudo cp -n package/etc/core.conf {config_dir}", hide=hide)
c.run(f"sudo cp -n package/etc/logging.conf {config_dir}", hide=hide)
# install examples
examples_dir = f"{prefix}/share/core"
c.run(f"sudo mkdir -p {examples_dir}", hide=hide)
c.run(f"sudo cp -r daemon/examples {examples_dir}", hide=hide)
c.run(f"sudo cp -r package/examples {examples_dir}", hide=hide)
@task(
@ -467,11 +448,9 @@ def uninstall(
c.run("sudo -v", hide=True)
with p.start("uninstalling core"):
c.run("sudo make uninstall", hide=hide)
with p.start("cleaning build directory"):
c.run("make clean", hide=hide)
c.run("./bootstrap.sh clean", hide=hide)
if local:
with p.start("uninstalling core"):
python_bin = get_env_python()
@ -485,22 +464,15 @@ def uninstall(
c.run("poetry run pre-commit uninstall", hide=hide)
with p.start("uninstalling poetry virtual environment"):
c.run(f"poetry env remove {python}", hide=hide)
# remove installed files
bin_dir = Path(prefix).joinpath("bin")
with p.start("uninstalling script files"):
for script in Path("daemon/scripts").iterdir():
dest = bin_dir.joinpath(script.name)
c.run(f"sudo rm -f {dest}", hide=hide)
with p.start("uninstalling examples"):
examples_dir = Path(prefix).joinpath("share/core")
c.run(f"sudo rm -rf {examples_dir}")
# remove core-python symlink
if not local:
core_python = bin_dir.joinpath("core-python")
c.run(f"sudo rm -f {core_python}", hide=hide)
# remove service
systemd_dir = Path("/lib/systemd/system/")
service_name = "core-daemon.service"