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:
parent
cd6bb319ad
commit
fcf6f30302
54 changed files with 528 additions and 187 deletions
0
daemon/core/scripts/__init__.py
Normal file
0
daemon/core/scripts/__init__.py
Normal file
103
daemon/core/scripts/cleanup.py
Executable file
103
daemon/core/scripts/cleanup.py
Executable 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()
|
628
daemon/core/scripts/cli.py
Executable file
628
daemon/core/scripts/cli.py
Executable file
|
@ -0,0 +1,628 @@
|
|||
import json
|
||||
import sys
|
||||
from argparse import (
|
||||
ArgumentDefaultsHelpFormatter,
|
||||
ArgumentParser,
|
||||
ArgumentTypeError,
|
||||
Namespace,
|
||||
)
|
||||
from functools import wraps
|
||||
from pathlib import Path
|
||||
from typing import Any, Dict, Optional, Tuple
|
||||
|
||||
import grpc
|
||||
import netaddr
|
||||
from google.protobuf.json_format import MessageToDict
|
||||
from netaddr import EUI, AddrFormatError, IPNetwork
|
||||
|
||||
from core.api.grpc.client import CoreGrpcClient
|
||||
from core.api.grpc.wrappers import (
|
||||
ConfigOption,
|
||||
Geo,
|
||||
Interface,
|
||||
Link,
|
||||
LinkOptions,
|
||||
Node,
|
||||
NodeType,
|
||||
Position,
|
||||
)
|
||||
|
||||
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
|
||||
)
|
||||
|
||||
|
||||
def print_json(data: Any) -> None:
|
||||
data = json.dumps(data, indent=2)
|
||||
print(data)
|
||||
|
||||
|
||||
def coreclient(func):
|
||||
@wraps(func)
|
||||
def wrapper(*args, **kwargs):
|
||||
core = CoreGrpcClient()
|
||||
try:
|
||||
with core.context_connect():
|
||||
return func(core, *args, **kwargs)
|
||||
except grpc.RpcError as e:
|
||||
print(f"grpc error: {e.details()}")
|
||||
|
||||
return wrapper
|
||||
|
||||
|
||||
def mac_type(value: str) -> str:
|
||||
try:
|
||||
mac = EUI(value, dialect=netaddr.mac_unix_expanded)
|
||||
return str(mac)
|
||||
except AddrFormatError:
|
||||
raise ArgumentTypeError(f"invalid mac address: {value}")
|
||||
|
||||
|
||||
def ip4_type(value: str) -> IPNetwork:
|
||||
try:
|
||||
ip = IPNetwork(value)
|
||||
if not netaddr.valid_ipv4(str(ip.ip)):
|
||||
raise ArgumentTypeError(f"invalid ip4 address: {value}")
|
||||
return ip
|
||||
except AddrFormatError:
|
||||
raise ArgumentTypeError(f"invalid ip4 address: {value}")
|
||||
|
||||
|
||||
def ip6_type(value: str) -> IPNetwork:
|
||||
try:
|
||||
ip = IPNetwork(value)
|
||||
if not netaddr.valid_ipv6(str(ip.ip)):
|
||||
raise ArgumentTypeError(f"invalid ip6 address: {value}")
|
||||
return ip
|
||||
except AddrFormatError:
|
||||
raise ArgumentTypeError(f"invalid ip6 address: {value}")
|
||||
|
||||
|
||||
def position_type(value: str) -> Tuple[float, float]:
|
||||
error = "invalid position, must be in the format: float,float"
|
||||
try:
|
||||
values = [float(x) for x in value.split(",")]
|
||||
except ValueError:
|
||||
raise ArgumentTypeError(error)
|
||||
if len(values) != 2:
|
||||
raise ArgumentTypeError(error)
|
||||
x, y = values
|
||||
return x, y
|
||||
|
||||
|
||||
def geo_type(value: str) -> Tuple[float, float, float]:
|
||||
error = "invalid geo, must be in the format: float,float,float"
|
||||
try:
|
||||
values = [float(x) for x in value.split(",")]
|
||||
except ValueError:
|
||||
raise ArgumentTypeError(error)
|
||||
if len(values) != 3:
|
||||
raise ArgumentTypeError(error)
|
||||
lon, lat, alt = values
|
||||
return lon, lat, alt
|
||||
|
||||
|
||||
def file_type(value: str) -> Path:
|
||||
path = Path(value)
|
||||
if not path.is_file():
|
||||
raise ArgumentTypeError(f"invalid file: {value}")
|
||||
return path
|
||||
|
||||
|
||||
def get_current_session(core: CoreGrpcClient, session_id: Optional[int]) -> int:
|
||||
if session_id:
|
||||
return session_id
|
||||
sessions = core.get_sessions()
|
||||
if not sessions:
|
||||
print("no current session to interact with")
|
||||
sys.exit(1)
|
||||
return sessions[0].id
|
||||
|
||||
|
||||
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
|
||||
)
|
||||
|
||||
|
||||
def print_iface_header() -> None:
|
||||
print("ID | MAC Address | IP4 Address | IP6 Address")
|
||||
|
||||
|
||||
def print_iface(iface: Interface) -> None:
|
||||
iface_ip4 = f"{iface.ip4}/{iface.ip4_mask}" if iface.ip4 else ""
|
||||
iface_ip6 = f"{iface.ip6}/{iface.ip6_mask}" if iface.ip6 else ""
|
||||
print(f"{iface.id:<3} | {iface.mac:<17} | {iface_ip4:<18} | {iface_ip6}")
|
||||
|
||||
|
||||
@coreclient
|
||||
def get_wlan_config(core: CoreGrpcClient, args: Namespace) -> None:
|
||||
session_id = get_current_session(core, args.session)
|
||||
config = core.get_wlan_config(session_id, args.node)
|
||||
if args.json:
|
||||
print_json(ConfigOption.to_dict(config))
|
||||
else:
|
||||
size = 0
|
||||
for option in config.values():
|
||||
size = max(size, len(option.name))
|
||||
print(f"{'Name':<{size}.{size}} | Value")
|
||||
for option in config.values():
|
||||
print(f"{option.name:<{size}.{size}} | {option.value}")
|
||||
|
||||
|
||||
@coreclient
|
||||
def set_wlan_config(core: CoreGrpcClient, args: Namespace) -> None:
|
||||
session_id = get_current_session(core, args.session)
|
||||
config = {}
|
||||
if args.bandwidth:
|
||||
config["bandwidth"] = str(args.bandwidth)
|
||||
if args.delay:
|
||||
config["delay"] = str(args.delay)
|
||||
if args.loss:
|
||||
config["error"] = str(args.loss)
|
||||
if args.jitter:
|
||||
config["jitter"] = str(args.jitter)
|
||||
if args.range:
|
||||
config["range"] = str(args.range)
|
||||
result = core.set_wlan_config(session_id, args.node, config)
|
||||
if args.json:
|
||||
print_json(dict(result=result))
|
||||
else:
|
||||
print(f"set wlan config: {result}")
|
||||
|
||||
|
||||
@coreclient
|
||||
def open_xml(core: CoreGrpcClient, args: Namespace) -> None:
|
||||
result, session_id = core.open_xml(args.file, args.start)
|
||||
if args.json:
|
||||
print_json(dict(result=result, session_id=session_id))
|
||||
else:
|
||||
print(f"opened xml: {result},{session_id}")
|
||||
|
||||
|
||||
@coreclient
|
||||
def query_sessions(core: CoreGrpcClient, args: Namespace) -> None:
|
||||
sessions = core.get_sessions()
|
||||
if args.json:
|
||||
sessions = [protobuf_to_json(x.to_proto()) for x in sessions]
|
||||
print_json(sessions)
|
||||
else:
|
||||
print("Session ID | Session State | Nodes")
|
||||
for session in sessions:
|
||||
print(f"{session.id:<10} | {session.state.name:<13} | {session.nodes}")
|
||||
|
||||
|
||||
@coreclient
|
||||
def query_session(core: CoreGrpcClient, args: Namespace) -> None:
|
||||
session = core.get_session(args.id)
|
||||
if args.json:
|
||||
session = protobuf_to_json(session.to_proto())
|
||||
print_json(session)
|
||||
else:
|
||||
print("Nodes")
|
||||
print("ID | Name | Type | XY | Geo")
|
||||
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("\nLinks")
|
||||
for link in session.links:
|
||||
n1 = session.nodes[link.node1_id].name
|
||||
n2 = session.nodes[link.node2_id].name
|
||||
print("Node | ", end="")
|
||||
print_iface_header()
|
||||
print(f"{n1:<6} | ", end="")
|
||||
if link.iface1:
|
||||
print_iface(link.iface1)
|
||||
else:
|
||||
print()
|
||||
print(f"{n2:<6} | ", end="")
|
||||
if link.iface2:
|
||||
print_iface(link.iface2)
|
||||
else:
|
||||
print()
|
||||
print()
|
||||
|
||||
|
||||
@coreclient
|
||||
def query_node(core: CoreGrpcClient, args: Namespace) -> None:
|
||||
session = core.get_session(args.id)
|
||||
node, ifaces, _ = core.get_node(args.id, args.node)
|
||||
if args.json:
|
||||
node = protobuf_to_json(node.to_proto())
|
||||
ifaces = [protobuf_to_json(x.to_proto()) for x in ifaces]
|
||||
print_json(dict(node=node, ifaces=ifaces))
|
||||
else:
|
||||
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}"
|
||||
)
|
||||
if ifaces:
|
||||
print("Interfaces")
|
||||
print("Connected To | ", end="")
|
||||
print_iface_header()
|
||||
for iface in ifaces:
|
||||
if iface.net_id == node.id:
|
||||
if iface.node_id:
|
||||
name = session.nodes[iface.node_id].name
|
||||
else:
|
||||
name = session.nodes[iface.net2_id].name
|
||||
else:
|
||||
net_node = session.nodes.get(iface.net_id)
|
||||
name = net_node.name if net_node else ""
|
||||
print(f"{name:<12} | ", end="")
|
||||
print_iface(iface)
|
||||
|
||||
|
||||
@coreclient
|
||||
def delete_session(core: CoreGrpcClient, args: Namespace) -> None:
|
||||
result = core.delete_session(args.id)
|
||||
if args.json:
|
||||
print_json(dict(result=result))
|
||||
else:
|
||||
print(f"delete session({args.id}): {result}")
|
||||
|
||||
|
||||
@coreclient
|
||||
def add_node(core: CoreGrpcClient, args: Namespace) -> None:
|
||||
session_id = get_current_session(core, args.session)
|
||||
node_type = NodeType[args.type]
|
||||
pos = None
|
||||
if args.pos:
|
||||
x, y = args.pos
|
||||
pos = Position(x=x, y=y)
|
||||
geo = None
|
||||
if args.geo:
|
||||
lon, lat, alt = args.geo
|
||||
geo = Geo(lon=lon, lat=lat, alt=alt)
|
||||
node = Node(
|
||||
id=args.id,
|
||||
name=args.name,
|
||||
type=node_type,
|
||||
model=args.model,
|
||||
emane=args.emane,
|
||||
icon=args.icon,
|
||||
image=args.image,
|
||||
position=pos,
|
||||
geo=geo,
|
||||
)
|
||||
node_id = core.add_node(session_id, node)
|
||||
if args.json:
|
||||
print_json(dict(node_id=node_id))
|
||||
else:
|
||||
print(f"created node: {node_id}")
|
||||
|
||||
|
||||
@coreclient
|
||||
def edit_node(core: CoreGrpcClient, args: Namespace) -> None:
|
||||
session_id = get_current_session(core, args.session)
|
||||
result = core.edit_node(session_id, args.id, args.icon)
|
||||
if args.json:
|
||||
print_json(dict(result=result))
|
||||
else:
|
||||
print(f"edit node: {result}")
|
||||
|
||||
|
||||
@coreclient
|
||||
def move_node(core: CoreGrpcClient, args: Namespace) -> None:
|
||||
session_id = get_current_session(core, args.session)
|
||||
pos = None
|
||||
if args.pos:
|
||||
x, y = args.pos
|
||||
pos = Position(x=x, y=y)
|
||||
geo = None
|
||||
if args.geo:
|
||||
lon, lat, alt = args.geo
|
||||
geo = Geo(lon=lon, lat=lat, alt=alt)
|
||||
result = core.move_node(session_id, args.id, pos, geo)
|
||||
if args.json:
|
||||
print_json(dict(result=result))
|
||||
else:
|
||||
print(f"move node: {result}")
|
||||
|
||||
|
||||
@coreclient
|
||||
def delete_node(core: CoreGrpcClient, args: Namespace) -> None:
|
||||
session_id = get_current_session(core, args.session)
|
||||
result = core.delete_node(session_id, args.id)
|
||||
if args.json:
|
||||
print_json(dict(result=result))
|
||||
else:
|
||||
print(f"deleted node: {result}")
|
||||
|
||||
|
||||
@coreclient
|
||||
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
|
||||
)
|
||||
iface2 = None
|
||||
if args.iface2_id is not None:
|
||||
iface2 = create_iface(
|
||||
args.iface2_id, args.iface2_mac, args.iface2_ip4, args.iface2_ip6
|
||||
)
|
||||
options = LinkOptions(
|
||||
bandwidth=args.bandwidth,
|
||||
loss=args.loss,
|
||||
jitter=args.jitter,
|
||||
delay=args.delay,
|
||||
dup=args.duplicate,
|
||||
unidirectional=args.uni,
|
||||
)
|
||||
link = Link(args.node1, args.node2, iface1=iface1, iface2=iface2, options=options)
|
||||
result, iface1, iface2 = core.add_link(session_id, link)
|
||||
if args.json:
|
||||
iface1 = protobuf_to_json(iface1.to_proto())
|
||||
iface2 = protobuf_to_json(iface2.to_proto())
|
||||
print_json(dict(result=result, iface1=iface1, iface2=iface2))
|
||||
else:
|
||||
print(f"add link: {result}")
|
||||
|
||||
|
||||
@coreclient
|
||||
def edit_link(core: CoreGrpcClient, args: Namespace) -> None:
|
||||
session_id = get_current_session(core, args.session)
|
||||
options = LinkOptions(
|
||||
bandwidth=args.bandwidth,
|
||||
loss=args.loss,
|
||||
jitter=args.jitter,
|
||||
delay=args.delay,
|
||||
dup=args.duplicate,
|
||||
unidirectional=args.uni,
|
||||
)
|
||||
iface1 = Interface(args.iface1)
|
||||
iface2 = Interface(args.iface2)
|
||||
link = Link(args.node1, args.node2, iface1=iface1, iface2=iface2, options=options)
|
||||
result = core.edit_link(session_id, link)
|
||||
if args.json:
|
||||
print_json(dict(result=result))
|
||||
else:
|
||||
print(f"edit link: {result}")
|
||||
|
||||
|
||||
@coreclient
|
||||
def delete_link(core: CoreGrpcClient, args: Namespace) -> None:
|
||||
session_id = get_current_session(core, args.session)
|
||||
iface1 = Interface(args.iface1)
|
||||
iface2 = Interface(args.iface2)
|
||||
link = Link(args.node1, args.node2, iface1=iface1, iface2=iface2)
|
||||
result = core.delete_link(session_id, link)
|
||||
if args.json:
|
||||
print_json(dict(result=result))
|
||||
else:
|
||||
print(f"delete link: {result}")
|
||||
|
||||
|
||||
def setup_sessions_parser(parent) -> None:
|
||||
parser = parent.add_parser("session", help="session interactions")
|
||||
parser.formatter_class = ArgumentDefaultsHelpFormatter
|
||||
parser.add_argument("-i", "--id", type=int, help="session id to use", required=True)
|
||||
subparsers = parser.add_subparsers(help="session commands")
|
||||
subparsers.required = True
|
||||
subparsers.dest = "command"
|
||||
|
||||
delete_parser = subparsers.add_parser("delete", help="delete a session")
|
||||
delete_parser.formatter_class = ArgumentDefaultsHelpFormatter
|
||||
delete_parser.set_defaults(func=delete_session)
|
||||
|
||||
|
||||
def setup_node_parser(parent) -> None:
|
||||
parser = parent.add_parser("node", help="node interactions")
|
||||
parser.formatter_class = ArgumentDefaultsHelpFormatter
|
||||
parser.add_argument("-s", "--session", type=int, help="session to interact with")
|
||||
subparsers = parser.add_subparsers(help="node commands")
|
||||
subparsers.required = True
|
||||
subparsers.dest = "command"
|
||||
|
||||
add_parser = subparsers.add_parser("add", help="add a node")
|
||||
add_parser.formatter_class = ArgumentDefaultsHelpFormatter
|
||||
add_parser.add_argument("-i", "--id", type=int, help="id to use, optional")
|
||||
add_parser.add_argument("-n", "--name", help="name to use, optional")
|
||||
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"
|
||||
)
|
||||
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.set_defaults(func=add_node)
|
||||
|
||||
edit_parser = subparsers.add_parser("edit", help="edit a node")
|
||||
edit_parser.formatter_class = ArgumentDefaultsHelpFormatter
|
||||
edit_parser.add_argument("-i", "--id", type=int, help="id to use", required=True)
|
||||
edit_parser.add_argument("-ic", "--icon", help="icon to use, optional")
|
||||
edit_parser.set_defaults(func=edit_node)
|
||||
|
||||
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
|
||||
)
|
||||
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")
|
||||
move_parser.set_defaults(func=move_node)
|
||||
|
||||
delete_parser = subparsers.add_parser("delete", help="delete a node")
|
||||
delete_parser.formatter_class = ArgumentDefaultsHelpFormatter
|
||||
delete_parser.add_argument("-i", "--id", type=int, help="node id", required=True)
|
||||
delete_parser.set_defaults(func=delete_node)
|
||||
|
||||
|
||||
def setup_link_parser(parent) -> None:
|
||||
parser = parent.add_parser("link", help="link interactions")
|
||||
parser.formatter_class = ArgumentDefaultsHelpFormatter
|
||||
parser.add_argument("-s", "--session", type=int, help="session to interact with")
|
||||
subparsers = parser.add_subparsers(help="link commands")
|
||||
subparsers.required = True
|
||||
subparsers.dest = "command"
|
||||
|
||||
add_parser = subparsers.add_parser("add", help="add a node")
|
||||
add_parser.formatter_class = ArgumentDefaultsHelpFormatter
|
||||
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("-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("-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.set_defaults(func=add_link)
|
||||
|
||||
edit_parser = subparsers.add_parser("edit", help="edit a link")
|
||||
edit_parser.formatter_class = ArgumentDefaultsHelpFormatter
|
||||
edit_parser.add_argument("-n1", "--node1", type=int, help="node1 id", required=True)
|
||||
edit_parser.add_argument("-n2", "--node2", type=int, help="node2 id", required=True)
|
||||
edit_parser.add_argument("-i1", "--iface1", type=int, help="node1 interface id")
|
||||
edit_parser.add_argument("-i2", "--iface2", type=int, help="node2 interface id")
|
||||
edit_parser.add_argument("-b", "--bandwidth", type=int, help="bandwidth (bps)")
|
||||
edit_parser.add_argument("-l", "--loss", type=float, help="loss (%%)")
|
||||
edit_parser.add_argument("-j", "--jitter", type=int, help="jitter (us)")
|
||||
edit_parser.add_argument("-de", "--delay", type=int, help="delay (us)")
|
||||
edit_parser.add_argument("-du", "--duplicate", type=int, help="duplicate (%%)")
|
||||
edit_parser.add_argument(
|
||||
"-u", "--uni", action="store_true", help="is link unidirectional?"
|
||||
)
|
||||
edit_parser.set_defaults(func=edit_link)
|
||||
|
||||
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("-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)
|
||||
|
||||
|
||||
def setup_query_parser(parent) -> None:
|
||||
parser = parent.add_parser("query", help="query interactions")
|
||||
subparsers = parser.add_subparsers(help="query commands")
|
||||
subparsers.required = True
|
||||
subparsers.dest = "command"
|
||||
|
||||
sessions_parser = subparsers.add_parser("sessions", help="query current sessions")
|
||||
sessions_parser.formatter_class = ArgumentDefaultsHelpFormatter
|
||||
sessions_parser.set_defaults(func=query_sessions)
|
||||
|
||||
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.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.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("-s", "--start", action="store_true", help="start the session?")
|
||||
parser.set_defaults(func=open_xml)
|
||||
|
||||
|
||||
def setup_wlan_parser(parent) -> None:
|
||||
parser = parent.add_parser("wlan", help="wlan specific interactions")
|
||||
parser.formatter_class = ArgumentDefaultsHelpFormatter
|
||||
parser.add_argument("-s", "--session", type=int, help="session to interact with")
|
||||
subparsers = parser.add_subparsers(help="link commands")
|
||||
subparsers.required = True
|
||||
subparsers.dest = "command"
|
||||
|
||||
get_parser = subparsers.add_parser("get", help="get wlan configuration")
|
||||
get_parser.formatter_class = ArgumentDefaultsHelpFormatter
|
||||
get_parser.add_argument("-n", "--node", type=int, help="wlan node", required=True)
|
||||
get_parser.set_defaults(func=get_wlan_config)
|
||||
|
||||
set_parser = subparsers.add_parser("set", help="set wlan configuration")
|
||||
set_parser.formatter_class = ArgumentDefaultsHelpFormatter
|
||||
set_parser.add_argument("-n", "--node", type=int, help="wlan node", required=True)
|
||||
set_parser.add_argument("-b", "--bandwidth", type=int, help="bandwidth (bps)")
|
||||
set_parser.add_argument("-d", "--delay", type=int, help="delay (us)")
|
||||
set_parser.add_argument("-l", "--loss", type=float, help="loss (%%)")
|
||||
set_parser.add_argument("-j", "--jitter", type=int, help="jitter (us)")
|
||||
set_parser.add_argument("-r", "--range", type=int, help="range (pixels)")
|
||||
set_parser.set_defaults(func=set_wlan_config)
|
||||
|
||||
|
||||
def main() -> None:
|
||||
parser = ArgumentParser(formatter_class=ArgumentDefaultsHelpFormatter)
|
||||
parser.add_argument(
|
||||
"-js", "--json", action="store_true", help="print responses to terminal as json"
|
||||
)
|
||||
subparsers = parser.add_subparsers(help="supported commands")
|
||||
subparsers.required = True
|
||||
subparsers.dest = "command"
|
||||
setup_sessions_parser(subparsers)
|
||||
setup_node_parser(subparsers)
|
||||
setup_link_parser(subparsers)
|
||||
setup_query_parser(subparsers)
|
||||
setup_xml_parser(subparsers)
|
||||
setup_wlan_parser(subparsers)
|
||||
args = parser.parse_args()
|
||||
args.func(args)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
130
daemon/core/scripts/daemon.py
Executable file
130
daemon/core/scripts/daemon.py
Executable file
|
@ -0,0 +1,130 @@
|
|||
"""
|
||||
core-daemon: the CORE daemon is a server process that receives CORE API
|
||||
messages and instantiates emulated nodes and networks within the kernel. Various
|
||||
message handlers are defined and some support for sending messages.
|
||||
"""
|
||||
|
||||
import argparse
|
||||
import logging
|
||||
import os
|
||||
import time
|
||||
from configparser import ConfigParser
|
||||
from pathlib import Path
|
||||
|
||||
from core import constants
|
||||
from core.api.grpc.server import CoreGrpcServer
|
||||
from core.constants import CORE_CONF_DIR, COREDPY_VERSION
|
||||
from core.emulator.coreemu import CoreEmu
|
||||
from core.utils import load_logging_config
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def banner():
|
||||
"""
|
||||
Output the program banner printed to the terminal or log file.
|
||||
|
||||
:return: nothing
|
||||
"""
|
||||
logger.info("CORE daemon v.%s started %s", constants.COREDPY_VERSION, time.ctime())
|
||||
|
||||
|
||||
def cored(cfg):
|
||||
"""
|
||||
Start the CoreServer object and enter the server loop.
|
||||
|
||||
:param dict cfg: core configuration
|
||||
:return: nothing
|
||||
"""
|
||||
# initialize grpc api
|
||||
coreemu = CoreEmu(cfg)
|
||||
grpc_server = CoreGrpcServer(coreemu)
|
||||
address_config = cfg["grpcaddress"]
|
||||
port_config = cfg["grpcport"]
|
||||
grpc_address = f"{address_config}:{port_config}"
|
||||
grpc_server.listen(grpc_address)
|
||||
|
||||
|
||||
def get_merged_config(filename):
|
||||
"""
|
||||
Return a configuration after merging config file and command-line arguments.
|
||||
|
||||
:param str filename: file name to merge configuration settings with
|
||||
:return: merged configuration
|
||||
:rtype: dict
|
||||
"""
|
||||
# these are the defaults used in the config file
|
||||
default_log = os.path.join(constants.CORE_CONF_DIR, "logging.conf")
|
||||
default_grpc_port = "50051"
|
||||
default_address = "localhost"
|
||||
defaults = {
|
||||
"grpcport": default_grpc_port,
|
||||
"grpcaddress": default_address,
|
||||
"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}"
|
||||
)
|
||||
# parse command line options
|
||||
args = parser.parse_args()
|
||||
# convert ovs to internal format
|
||||
args.ovs = "1" if args.ovs else "0"
|
||||
# read the config file
|
||||
if args.configfile is not None:
|
||||
filename = args.configfile
|
||||
del args.configfile
|
||||
cfg = ConfigParser(defaults)
|
||||
cfg.read(filename)
|
||||
section = "core-daemon"
|
||||
if not cfg.has_section(section):
|
||||
cfg.add_section(section)
|
||||
# merge argparse with configparser
|
||||
for opt in vars(args):
|
||||
val = getattr(args, opt)
|
||||
if val is not None:
|
||||
cfg.set(section, opt, str(val))
|
||||
return dict(cfg.items(section))
|
||||
|
||||
|
||||
def main():
|
||||
"""
|
||||
Main program startup.
|
||||
|
||||
:return: nothing
|
||||
"""
|
||||
cfg = get_merged_config(f"{CORE_CONF_DIR}/core.conf")
|
||||
log_config_path = Path(cfg["logfile"])
|
||||
load_logging_config(log_config_path)
|
||||
banner()
|
||||
try:
|
||||
cored(cfg)
|
||||
except KeyboardInterrupt:
|
||||
logger.info("keyboard interrupt, stopping core daemon")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
50
daemon/core/scripts/gui.py
Executable file
50
daemon/core/scripts/gui.py
Executable file
|
@ -0,0 +1,50 @@
|
|||
import argparse
|
||||
import logging
|
||||
from logging.handlers import TimedRotatingFileHandler
|
||||
|
||||
from core.gui import appconfig, images
|
||||
from core.gui.app import Application
|
||||
|
||||
|
||||
def main() -> None:
|
||||
# parse flags
|
||||
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"
|
||||
)
|
||||
args = parser.parse_args()
|
||||
|
||||
# check home directory exists and create if necessary
|
||||
appconfig.check_directory()
|
||||
if args.create_dir:
|
||||
return
|
||||
|
||||
# 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
|
||||
)
|
||||
log_level = logging.getLevelName(args.level)
|
||||
logging.basicConfig(
|
||||
level=log_level, format=log_format, handlers=[stream_handler, file_handler]
|
||||
)
|
||||
logging.getLogger("PIL").setLevel(logging.ERROR)
|
||||
|
||||
# start app
|
||||
images.load_all()
|
||||
app = Application(args.proxy, args.session)
|
||||
app.mainloop()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
51
daemon/core/scripts/player.py
Executable file
51
daemon/core/scripts/player.py
Executable file
|
@ -0,0 +1,51 @@
|
|||
import argparse
|
||||
import logging
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
from core.player import CorePlayer
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def path_type(value: str) -> Path:
|
||||
file_path = Path(value)
|
||||
if not file_path.is_file():
|
||||
raise argparse.ArgumentTypeError(f"file does not exist: {value}")
|
||||
return file_path
|
||||
|
||||
|
||||
def parse_args() -> argparse.Namespace:
|
||||
"""
|
||||
Setup and parse command line arguments.
|
||||
|
||||
:return: parsed arguments
|
||||
"""
|
||||
parser = argparse.ArgumentParser(
|
||||
description="core player runs files that can move nodes and send commands",
|
||||
formatter_class=argparse.ArgumentDefaultsHelpFormatter,
|
||||
)
|
||||
parser.add_argument(
|
||||
"-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",
|
||||
)
|
||||
return parser.parse_args()
|
||||
|
||||
|
||||
def main() -> None:
|
||||
logging.basicConfig(level=logging.INFO)
|
||||
args = parse_args()
|
||||
player = CorePlayer(args.file)
|
||||
result = player.init(args.session)
|
||||
if not result:
|
||||
sys.exit(1)
|
||||
player.start()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
259
daemon/core/scripts/routemonitor.py
Executable file
259
daemon/core/scripts/routemonitor.py
Executable file
|
@ -0,0 +1,259 @@
|
|||
import argparse
|
||||
import enum
|
||||
import select
|
||||
import socket
|
||||
import subprocess
|
||||
import sys
|
||||
import time
|
||||
from argparse import ArgumentDefaultsHelpFormatter
|
||||
from functools import cmp_to_key
|
||||
from queue import Queue
|
||||
from threading import Thread
|
||||
from typing import Dict, Tuple
|
||||
|
||||
import grpc
|
||||
|
||||
from core import utils
|
||||
from core.api.grpc.client import CoreGrpcClient
|
||||
from core.api.grpc.wrappers import NodeType
|
||||
|
||||
SDT_HOST = "127.0.0.1"
|
||||
SDT_PORT = 50000
|
||||
ROUTE_LAYER = "CORE Route"
|
||||
DEAD_TIME = 3
|
||||
ROUTE_TIME = 3
|
||||
PACKET_CHOICES = ["udp", "tcp", "icmp"]
|
||||
|
||||
|
||||
class RouteEnum(enum.Enum):
|
||||
ADD = 0
|
||||
DEL = 1
|
||||
|
||||
|
||||
class SdtClient:
|
||||
def __init__(self, address: Tuple[str, int]) -> None:
|
||||
self.sock = socket.create_connection(address)
|
||||
self.links = []
|
||||
self.send(f'layer "{ROUTE_LAYER}"')
|
||||
|
||||
def close(self) -> None:
|
||||
self.sock.close()
|
||||
|
||||
def send(self, cmd: str) -> None:
|
||||
sdt_cmd = f"{cmd}\n".encode()
|
||||
self.sock.sendall(sdt_cmd)
|
||||
|
||||
def add_link(self, node1, node2) -> None:
|
||||
route_id = f"{node1}-{node2}-r"
|
||||
link_id = f"{node1},{node2},{route_id}"
|
||||
cmd = f'link {link_id} linkLayer "{ROUTE_LAYER}" line yellow,2'
|
||||
self.send(cmd)
|
||||
self.links.append(link_id)
|
||||
|
||||
def delete_links(self) -> None:
|
||||
for link_id in self.links:
|
||||
cmd = f"delete link,{link_id}"
|
||||
self.send(cmd)
|
||||
self.links.clear()
|
||||
|
||||
|
||||
class RouterMonitor:
|
||||
def __init__(
|
||||
self,
|
||||
session: int,
|
||||
src: str,
|
||||
dst: str,
|
||||
pkt: str,
|
||||
rate: int,
|
||||
dead: int,
|
||||
sdt_host: str,
|
||||
sdt_port: int,
|
||||
) -> None:
|
||||
self.queue = Queue()
|
||||
self.core = CoreGrpcClient()
|
||||
self.session = session
|
||||
self.src_id = None
|
||||
self.src = src
|
||||
self.dst = dst
|
||||
self.pkt = pkt
|
||||
self.rate = rate
|
||||
self.dead = dead
|
||||
self.seen = {}
|
||||
self.running = False
|
||||
self.route_time = None
|
||||
self.listeners = []
|
||||
self.sdt = SdtClient((sdt_host, sdt_port))
|
||||
self.nodes = self.get_nodes()
|
||||
|
||||
def get_nodes(self) -> Dict[int, str]:
|
||||
with self.core.context_connect():
|
||||
if self.session is None:
|
||||
self.session = self.get_session()
|
||||
print("session: ", self.session)
|
||||
try:
|
||||
session = self.core.get_session(self.session)
|
||||
node_map = {}
|
||||
for node in session.nodes.values():
|
||||
if node.type != NodeType.DEFAULT:
|
||||
continue
|
||||
node_map[node.id] = node.channel
|
||||
if self.src_id is None:
|
||||
_, ifaces, _ = self.core.get_node(self.session, node.id)
|
||||
for iface in ifaces:
|
||||
if self.src == iface.ip4:
|
||||
self.src_id = node.id
|
||||
break
|
||||
except grpc.RpcError:
|
||||
print(f"invalid session: {self.session}")
|
||||
sys.exit(1)
|
||||
if self.src_id is None:
|
||||
print(f"could not find node with source address: {self.src}")
|
||||
sys.exit(1)
|
||||
print(
|
||||
f"monitoring src_id ({self.src_id}) src({self.src}) dst({self.dst}) pkt({self.pkt})"
|
||||
)
|
||||
return node_map
|
||||
|
||||
def get_session(self) -> int:
|
||||
sessions = self.core.get_sessions()
|
||||
session = None
|
||||
if sessions:
|
||||
session = sessions[0]
|
||||
if not session:
|
||||
print("no current core sessions")
|
||||
sys.exit(1)
|
||||
return session.id
|
||||
|
||||
def start(self) -> None:
|
||||
self.running = True
|
||||
for node_id, node in self.nodes.items():
|
||||
print("listening on node: ", node)
|
||||
thread = Thread(target=self.listen, args=(node_id, node), daemon=True)
|
||||
thread.start()
|
||||
self.listeners.append(thread)
|
||||
self.manage()
|
||||
|
||||
def manage(self) -> None:
|
||||
self.route_time = time.monotonic()
|
||||
while self.running:
|
||||
route_enum, node, seen = self.queue.get()
|
||||
if route_enum == RouteEnum.ADD:
|
||||
self.seen[node] = seen
|
||||
elif node in self.seen:
|
||||
del self.seen[node]
|
||||
|
||||
if (time.monotonic() - self.route_time) >= self.rate:
|
||||
self.manage_routes()
|
||||
self.route_time = time.monotonic()
|
||||
|
||||
def route_sort(self, x: Tuple[str, int], y: Tuple[str, int]) -> int:
|
||||
x_node = x[0]
|
||||
y_node = y[0]
|
||||
if x_node == self.src_id:
|
||||
return 1
|
||||
if y_node == self.src_id:
|
||||
return -1
|
||||
x_ttl, y_ttl = x[1], y[1]
|
||||
return x_ttl - y_ttl
|
||||
|
||||
def manage_routes(self) -> None:
|
||||
self.sdt.delete_links()
|
||||
if not self.seen:
|
||||
return
|
||||
values = sorted(
|
||||
self.seen.items(), key=cmp_to_key(self.route_sort), reverse=True
|
||||
)
|
||||
print("current route:")
|
||||
for index, node_data in enumerate(values):
|
||||
next_index = index + 1
|
||||
if next_index == len(values):
|
||||
break
|
||||
next_node_id = values[next_index][0]
|
||||
node_id, ttl = node_data
|
||||
print(f"{node_id} -> {next_node_id}")
|
||||
self.sdt.add_link(node_id, next_node_id)
|
||||
|
||||
def stop(self) -> None:
|
||||
self.running = False
|
||||
self.sdt.delete_links()
|
||||
self.sdt.close()
|
||||
for thread in self.listeners:
|
||||
thread.join()
|
||||
self.listeners.clear()
|
||||
|
||||
def listen(self, node_id, node) -> None:
|
||||
cmd = f"tcpdump -lnvi any src host {self.src} and dst host {self.dst} and {self.pkt}"
|
||||
node_cmd = f"vcmd -c {node} -- {cmd}"
|
||||
p = subprocess.Popen(
|
||||
node_cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.DEVNULL
|
||||
)
|
||||
current = time.monotonic()
|
||||
try:
|
||||
while not p.poll() and self.running:
|
||||
ready, _, _ = select.select([p.stdout], [], [], 1)
|
||||
if ready:
|
||||
line = p.stdout.readline().strip().decode()
|
||||
if line:
|
||||
line = line.split("ttl", 1)[1]
|
||||
ttl = int(line.split(",", 1)[0])
|
||||
p.stdout.readline()
|
||||
self.queue.put((RouteEnum.ADD, node_id, ttl))
|
||||
current = time.monotonic()
|
||||
else:
|
||||
if (time.monotonic() - current) >= self.dead:
|
||||
self.queue.put((RouteEnum.DEL, node_id, None))
|
||||
except Exception as e:
|
||||
print(f"listener error: {e}")
|
||||
|
||||
|
||||
def main() -> None:
|
||||
if not utils.which("tcpdump", required=False):
|
||||
print("core-route-monitor requires tcpdump to be installed")
|
||||
return
|
||||
|
||||
desc = "core route monitor leverages tcpdump to monitor traffic and find route using TTL"
|
||||
parser = argparse.ArgumentParser(
|
||||
description=desc, formatter_class=ArgumentDefaultsHelpFormatter
|
||||
)
|
||||
parser.add_argument(
|
||||
"--src", required=True, help="source address for route monitoring"
|
||||
)
|
||||
parser.add_argument(
|
||||
"--dst", required=True, help="destination address for route monitoring"
|
||||
)
|
||||
parser.add_argument("--session", type=int, help="session to monitor route")
|
||||
parser.add_argument(
|
||||
"--pkt", default="icmp", choices=PACKET_CHOICES, help="packet type"
|
||||
)
|
||||
parser.add_argument(
|
||||
"--rate", type=int, default=ROUTE_TIME, help="rate to update route, in seconds"
|
||||
)
|
||||
parser.add_argument(
|
||||
"--dead",
|
||||
type=int,
|
||||
default=DEAD_TIME,
|
||||
help="timeout to declare path dead, in seconds",
|
||||
)
|
||||
parser.add_argument("--sdt-host", default=SDT_HOST, help="sdt host address")
|
||||
parser.add_argument("--sdt-port", type=int, default=SDT_PORT, help="sdt port")
|
||||
args = parser.parse_args()
|
||||
|
||||
monitor = RouterMonitor(
|
||||
args.session,
|
||||
args.src,
|
||||
args.dst,
|
||||
args.pkt,
|
||||
args.rate,
|
||||
args.dead,
|
||||
args.sdt_host,
|
||||
args.sdt_port,
|
||||
)
|
||||
try:
|
||||
monitor.start()
|
||||
except KeyboardInterrupt:
|
||||
monitor.stop()
|
||||
print("ending route monitor")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
71
daemon/core/scripts/serviceupdate.py
Executable file
71
daemon/core/scripts/serviceupdate.py
Executable file
|
@ -0,0 +1,71 @@
|
|||
import argparse
|
||||
import re
|
||||
from io import TextIOWrapper
|
||||
|
||||
|
||||
def parse_args() -> argparse.Namespace:
|
||||
parser = argparse.ArgumentParser(
|
||||
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()
|
||||
|
||||
|
||||
def update_service(service_file: TextIOWrapper) -> None:
|
||||
update = []
|
||||
for line in service_file.readlines():
|
||||
# update service attributes
|
||||
line = re.sub(r"^(\s+)_([a-z])", r"\1\2", line)
|
||||
# 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,
|
||||
)
|
||||
# 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,
|
||||
)
|
||||
# remove unwanted lines
|
||||
if re.search(r"addservice\(", line):
|
||||
continue
|
||||
if re.search(r"from.+\.ipaddr|import ipaddr", line):
|
||||
continue
|
||||
if re.search(r"from.+\.ipaddress|import ipaddress", line):
|
||||
continue
|
||||
# add modified line to make updated copy
|
||||
update.append(line)
|
||||
service_file.close()
|
||||
|
||||
with open(f"{service_file.name}.update", "w") as f:
|
||||
f.writelines(update)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
args = parse_args()
|
||||
update_service(args.file)
|
Loading…
Add table
Add a link
Reference in a new issue