commit
a57b838f19
48 changed files with 2582 additions and 536 deletions
21
CHANGELOG.md
21
CHANGELOG.md
|
@ -1,3 +1,24 @@
|
|||
## 2020-12-02 CORE 7.3.0
|
||||
|
||||
* core-daemon
|
||||
* fixed issue where emane global configuration was not being sent to core-gui
|
||||
* updated controlnet names on host to be prefixed with ctrl
|
||||
* fixed RJ45 link shutdown from core-gui causing an error
|
||||
* fixed emane external transport xml generation
|
||||
* \#517 - update to account for radvd required directory
|
||||
* \#514 - support added for session specific environment files
|
||||
* \#529 - updated to configure netem limit based on delay or user specified, requires kernel 3.3+
|
||||
* core-pygui
|
||||
* fixed issue drawing wlan/emane link options when it should not have
|
||||
* edge labels are now placed a set distance from nodes like original gui
|
||||
* link color/width are now saved to xml files
|
||||
* added support to configure buffer size for links
|
||||
* \#525 - added support for multiple wired links between the same nodes
|
||||
* \#526 - added option to hide/show links with 100% loss
|
||||
* Documentation
|
||||
* \#527 - typo in service documentation
|
||||
* \#515 - added examples to docs for using EMANE features within a CORE context
|
||||
|
||||
## 2020-09-29 CORE 7.2.1
|
||||
|
||||
* core-daemon
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
# Process this file with autoconf to produce a configure script.
|
||||
|
||||
# this defines the CORE version number, must be static for AC_INIT
|
||||
AC_INIT(core, 7.2.1)
|
||||
AC_INIT(core, 7.3.0)
|
||||
|
||||
# autoconf and automake initialization
|
||||
AC_CONFIG_SRCDIR([netns/version.h.in])
|
||||
|
|
|
@ -128,6 +128,7 @@ def add_link_data(
|
|||
options.mer = options_proto.mer
|
||||
options.burst = options_proto.burst
|
||||
options.mburst = options_proto.mburst
|
||||
options.buffer = options_proto.buffer
|
||||
options.unidirectional = options_proto.unidirectional
|
||||
options.key = options_proto.key
|
||||
return iface1_data, iface2_data, options, link_type
|
||||
|
@ -329,6 +330,7 @@ def convert_link_options(options_data: LinkOptions) -> core_pb2.LinkOptions:
|
|||
burst=options_data.burst,
|
||||
delay=options_data.delay,
|
||||
dup=options_data.dup,
|
||||
buffer=options_data.buffer,
|
||||
unidirectional=options_data.unidirectional,
|
||||
)
|
||||
|
||||
|
|
|
@ -972,6 +972,7 @@ class CoreGrpcServer(core_pb2_grpc.CoreApiServicer):
|
|||
mburst=options_proto.mburst,
|
||||
unidirectional=options_proto.unidirectional,
|
||||
key=options_proto.key,
|
||||
buffer=options_proto.buffer,
|
||||
)
|
||||
session.update_link(node1_id, node2_id, iface1_id, iface2_id, options)
|
||||
iface1 = InterfaceData(id=iface1_id)
|
||||
|
|
|
@ -457,6 +457,7 @@ class LinkOptions:
|
|||
delay: int = 0
|
||||
dup: int = 0
|
||||
unidirectional: bool = False
|
||||
buffer: int = 0
|
||||
|
||||
@classmethod
|
||||
def from_proto(cls, proto: core_pb2.LinkOptions) -> "LinkOptions":
|
||||
|
@ -471,6 +472,7 @@ class LinkOptions:
|
|||
delay=proto.delay,
|
||||
dup=proto.dup,
|
||||
unidirectional=proto.unidirectional,
|
||||
buffer=proto.buffer,
|
||||
)
|
||||
|
||||
def to_proto(self) -> core_pb2.LinkOptions:
|
||||
|
@ -485,6 +487,7 @@ class LinkOptions:
|
|||
delay=self.delay,
|
||||
dup=self.dup,
|
||||
unidirectional=self.unidirectional,
|
||||
buffer=self.buffer,
|
||||
)
|
||||
|
||||
|
||||
|
|
|
@ -52,6 +52,7 @@ from core.errors import CoreCommandError, CoreError
|
|||
from core.location.mobility import BasicRangeModel
|
||||
from core.nodes.base import CoreNode, CoreNodeBase, NodeBase
|
||||
from core.nodes.network import WlanNode
|
||||
from core.nodes.physical import Rj45Node
|
||||
from core.services.coreservices import ServiceManager, ServiceShim
|
||||
|
||||
|
||||
|
@ -787,20 +788,30 @@ class CoreHandler(socketserver.BaseRequestHandler):
|
|||
options = LinkOptions()
|
||||
options.delay = message.get_tlv(LinkTlvs.DELAY.value)
|
||||
options.bandwidth = message.get_tlv(LinkTlvs.BANDWIDTH.value)
|
||||
options.loss = message.get_tlv(LinkTlvs.LOSS.value)
|
||||
options.dup = message.get_tlv(LinkTlvs.DUP.value)
|
||||
options.jitter = message.get_tlv(LinkTlvs.JITTER.value)
|
||||
options.mer = message.get_tlv(LinkTlvs.MER.value)
|
||||
options.burst = message.get_tlv(LinkTlvs.BURST.value)
|
||||
options.mburst = message.get_tlv(LinkTlvs.MBURST.value)
|
||||
options.unidirectional = message.get_tlv(LinkTlvs.UNIDIRECTIONAL.value)
|
||||
options.key = message.get_tlv(LinkTlvs.KEY.value)
|
||||
loss = message.get_tlv(LinkTlvs.LOSS.value)
|
||||
dup = message.get_tlv(LinkTlvs.DUP.value)
|
||||
if loss is not None:
|
||||
options.loss = float(loss)
|
||||
if dup is not None:
|
||||
options.dup = int(dup)
|
||||
|
||||
if message.flags & MessageFlags.ADD.value:
|
||||
self.session.add_link(
|
||||
node1_id, node2_id, iface1_data, iface2_data, options, link_type
|
||||
)
|
||||
elif message.flags & MessageFlags.DELETE.value:
|
||||
node1 = self.session.get_node(node1_id, NodeBase)
|
||||
node2 = self.session.get_node(node2_id, NodeBase)
|
||||
if isinstance(node1, Rj45Node):
|
||||
iface1_data.id = node1.iface_id
|
||||
if isinstance(node2, Rj45Node):
|
||||
iface2_data.id = node2.iface_id
|
||||
self.session.delete_link(
|
||||
node1_id, node2_id, iface1_data.id, iface2_data.id, link_type
|
||||
)
|
||||
|
@ -1851,7 +1862,15 @@ class CoreHandler(socketserver.BaseRequestHandler):
|
|||
)
|
||||
self.session.broadcast_config(config_data)
|
||||
|
||||
# send emane model info
|
||||
# send global emane config
|
||||
config = self.session.emane.get_configs()
|
||||
logging.debug("global emane config: values(%s)", config)
|
||||
config_data = ConfigShim.config_data(
|
||||
0, None, ConfigFlags.UPDATE.value, self.session.emane.emane_config, config
|
||||
)
|
||||
self.session.broadcast_config(config_data)
|
||||
|
||||
# send emane model configs
|
||||
for node_id in self.session.emane.nodes():
|
||||
emane_configs = self.session.emane.get_all_configs(node_id)
|
||||
for model_name in emane_configs:
|
||||
|
|
|
@ -5,7 +5,7 @@ from core.config import Configuration
|
|||
from core.configservice.base import ConfigService, ConfigServiceMode
|
||||
from core.emane.nodes import EmaneNet
|
||||
from core.nodes.base import CoreNodeBase
|
||||
from core.nodes.interface import CoreInterface
|
||||
from core.nodes.interface import DEFAULT_MTU, CoreInterface
|
||||
from core.nodes.network import WlanNode
|
||||
|
||||
GROUP: str = "FRR"
|
||||
|
@ -18,7 +18,7 @@ def has_mtu_mismatch(iface: CoreInterface) -> bool:
|
|||
mtu-ignore command. This is needed when e.g. a node is linked via a
|
||||
GreTap device.
|
||||
"""
|
||||
if iface.mtu != 1500:
|
||||
if iface.mtu != DEFAULT_MTU:
|
||||
return True
|
||||
if not iface.net:
|
||||
return False
|
||||
|
|
|
@ -6,7 +6,7 @@ from core.config import Configuration
|
|||
from core.configservice.base import ConfigService, ConfigServiceMode
|
||||
from core.emane.nodes import EmaneNet
|
||||
from core.nodes.base import CoreNodeBase
|
||||
from core.nodes.interface import CoreInterface
|
||||
from core.nodes.interface import DEFAULT_MTU, CoreInterface
|
||||
from core.nodes.network import WlanNode
|
||||
|
||||
GROUP: str = "Quagga"
|
||||
|
@ -19,7 +19,7 @@ def has_mtu_mismatch(iface: CoreInterface) -> bool:
|
|||
mtu-ignore command. This is needed when e.g. a node is linked via a
|
||||
GreTap device.
|
||||
"""
|
||||
if iface.mtu != 1500:
|
||||
if iface.mtu != DEFAULT_MTU:
|
||||
return True
|
||||
if not iface.net:
|
||||
return False
|
||||
|
|
|
@ -217,7 +217,7 @@ class PcapService(ConfigService):
|
|||
class RadvdService(ConfigService):
|
||||
name: str = "radvd"
|
||||
group: str = GROUP_NAME
|
||||
directories: List[str] = ["/etc/radvd"]
|
||||
directories: List[str] = ["/etc/radvd", "/var/run/radvd"]
|
||||
files: List[str] = ["/etc/radvd/radvd.conf"]
|
||||
executables: List[str] = ["radvd"]
|
||||
dependencies: List[str] = []
|
||||
|
|
|
@ -172,6 +172,7 @@ class LinkOptions:
|
|||
mburst: int = None
|
||||
unidirectional: int = None
|
||||
key: int = None
|
||||
buffer: int = None
|
||||
|
||||
|
||||
@dataclass
|
||||
|
|
|
@ -12,6 +12,7 @@ import sys
|
|||
import tempfile
|
||||
import threading
|
||||
import time
|
||||
from pathlib import Path
|
||||
from typing import Any, Callable, Dict, List, Optional, Set, Tuple, Type, TypeVar
|
||||
|
||||
from core import constants, utils
|
||||
|
@ -997,28 +998,25 @@ class Session:
|
|||
env["SESSION_USER"] = str(self.user)
|
||||
if state:
|
||||
env["SESSION_STATE"] = str(self.state)
|
||||
# attempt to read and add environment config file
|
||||
environment_config_file = os.path.join(constants.CORE_CONF_DIR, "environment")
|
||||
try:
|
||||
if os.path.isfile(environment_config_file):
|
||||
utils.load_config(environment_config_file, env)
|
||||
except IOError:
|
||||
logging.warning(
|
||||
"environment configuration file does not exist: %s",
|
||||
environment_config_file,
|
||||
)
|
||||
# attempt to read and add user environment file
|
||||
# try reading and merging optional environments from:
|
||||
# /etc/core/environment
|
||||
# /home/user/.core/environment
|
||||
# /tmp/pycore.<session id>/environment
|
||||
core_env_path = Path(constants.CORE_CONF_DIR) / "environment"
|
||||
session_env_path = Path(self.session_dir) / "environment"
|
||||
if self.user:
|
||||
environment_user_file = os.path.join(
|
||||
"/home", self.user, ".core", "environment"
|
||||
)
|
||||
try:
|
||||
utils.load_config(environment_user_file, env)
|
||||
except IOError:
|
||||
logging.debug(
|
||||
"user core environment settings file not present: %s",
|
||||
environment_user_file,
|
||||
)
|
||||
user_home_path = Path(f"~{self.user}").expanduser()
|
||||
user_env1 = user_home_path / ".core" / "environment"
|
||||
user_env2 = user_home_path / ".coregui" / "environment"
|
||||
paths = [core_env_path, user_env1, user_env2, session_env_path]
|
||||
else:
|
||||
paths = [core_env_path, session_env_path]
|
||||
for path in paths:
|
||||
if path.is_file():
|
||||
try:
|
||||
utils.load_config(path, env)
|
||||
except IOError:
|
||||
logging.exception("error reading environment file: %s", path)
|
||||
return env
|
||||
|
||||
def set_thumbnail(self, thumb_file: str) -> None:
|
||||
|
@ -1447,12 +1445,14 @@ class Session:
|
|||
)
|
||||
control_net = self.create_node(
|
||||
CtrlNet,
|
||||
True,
|
||||
prefix,
|
||||
start=False,
|
||||
prefix=prefix,
|
||||
_id=_id,
|
||||
updown_script=updown_script,
|
||||
serverintf=server_iface,
|
||||
)
|
||||
control_net.brname = f"ctrl{net_index}.{self.short_session_id()}"
|
||||
control_net.startup()
|
||||
return control_net
|
||||
|
||||
def add_remove_control_iface(
|
||||
|
|
|
@ -87,14 +87,14 @@ class CoreClient:
|
|||
self.read_config()
|
||||
|
||||
# helpers
|
||||
self.iface_to_edge: Dict[Tuple[int, ...], Tuple[int, ...]] = {}
|
||||
self.iface_to_edge: Dict[Tuple[int, ...], CanvasEdge] = {}
|
||||
self.ifaces_manager: InterfaceManager = InterfaceManager(self.app)
|
||||
self.observer: Optional[str] = None
|
||||
|
||||
# session data
|
||||
self.mobility_players: Dict[int, MobilityPlayer] = {}
|
||||
self.canvas_nodes: Dict[int, CanvasNode] = {}
|
||||
self.links: Dict[Tuple[int, int], CanvasEdge] = {}
|
||||
self.links: Dict[str, CanvasEdge] = {}
|
||||
self.handling_throughputs: Optional[grpc.Future] = None
|
||||
self.handling_cpu_usage: Optional[grpc.Future] = None
|
||||
self.handling_events: Optional[grpc.Future] = None
|
||||
|
@ -225,11 +225,9 @@ class CoreClient:
|
|||
self.app.canvas.add_wired_edge(canvas_node1, canvas_node2, event.link)
|
||||
self.app.canvas.organize()
|
||||
elif event.message_type == MessageType.DELETE:
|
||||
self.app.canvas.delete_wired_edge(canvas_node1, canvas_node2)
|
||||
self.app.canvas.delete_wired_edge(event.link)
|
||||
elif event.message_type == MessageType.NONE:
|
||||
self.app.canvas.update_wired_edge(
|
||||
canvas_node1, canvas_node2, event.link
|
||||
)
|
||||
self.app.canvas.update_wired_edge(event.link)
|
||||
else:
|
||||
logging.warning("unknown link event: %s", event)
|
||||
|
||||
|
@ -383,6 +381,17 @@ class CoreClient:
|
|||
except ValueError:
|
||||
logging.exception("unknown shape: %s", shape_type)
|
||||
|
||||
# load edges config
|
||||
edges_config = config.get("edges")
|
||||
if edges_config:
|
||||
edges_config = json.loads(edges_config)
|
||||
logging.info("edges config: %s", edges_config)
|
||||
for edge_config in edges_config:
|
||||
edge = self.links[edge_config["token"]]
|
||||
edge.width = edge_config["width"]
|
||||
edge.color = edge_config["color"]
|
||||
edge.redraw()
|
||||
|
||||
def create_new_session(self) -> None:
|
||||
"""
|
||||
Create a new session
|
||||
|
@ -570,7 +579,15 @@ class CoreClient:
|
|||
shapes.append(shape.metadata())
|
||||
shapes = json.dumps(shapes)
|
||||
|
||||
metadata = {"canvas": canvas_config, "shapes": shapes}
|
||||
# create edges config
|
||||
edges_config = []
|
||||
for edge in self.links.values():
|
||||
edge_config = dict(token=edge.token, width=edge.width, color=edge.color)
|
||||
edges_config.append(edge_config)
|
||||
edges_config = json.dumps(edges_config)
|
||||
|
||||
# save metadata
|
||||
metadata = dict(canvas=canvas_config, shapes=shapes, edges=edges_config)
|
||||
response = self.client.set_session_metadata(self.session.id, metadata)
|
||||
logging.debug("set session metadata %s, result: %s", metadata, response)
|
||||
|
||||
|
@ -877,7 +894,7 @@ class CoreClient:
|
|||
|
||||
def create_link(
|
||||
self, edge: CanvasEdge, canvas_src_node: CanvasNode, canvas_dst_node: CanvasNode
|
||||
) -> None:
|
||||
) -> Link:
|
||||
"""
|
||||
Create core link for a pair of canvas nodes, with token referencing
|
||||
the canvas edge.
|
||||
|
@ -888,15 +905,9 @@ class CoreClient:
|
|||
src_iface = None
|
||||
if NodeUtils.is_container_node(src_node.type):
|
||||
src_iface = self.create_iface(canvas_src_node)
|
||||
self.iface_to_edge[(src_node.id, src_iface.id)] = edge.token
|
||||
edge.src_iface = src_iface
|
||||
canvas_src_node.ifaces[src_iface.id] = src_iface
|
||||
dst_iface = None
|
||||
if NodeUtils.is_container_node(dst_node.type):
|
||||
dst_iface = self.create_iface(canvas_dst_node)
|
||||
self.iface_to_edge[(dst_node.id, dst_iface.id)] = edge.token
|
||||
edge.dst_iface = dst_iface
|
||||
canvas_dst_node.ifaces[dst_iface.id] = dst_iface
|
||||
link = Link(
|
||||
type=LinkType.WIRED,
|
||||
node1_id=src_node.id,
|
||||
|
@ -904,9 +915,21 @@ class CoreClient:
|
|||
iface1=src_iface,
|
||||
iface2=dst_iface,
|
||||
)
|
||||
edge.set_link(link)
|
||||
self.links[edge.token] = edge
|
||||
logging.info("added link between %s and %s", src_node.name, dst_node.name)
|
||||
return link
|
||||
|
||||
def save_edge(
|
||||
self, edge: CanvasEdge, canvas_src_node: CanvasNode, canvas_dst_node: CanvasNode
|
||||
) -> None:
|
||||
self.links[edge.token] = edge
|
||||
src_node = canvas_src_node.core_node
|
||||
dst_node = canvas_dst_node.core_node
|
||||
if NodeUtils.is_container_node(src_node.type):
|
||||
src_iface_id = edge.link.iface1.id
|
||||
self.iface_to_edge[(src_node.id, src_iface_id)] = edge
|
||||
if NodeUtils.is_container_node(dst_node.type):
|
||||
dst_iface_id = edge.link.iface2.id
|
||||
self.iface_to_edge[(dst_node.id, dst_iface_id)] = edge
|
||||
|
||||
def get_wlan_configs_proto(self) -> List[wlan_pb2.WlanConfig]:
|
||||
configs = []
|
||||
|
|
340
daemon/core/gui/data/xmls/emane-demo-antenna.xml
Normal file
340
daemon/core/gui/data/xmls/emane-demo-antenna.xml
Normal file
|
@ -0,0 +1,340 @@
|
|||
<?xml version='1.0' encoding='UTF-8'?>
|
||||
<scenario name="/home/developer/.core/configs/emane-demo-antenna.xml">
|
||||
<networks>
|
||||
<network id="5" name="wlan5" model="emane_rfpipe" type="EMANE">
|
||||
<position x="388" y="555" lat="47.57412169587584" lon="-122.12709380504643" alt="2.0"/>
|
||||
</network>
|
||||
</networks>
|
||||
<devices>
|
||||
<device id="1" name="n1" type="mdr" class="" image="">
|
||||
<position x="258" y="147" lat="47.577830502987744" lon="-122.12884551985047" alt="2.0"/>
|
||||
<services>
|
||||
<service name="zebra"/>
|
||||
<service name="OSPFv3MDR"/>
|
||||
<service name="IPForward"/>
|
||||
</services>
|
||||
</device>
|
||||
<device id="2" name="n2" type="mdr" class="" image="">
|
||||
<position x="526" y="147" lat="47.577830502987744" lon="-122.12523429240828" alt="2.0"/>
|
||||
<services>
|
||||
<service name="zebra"/>
|
||||
<service name="OSPFv3MDR"/>
|
||||
<service name="IPForward"/>
|
||||
</services>
|
||||
</device>
|
||||
<device id="3" name="n3" type="mdr" class="" image="">
|
||||
<position x="241" y="387" lat="47.57564888355958" lon="-122.12907459024791" alt="2.0"/>
|
||||
<services>
|
||||
<service name="zebra"/>
|
||||
<service name="OSPFv3MDR"/>
|
||||
<service name="IPForward"/>
|
||||
</services>
|
||||
</device>
|
||||
<device id="4" name="n4" type="mdr" class="" image="">
|
||||
<position x="529" y="385" lat="47.57566706409707" lon="-122.1251938682205" alt="2.0"/>
|
||||
<services>
|
||||
<service name="zebra"/>
|
||||
<service name="OSPFv3MDR"/>
|
||||
<service name="IPForward"/>
|
||||
</services>
|
||||
</device>
|
||||
</devices>
|
||||
<links>
|
||||
<link node1="5" node2="1">
|
||||
<iface2 nem="1" id="0" name="eth0" mac="02:02:00:00:00:01" ip4="10.0.0.1" ip4_mask="32" ip6="2001::1" ip6_mask="128"/>
|
||||
</link>
|
||||
<link node1="5" node2="2">
|
||||
<iface2 nem="2" id="0" name="eth0" mac="02:02:00:00:00:02" ip4="10.0.0.2" ip4_mask="32" ip6="2001::2" ip6_mask="128"/>
|
||||
</link>
|
||||
<link node1="5" node2="3">
|
||||
<iface2 nem="3" id="0" name="eth0" mac="02:02:00:00:00:03" ip4="10.0.0.3" ip4_mask="32" ip6="2001::3" ip6_mask="128"/>
|
||||
</link>
|
||||
<link node1="5" node2="4">
|
||||
<iface2 nem="4" id="0" name="eth0" mac="02:02:00:00:00:04" ip4="10.0.0.4" ip4_mask="32" ip6="2001::4" ip6_mask="128"/>
|
||||
</link>
|
||||
</links>
|
||||
<emane_global_configuration>
|
||||
<emulator>
|
||||
<configuration name="antennaprofilemanifesturi" value="/tmp/emane/antennaprofile.xml"/>
|
||||
<configuration name="controlportendpoint" value="0.0.0.0:47000"/>
|
||||
<configuration name="eventservicedevice" value="ctrl0"/>
|
||||
<configuration name="eventservicegroup" value="224.1.2.8:45703"/>
|
||||
<configuration name="eventservicettl" value="1"/>
|
||||
<configuration name="otamanagerchannelenable" value="1"/>
|
||||
<configuration name="otamanagerdevice" value="ctrl0"/>
|
||||
<configuration name="otamanagergroup" value="224.1.2.8:45702"/>
|
||||
<configuration name="otamanagerloopback" value="0"/>
|
||||
<configuration name="otamanagermtu" value="0"/>
|
||||
<configuration name="otamanagerpartcheckthreshold" value="2"/>
|
||||
<configuration name="otamanagerparttimeoutthreshold" value="5"/>
|
||||
<configuration name="otamanagerttl" value="1"/>
|
||||
<configuration name="stats.event.maxeventcountrows" value="0"/>
|
||||
<configuration name="stats.ota.maxeventcountrows" value="0"/>
|
||||
<configuration name="stats.ota.maxpacketcountrows" value="0"/>
|
||||
</emulator>
|
||||
<core>
|
||||
<configuration name="platform_id_start" value="1"/>
|
||||
<configuration name="nem_id_start" value="1"/>
|
||||
<configuration name="link_enabled" value="1"/>
|
||||
<configuration name="loss_threshold" value="30"/>
|
||||
<configuration name="link_interval" value="1"/>
|
||||
<configuration name="link_timeout" value="4"/>
|
||||
</core>
|
||||
</emane_global_configuration>
|
||||
<emane_configurations>
|
||||
<emane_configuration node="5" model="emane_rfpipe">
|
||||
<mac>
|
||||
<configuration name="datarate" value="1000000"/>
|
||||
<configuration name="delay" value="0.000000"/>
|
||||
<configuration name="enablepromiscuousmode" value="0"/>
|
||||
<configuration name="flowcontrolenable" value="0"/>
|
||||
<configuration name="flowcontroltokens" value="10"/>
|
||||
<configuration name="jitter" value="0.000000"/>
|
||||
<configuration name="neighbormetricdeletetime" value="60.000000"/>
|
||||
<configuration name="pcrcurveuri" value="/usr/share/emane/xml/models/mac/rfpipe/rfpipepcr.xml"/>
|
||||
<configuration name="radiometricenable" value="0"/>
|
||||
<configuration name="radiometricreportinterval" value="1.000000"/>
|
||||
</mac>
|
||||
<phy>
|
||||
<configuration name="bandwidth" value="1000000"/>
|
||||
<configuration name="fading.model" value="none"/>
|
||||
<configuration name="fading.nakagami.distance0" value="100.000000"/>
|
||||
<configuration name="fading.nakagami.distance1" value="250.000000"/>
|
||||
<configuration name="fading.nakagami.m0" value="0.750000"/>
|
||||
<configuration name="fading.nakagami.m1" value="1.000000"/>
|
||||
<configuration name="fading.nakagami.m2" value="200.000000"/>
|
||||
<configuration name="fixedantennagain" value="0.000000"/>
|
||||
<configuration name="fixedantennagainenable" value="1"/>
|
||||
<configuration name="frequency" value="2347000000"/>
|
||||
<configuration name="frequencyofinterest" value="2347000000"/>
|
||||
<configuration name="noisebinsize" value="20"/>
|
||||
<configuration name="noisemaxclampenable" value="0"/>
|
||||
<configuration name="noisemaxmessagepropagation" value="200000"/>
|
||||
<configuration name="noisemaxsegmentduration" value="1000000"/>
|
||||
<configuration name="noisemaxsegmentoffset" value="300000"/>
|
||||
<configuration name="noisemode" value="none"/>
|
||||
<configuration name="propagationmodel" value="2ray"/>
|
||||
<configuration name="subid" value="1"/>
|
||||
<configuration name="systemnoisefigure" value="4.000000"/>
|
||||
<configuration name="timesyncthreshold" value="10000"/>
|
||||
<configuration name="txpower" value="0.000000"/>
|
||||
</phy>
|
||||
<external>
|
||||
<configuration name="external" value="0"/>
|
||||
<configuration name="platformendpoint" value="127.0.0.1:40001"/>
|
||||
<configuration name="transportendpoint" value="127.0.0.1:50002"/>
|
||||
</external>
|
||||
</emane_configuration>
|
||||
<emane_configuration node="1" model="emane_rfpipe">
|
||||
<mac>
|
||||
<configuration name="datarate" value="1000000"/>
|
||||
<configuration name="delay" value="0.000000"/>
|
||||
<configuration name="enablepromiscuousmode" value="0"/>
|
||||
<configuration name="flowcontrolenable" value="0"/>
|
||||
<configuration name="flowcontroltokens" value="10"/>
|
||||
<configuration name="jitter" value="0.000000"/>
|
||||
<configuration name="neighbormetricdeletetime" value="60.000000"/>
|
||||
<configuration name="pcrcurveuri" value="/usr/share/emane/xml/models/mac/rfpipe/rfpipepcr.xml"/>
|
||||
<configuration name="radiometricenable" value="0"/>
|
||||
<configuration name="radiometricreportinterval" value="1.000000"/>
|
||||
</mac>
|
||||
<phy>
|
||||
<configuration name="bandwidth" value="1000000"/>
|
||||
<configuration name="fading.model" value="none"/>
|
||||
<configuration name="fading.nakagami.distance0" value="100.000000"/>
|
||||
<configuration name="fading.nakagami.distance1" value="250.000000"/>
|
||||
<configuration name="fading.nakagami.m0" value="0.750000"/>
|
||||
<configuration name="fading.nakagami.m1" value="1.000000"/>
|
||||
<configuration name="fading.nakagami.m2" value="200.000000"/>
|
||||
<configuration name="fixedantennagain" value="0.000000"/>
|
||||
<configuration name="fixedantennagainenable" value="0"/>
|
||||
<configuration name="frequency" value="2347000000"/>
|
||||
<configuration name="frequencyofinterest" value="2347000000"/>
|
||||
<configuration name="noisebinsize" value="20"/>
|
||||
<configuration name="noisemaxclampenable" value="0"/>
|
||||
<configuration name="noisemaxmessagepropagation" value="200000"/>
|
||||
<configuration name="noisemaxsegmentduration" value="1000000"/>
|
||||
<configuration name="noisemaxsegmentoffset" value="300000"/>
|
||||
<configuration name="noisemode" value="outofband"/>
|
||||
<configuration name="propagationmodel" value="precomputed"/>
|
||||
<configuration name="subid" value="1"/>
|
||||
<configuration name="systemnoisefigure" value="4.000000"/>
|
||||
<configuration name="timesyncthreshold" value="10000"/>
|
||||
<configuration name="txpower" value="0.000000"/>
|
||||
</phy>
|
||||
<external>
|
||||
<configuration name="external" value="0"/>
|
||||
<configuration name="platformendpoint" value="127.0.0.1:40001"/>
|
||||
<configuration name="transportendpoint" value="127.0.0.1:50002"/>
|
||||
</external>
|
||||
</emane_configuration>
|
||||
<emane_configuration node="2" model="emane_rfpipe">
|
||||
<mac>
|
||||
<configuration name="datarate" value="1000000"/>
|
||||
<configuration name="delay" value="0.000000"/>
|
||||
<configuration name="enablepromiscuousmode" value="0"/>
|
||||
<configuration name="flowcontrolenable" value="0"/>
|
||||
<configuration name="flowcontroltokens" value="10"/>
|
||||
<configuration name="jitter" value="0.000000"/>
|
||||
<configuration name="neighbormetricdeletetime" value="60.000000"/>
|
||||
<configuration name="pcrcurveuri" value="/usr/share/emane/xml/models/mac/rfpipe/rfpipepcr.xml"/>
|
||||
<configuration name="radiometricenable" value="0"/>
|
||||
<configuration name="radiometricreportinterval" value="1.000000"/>
|
||||
</mac>
|
||||
<phy>
|
||||
<configuration name="bandwidth" value="1000000"/>
|
||||
<configuration name="fading.model" value="none"/>
|
||||
<configuration name="fading.nakagami.distance0" value="100.000000"/>
|
||||
<configuration name="fading.nakagami.distance1" value="250.000000"/>
|
||||
<configuration name="fading.nakagami.m0" value="0.750000"/>
|
||||
<configuration name="fading.nakagami.m1" value="1.000000"/>
|
||||
<configuration name="fading.nakagami.m2" value="200.000000"/>
|
||||
<configuration name="fixedantennagain" value="0.000000"/>
|
||||
<configuration name="fixedantennagainenable" value="1"/>
|
||||
<configuration name="frequency" value="2347000000"/>
|
||||
<configuration name="frequencyofinterest" value="2347000000"/>
|
||||
<configuration name="noisebinsize" value="20"/>
|
||||
<configuration name="noisemaxclampenable" value="0"/>
|
||||
<configuration name="noisemaxmessagepropagation" value="200000"/>
|
||||
<configuration name="noisemaxsegmentduration" value="1000000"/>
|
||||
<configuration name="noisemaxsegmentoffset" value="300000"/>
|
||||
<configuration name="noisemode" value="outofband"/>
|
||||
<configuration name="propagationmodel" value="precomputed"/>
|
||||
<configuration name="subid" value="1"/>
|
||||
<configuration name="systemnoisefigure" value="4.000000"/>
|
||||
<configuration name="timesyncthreshold" value="10000"/>
|
||||
<configuration name="txpower" value="0.000000"/>
|
||||
</phy>
|
||||
<external>
|
||||
<configuration name="external" value="0"/>
|
||||
<configuration name="platformendpoint" value="127.0.0.1:40001"/>
|
||||
<configuration name="transportendpoint" value="127.0.0.1:50002"/>
|
||||
</external>
|
||||
</emane_configuration>
|
||||
<emane_configuration node="3" model="emane_rfpipe">
|
||||
<mac>
|
||||
<configuration name="datarate" value="1000000"/>
|
||||
<configuration name="delay" value="0.000000"/>
|
||||
<configuration name="enablepromiscuousmode" value="0"/>
|
||||
<configuration name="flowcontrolenable" value="0"/>
|
||||
<configuration name="flowcontroltokens" value="10"/>
|
||||
<configuration name="jitter" value="0.000000"/>
|
||||
<configuration name="neighbormetricdeletetime" value="60.000000"/>
|
||||
<configuration name="pcrcurveuri" value="/usr/share/emane/xml/models/mac/rfpipe/rfpipepcr.xml"/>
|
||||
<configuration name="radiometricenable" value="0"/>
|
||||
<configuration name="radiometricreportinterval" value="1.000000"/>
|
||||
</mac>
|
||||
<phy>
|
||||
<configuration name="bandwidth" value="1000000"/>
|
||||
<configuration name="fading.model" value="none"/>
|
||||
<configuration name="fading.nakagami.distance0" value="100.000000"/>
|
||||
<configuration name="fading.nakagami.distance1" value="250.000000"/>
|
||||
<configuration name="fading.nakagami.m0" value="0.750000"/>
|
||||
<configuration name="fading.nakagami.m1" value="1.000000"/>
|
||||
<configuration name="fading.nakagami.m2" value="200.000000"/>
|
||||
<configuration name="fixedantennagain" value="5.000000"/>
|
||||
<configuration name="fixedantennagainenable" value="1"/>
|
||||
<configuration name="frequency" value="2347000000"/>
|
||||
<configuration name="frequencyofinterest" value="2347000000"/>
|
||||
<configuration name="noisebinsize" value="20"/>
|
||||
<configuration name="noisemaxclampenable" value="0"/>
|
||||
<configuration name="noisemaxmessagepropagation" value="200000"/>
|
||||
<configuration name="noisemaxsegmentduration" value="1000000"/>
|
||||
<configuration name="noisemaxsegmentoffset" value="300000"/>
|
||||
<configuration name="noisemode" value="outofband"/>
|
||||
<configuration name="propagationmodel" value="precomputed"/>
|
||||
<configuration name="subid" value="1"/>
|
||||
<configuration name="systemnoisefigure" value="4.000000"/>
|
||||
<configuration name="timesyncthreshold" value="10000"/>
|
||||
<configuration name="txpower" value="0.000000"/>
|
||||
</phy>
|
||||
<external>
|
||||
<configuration name="external" value="0"/>
|
||||
<configuration name="platformendpoint" value="127.0.0.1:40001"/>
|
||||
<configuration name="transportendpoint" value="127.0.0.1:50002"/>
|
||||
</external>
|
||||
</emane_configuration>
|
||||
<emane_configuration node="4" model="emane_rfpipe">
|
||||
<mac>
|
||||
<configuration name="datarate" value="1000000"/>
|
||||
<configuration name="delay" value="0.000000"/>
|
||||
<configuration name="enablepromiscuousmode" value="0"/>
|
||||
<configuration name="flowcontrolenable" value="0"/>
|
||||
<configuration name="flowcontroltokens" value="10"/>
|
||||
<configuration name="jitter" value="0.000000"/>
|
||||
<configuration name="neighbormetricdeletetime" value="60.000000"/>
|
||||
<configuration name="pcrcurveuri" value="/usr/share/emane/xml/models/mac/rfpipe/rfpipepcr.xml"/>
|
||||
<configuration name="radiometricenable" value="0"/>
|
||||
<configuration name="radiometricreportinterval" value="1.000000"/>
|
||||
</mac>
|
||||
<phy>
|
||||
<configuration name="bandwidth" value="1000000"/>
|
||||
<configuration name="fading.model" value="none"/>
|
||||
<configuration name="fading.nakagami.distance0" value="100.000000"/>
|
||||
<configuration name="fading.nakagami.distance1" value="250.000000"/>
|
||||
<configuration name="fading.nakagami.m0" value="0.750000"/>
|
||||
<configuration name="fading.nakagami.m1" value="1.000000"/>
|
||||
<configuration name="fading.nakagami.m2" value="200.000000"/>
|
||||
<configuration name="fixedantennagain" value="0.000000"/>
|
||||
<configuration name="fixedantennagainenable" value="0"/>
|
||||
<configuration name="frequency" value="2347000000"/>
|
||||
<configuration name="frequencyofinterest" value="2347000000"/>
|
||||
<configuration name="noisebinsize" value="20"/>
|
||||
<configuration name="noisemaxclampenable" value="0"/>
|
||||
<configuration name="noisemaxmessagepropagation" value="200000"/>
|
||||
<configuration name="noisemaxsegmentduration" value="1000000"/>
|
||||
<configuration name="noisemaxsegmentoffset" value="300000"/>
|
||||
<configuration name="noisemode" value="outofband"/>
|
||||
<configuration name="propagationmodel" value="precomputed"/>
|
||||
<configuration name="subid" value="1"/>
|
||||
<configuration name="systemnoisefigure" value="4.000000"/>
|
||||
<configuration name="timesyncthreshold" value="10000"/>
|
||||
<configuration name="txpower" value="0.000000"/>
|
||||
</phy>
|
||||
<external>
|
||||
<configuration name="external" value="0"/>
|
||||
<configuration name="platformendpoint" value="127.0.0.1:40001"/>
|
||||
<configuration name="transportendpoint" value="127.0.0.1:50002"/>
|
||||
</external>
|
||||
</emane_configuration>
|
||||
</emane_configurations>
|
||||
<session_origin lat="47.5791667" lon="-122.132322" alt="2.0" scale="150.0"/>
|
||||
<session_options>
|
||||
<configuration name="controlnet" value="172.16.0.0/24"/>
|
||||
<configuration name="controlnet0" value=""/>
|
||||
<configuration name="controlnet1" value=""/>
|
||||
<configuration name="controlnet2" value=""/>
|
||||
<configuration name="controlnet3" value=""/>
|
||||
<configuration name="controlnet_updown_script" value=""/>
|
||||
<configuration name="enablerj45" value="1"/>
|
||||
<configuration name="preservedir" value="0"/>
|
||||
<configuration name="enablesdt" value="0"/>
|
||||
<configuration name="sdturl" value="tcp://127.0.0.1:50000/"/>
|
||||
<configuration name="ovs" value="0"/>
|
||||
</session_options>
|
||||
<session_metadata>
|
||||
<configuration name="canvas c1" value="{name {Canvas1}}"/>
|
||||
<configuration name="global_options" value="interface_names=no ip_addresses=yes ipv6_addresses=yes node_labels=yes link_labels=yes show_api=no background_images=no annotations=yes grid=yes traffic_start=0"/>
|
||||
</session_metadata>
|
||||
<default_services>
|
||||
<node type="mdr">
|
||||
<service name="zebra"/>
|
||||
<service name="OSPFv3MDR"/>
|
||||
<service name="IPForward"/>
|
||||
</node>
|
||||
<node type="PC">
|
||||
<service name="DefaultRoute"/>
|
||||
</node>
|
||||
<node type="prouter"/>
|
||||
<node type="router">
|
||||
<service name="zebra"/>
|
||||
<service name="OSPFv2"/>
|
||||
<service name="OSPFv3"/>
|
||||
<service name="IPForward"/>
|
||||
</node>
|
||||
<node type="host">
|
||||
<service name="DefaultRoute"/>
|
||||
<service name="SSH"/>
|
||||
</node>
|
||||
</default_services>
|
||||
</scenario>
|
146
daemon/core/gui/data/xmls/emane-demo-eel.xml
Normal file
146
daemon/core/gui/data/xmls/emane-demo-eel.xml
Normal file
|
@ -0,0 +1,146 @@
|
|||
<?xml version='1.0' encoding='UTF-8'?>
|
||||
<scenario name="/home/developer/.core/configs/emane-demo-eel.xml">
|
||||
<networks>
|
||||
<network id="3" name="wlan3" model="emane_rfpipe" type="EMANE">
|
||||
<position x="282" y="317" lat="47.57628519861569" lon="-122.12852212634816" alt="2.0"/>
|
||||
</network>
|
||||
</networks>
|
||||
<devices>
|
||||
<device id="1" name="n1" type="mdr" class="" image="">
|
||||
<position x="153" y="172" lat="47.57760325520506" lon="-122.13026036642295" alt="2.0"/>
|
||||
<services>
|
||||
<service name="zebra"/>
|
||||
<service name="OSPFv3MDR"/>
|
||||
<service name="IPForward"/>
|
||||
</services>
|
||||
</device>
|
||||
<device id="2" name="n2" type="mdr" class="" image="">
|
||||
<position x="393" y="171" lat="47.57761234513531" lon="-122.12702643140011" alt="2.0"/>
|
||||
<services>
|
||||
<service name="zebra"/>
|
||||
<service name="OSPFv3MDR"/>
|
||||
<service name="IPForward"/>
|
||||
</services>
|
||||
</device>
|
||||
</devices>
|
||||
<links>
|
||||
<link node1="3" node2="1">
|
||||
<iface2 nem="1" id="0" name="eth0" mac="02:02:00:00:00:01" ip4="10.0.0.1" ip4_mask="32" ip6="2001::1" ip6_mask="128"/>
|
||||
</link>
|
||||
<link node1="3" node2="2">
|
||||
<iface2 nem="2" id="0" name="eth0" mac="02:02:00:00:00:02" ip4="10.0.0.2" ip4_mask="32" ip6="2001::2" ip6_mask="128"/>
|
||||
</link>
|
||||
</links>
|
||||
<emane_global_configuration>
|
||||
<emulator>
|
||||
<configuration name="antennaprofilemanifesturi" value=""/>
|
||||
<configuration name="controlportendpoint" value="0.0.0.0:47000"/>
|
||||
<configuration name="eventservicedevice" value="ctrl0"/>
|
||||
<configuration name="eventservicegroup" value="224.1.2.8:45703"/>
|
||||
<configuration name="eventservicettl" value="1"/>
|
||||
<configuration name="otamanagerchannelenable" value="1"/>
|
||||
<configuration name="otamanagerdevice" value="ctrl0"/>
|
||||
<configuration name="otamanagergroup" value="224.1.2.8:45702"/>
|
||||
<configuration name="otamanagerloopback" value="0"/>
|
||||
<configuration name="otamanagermtu" value="0"/>
|
||||
<configuration name="otamanagerpartcheckthreshold" value="2"/>
|
||||
<configuration name="otamanagerparttimeoutthreshold" value="5"/>
|
||||
<configuration name="otamanagerttl" value="1"/>
|
||||
<configuration name="stats.event.maxeventcountrows" value="0"/>
|
||||
<configuration name="stats.ota.maxeventcountrows" value="0"/>
|
||||
<configuration name="stats.ota.maxpacketcountrows" value="0"/>
|
||||
</emulator>
|
||||
<core>
|
||||
<configuration name="platform_id_start" value="1"/>
|
||||
<configuration name="nem_id_start" value="1"/>
|
||||
<configuration name="link_enabled" value="1"/>
|
||||
<configuration name="loss_threshold" value="30"/>
|
||||
<configuration name="link_interval" value="1"/>
|
||||
<configuration name="link_timeout" value="4"/>
|
||||
</core>
|
||||
</emane_global_configuration>
|
||||
<emane_configurations>
|
||||
<emane_configuration node="3" model="emane_rfpipe">
|
||||
<mac>
|
||||
<configuration name="datarate" value="1000000"/>
|
||||
<configuration name="delay" value="0.000000"/>
|
||||
<configuration name="enablepromiscuousmode" value="0"/>
|
||||
<configuration name="flowcontrolenable" value="0"/>
|
||||
<configuration name="flowcontroltokens" value="10"/>
|
||||
<configuration name="jitter" value="0.000000"/>
|
||||
<configuration name="neighbormetricdeletetime" value="60.000000"/>
|
||||
<configuration name="pcrcurveuri" value="/usr/share/emane/xml/models/mac/rfpipe/rfpipepcr.xml"/>
|
||||
<configuration name="radiometricenable" value="0"/>
|
||||
<configuration name="radiometricreportinterval" value="1.000000"/>
|
||||
</mac>
|
||||
<phy>
|
||||
<configuration name="bandwidth" value="1000000"/>
|
||||
<configuration name="fading.model" value="none"/>
|
||||
<configuration name="fading.nakagami.distance0" value="100.000000"/>
|
||||
<configuration name="fading.nakagami.distance1" value="250.000000"/>
|
||||
<configuration name="fading.nakagami.m0" value="0.750000"/>
|
||||
<configuration name="fading.nakagami.m1" value="1.000000"/>
|
||||
<configuration name="fading.nakagami.m2" value="200.000000"/>
|
||||
<configuration name="fixedantennagain" value="0.000000"/>
|
||||
<configuration name="fixedantennagainenable" value="1"/>
|
||||
<configuration name="frequency" value="2347000000"/>
|
||||
<configuration name="frequencyofinterest" value="2347000000"/>
|
||||
<configuration name="noisebinsize" value="20"/>
|
||||
<configuration name="noisemaxclampenable" value="0"/>
|
||||
<configuration name="noisemaxmessagepropagation" value="200000"/>
|
||||
<configuration name="noisemaxsegmentduration" value="1000000"/>
|
||||
<configuration name="noisemaxsegmentoffset" value="300000"/>
|
||||
<configuration name="noisemode" value="none"/>
|
||||
<configuration name="propagationmodel" value="precomputed"/>
|
||||
<configuration name="subid" value="1"/>
|
||||
<configuration name="systemnoisefigure" value="4.000000"/>
|
||||
<configuration name="timesyncthreshold" value="10000"/>
|
||||
<configuration name="txpower" value="0.000000"/>
|
||||
</phy>
|
||||
<external>
|
||||
<configuration name="external" value="0"/>
|
||||
<configuration name="platformendpoint" value="127.0.0.1:40001"/>
|
||||
<configuration name="transportendpoint" value="127.0.0.1:50002"/>
|
||||
</external>
|
||||
</emane_configuration>
|
||||
</emane_configurations>
|
||||
<session_origin lat="47.5791667" lon="-122.132322" alt="2.0" scale="150.0"/>
|
||||
<session_options>
|
||||
<configuration name="controlnet" value="172.16.0.0/24"/>
|
||||
<configuration name="controlnet0" value=""/>
|
||||
<configuration name="controlnet1" value=""/>
|
||||
<configuration name="controlnet2" value=""/>
|
||||
<configuration name="controlnet3" value=""/>
|
||||
<configuration name="controlnet_updown_script" value=""/>
|
||||
<configuration name="enablerj45" value="1"/>
|
||||
<configuration name="preservedir" value="0"/>
|
||||
<configuration name="enablesdt" value="0"/>
|
||||
<configuration name="sdturl" value="tcp://127.0.0.1:50000/"/>
|
||||
<configuration name="ovs" value="0"/>
|
||||
</session_options>
|
||||
<session_metadata>
|
||||
<configuration name="canvas c1" value="{name {Canvas1}}"/>
|
||||
<configuration name="global_options" value="interface_names=no ip_addresses=yes ipv6_addresses=yes node_labels=yes link_labels=yes show_api=no background_images=no annotations=yes grid=yes traffic_start=0"/>
|
||||
</session_metadata>
|
||||
<default_services>
|
||||
<node type="mdr">
|
||||
<service name="zebra"/>
|
||||
<service name="OSPFv3MDR"/>
|
||||
<service name="IPForward"/>
|
||||
</node>
|
||||
<node type="PC">
|
||||
<service name="DefaultRoute"/>
|
||||
</node>
|
||||
<node type="prouter"/>
|
||||
<node type="router">
|
||||
<service name="zebra"/>
|
||||
<service name="OSPFv2"/>
|
||||
<service name="OSPFv3"/>
|
||||
<service name="IPForward"/>
|
||||
</node>
|
||||
<node type="host">
|
||||
<service name="DefaultRoute"/>
|
||||
<service name="SSH"/>
|
||||
</node>
|
||||
</default_services>
|
||||
</scenario>
|
146
daemon/core/gui/data/xmls/emane-demo-files.xml
Normal file
146
daemon/core/gui/data/xmls/emane-demo-files.xml
Normal file
|
@ -0,0 +1,146 @@
|
|||
<?xml version='1.0' encoding='UTF-8'?>
|
||||
<scenario name="/home/developer/.core/configs/emane-demo-files.xml">
|
||||
<networks>
|
||||
<network id="3" name="wlan3" model="emane_rfpipe" type="EMANE">
|
||||
<position x="282" y="317" lat="47.57628519861569" lon="-122.12852212634816" alt="2.0"/>
|
||||
</network>
|
||||
</networks>
|
||||
<devices>
|
||||
<device id="1" name="n1" type="mdr" class="" image="">
|
||||
<position x="153" y="173" lat="47.57759416527324" lon="-122.13026036642295" alt="2.0"/>
|
||||
<services>
|
||||
<service name="zebra"/>
|
||||
<service name="OSPFv3MDR"/>
|
||||
<service name="IPForward"/>
|
||||
</services>
|
||||
</device>
|
||||
<device id="2" name="n2" type="mdr" class="" image="">
|
||||
<position x="393" y="171" lat="47.57761234513531" lon="-122.12702643140011" alt="2.0"/>
|
||||
<services>
|
||||
<service name="zebra"/>
|
||||
<service name="OSPFv3MDR"/>
|
||||
<service name="IPForward"/>
|
||||
</services>
|
||||
</device>
|
||||
</devices>
|
||||
<links>
|
||||
<link node1="3" node2="1">
|
||||
<iface2 nem="1" id="0" name="eth0" mac="02:02:00:00:00:01" ip4="10.0.0.1" ip4_mask="32" ip6="2001::1" ip6_mask="128"/>
|
||||
</link>
|
||||
<link node1="3" node2="2">
|
||||
<iface2 nem="2" id="0" name="eth0" mac="02:02:00:00:00:02" ip4="10.0.0.2" ip4_mask="32" ip6="2001::2" ip6_mask="128"/>
|
||||
</link>
|
||||
</links>
|
||||
<emane_global_configuration>
|
||||
<emulator>
|
||||
<configuration name="antennaprofilemanifesturi" value=""/>
|
||||
<configuration name="controlportendpoint" value="0.0.0.0:47000"/>
|
||||
<configuration name="eventservicedevice" value="ctrl0"/>
|
||||
<configuration name="eventservicegroup" value="224.1.2.8:45703"/>
|
||||
<configuration name="eventservicettl" value="1"/>
|
||||
<configuration name="otamanagerchannelenable" value="1"/>
|
||||
<configuration name="otamanagerdevice" value="ctrl0"/>
|
||||
<configuration name="otamanagergroup" value="224.1.2.8:45702"/>
|
||||
<configuration name="otamanagerloopback" value="0"/>
|
||||
<configuration name="otamanagermtu" value="0"/>
|
||||
<configuration name="otamanagerpartcheckthreshold" value="2"/>
|
||||
<configuration name="otamanagerparttimeoutthreshold" value="5"/>
|
||||
<configuration name="otamanagerttl" value="1"/>
|
||||
<configuration name="stats.event.maxeventcountrows" value="0"/>
|
||||
<configuration name="stats.ota.maxeventcountrows" value="0"/>
|
||||
<configuration name="stats.ota.maxpacketcountrows" value="0"/>
|
||||
</emulator>
|
||||
<core>
|
||||
<configuration name="platform_id_start" value="1"/>
|
||||
<configuration name="nem_id_start" value="1"/>
|
||||
<configuration name="link_enabled" value="1"/>
|
||||
<configuration name="loss_threshold" value="30"/>
|
||||
<configuration name="link_interval" value="1"/>
|
||||
<configuration name="link_timeout" value="4"/>
|
||||
</core>
|
||||
</emane_global_configuration>
|
||||
<emane_configurations>
|
||||
<emane_configuration node="3" model="emane_rfpipe">
|
||||
<mac>
|
||||
<configuration name="datarate" value="1000000"/>
|
||||
<configuration name="delay" value="0.000000"/>
|
||||
<configuration name="enablepromiscuousmode" value="0"/>
|
||||
<configuration name="flowcontrolenable" value="0"/>
|
||||
<configuration name="flowcontroltokens" value="10"/>
|
||||
<configuration name="jitter" value="0.000000"/>
|
||||
<configuration name="neighbormetricdeletetime" value="60.000000"/>
|
||||
<configuration name="pcrcurveuri" value="/usr/share/emane/xml/models/mac/rfpipe/rfpipepcr.xml"/>
|
||||
<configuration name="radiometricenable" value="0"/>
|
||||
<configuration name="radiometricreportinterval" value="1.000000"/>
|
||||
</mac>
|
||||
<phy>
|
||||
<configuration name="bandwidth" value="1000000"/>
|
||||
<configuration name="fading.model" value="none"/>
|
||||
<configuration name="fading.nakagami.distance0" value="100.000000"/>
|
||||
<configuration name="fading.nakagami.distance1" value="250.000000"/>
|
||||
<configuration name="fading.nakagami.m0" value="0.750000"/>
|
||||
<configuration name="fading.nakagami.m1" value="1.000000"/>
|
||||
<configuration name="fading.nakagami.m2" value="200.000000"/>
|
||||
<configuration name="fixedantennagain" value="0.000000"/>
|
||||
<configuration name="fixedantennagainenable" value="1"/>
|
||||
<configuration name="frequency" value="2347000000"/>
|
||||
<configuration name="frequencyofinterest" value="2347000000"/>
|
||||
<configuration name="noisebinsize" value="20"/>
|
||||
<configuration name="noisemaxclampenable" value="0"/>
|
||||
<configuration name="noisemaxmessagepropagation" value="200000"/>
|
||||
<configuration name="noisemaxsegmentduration" value="1000000"/>
|
||||
<configuration name="noisemaxsegmentoffset" value="300000"/>
|
||||
<configuration name="noisemode" value="none"/>
|
||||
<configuration name="propagationmodel" value="2ray"/>
|
||||
<configuration name="subid" value="1"/>
|
||||
<configuration name="systemnoisefigure" value="4.000000"/>
|
||||
<configuration name="timesyncthreshold" value="10000"/>
|
||||
<configuration name="txpower" value="0.000000"/>
|
||||
</phy>
|
||||
<external>
|
||||
<configuration name="external" value="0"/>
|
||||
<configuration name="platformendpoint" value="127.0.0.1:40001"/>
|
||||
<configuration name="transportendpoint" value="127.0.0.1:50002"/>
|
||||
</external>
|
||||
</emane_configuration>
|
||||
</emane_configurations>
|
||||
<session_origin lat="47.5791667" lon="-122.132322" alt="2.0" scale="150.0"/>
|
||||
<session_options>
|
||||
<configuration name="controlnet" value="172.16.0.0/24"/>
|
||||
<configuration name="controlnet0" value=""/>
|
||||
<configuration name="controlnet1" value=""/>
|
||||
<configuration name="controlnet2" value=""/>
|
||||
<configuration name="controlnet3" value=""/>
|
||||
<configuration name="controlnet_updown_script" value=""/>
|
||||
<configuration name="enablerj45" value="1"/>
|
||||
<configuration name="preservedir" value="0"/>
|
||||
<configuration name="enablesdt" value="0"/>
|
||||
<configuration name="sdturl" value="tcp://127.0.0.1:50000/"/>
|
||||
<configuration name="ovs" value="0"/>
|
||||
</session_options>
|
||||
<session_metadata>
|
||||
<configuration name="canvas c1" value="{name {Canvas1}}"/>
|
||||
<configuration name="global_options" value="interface_names=no ip_addresses=yes ipv6_addresses=yes node_labels=yes link_labels=yes show_api=no background_images=no annotations=yes grid=yes traffic_start=0"/>
|
||||
</session_metadata>
|
||||
<default_services>
|
||||
<node type="mdr">
|
||||
<service name="zebra"/>
|
||||
<service name="OSPFv3MDR"/>
|
||||
<service name="IPForward"/>
|
||||
</node>
|
||||
<node type="PC">
|
||||
<service name="DefaultRoute"/>
|
||||
</node>
|
||||
<node type="prouter"/>
|
||||
<node type="router">
|
||||
<service name="zebra"/>
|
||||
<service name="OSPFv2"/>
|
||||
<service name="OSPFv3"/>
|
||||
<service name="IPForward"/>
|
||||
</node>
|
||||
<node type="host">
|
||||
<service name="DefaultRoute"/>
|
||||
<service name="SSH"/>
|
||||
</node>
|
||||
</default_services>
|
||||
</scenario>
|
146
daemon/core/gui/data/xmls/emane-demo-gpsd.xml
Normal file
146
daemon/core/gui/data/xmls/emane-demo-gpsd.xml
Normal file
|
@ -0,0 +1,146 @@
|
|||
<?xml version='1.0' encoding='UTF-8'?>
|
||||
<scenario name="/home/developer/.core/configs/emane-demo-gpsd.xml">
|
||||
<networks>
|
||||
<network id="3" name="wlan3" model="emane_rfpipe" type="EMANE">
|
||||
<position x="282" y="317" lat="47.57628519861569" lon="-122.12852212634816" alt="2.0"/>
|
||||
</network>
|
||||
</networks>
|
||||
<devices>
|
||||
<device id="1" name="n1" type="mdr" class="" image="">
|
||||
<position x="153" y="173" lat="47.57759416527324" lon="-122.13026036642295" alt="2.0"/>
|
||||
<services>
|
||||
<service name="zebra"/>
|
||||
<service name="OSPFv3MDR"/>
|
||||
<service name="IPForward"/>
|
||||
</services>
|
||||
</device>
|
||||
<device id="2" name="n2" type="mdr" class="" image="">
|
||||
<position x="393" y="171" lat="47.57761234513531" lon="-122.12702643140011" alt="2.0"/>
|
||||
<services>
|
||||
<service name="zebra"/>
|
||||
<service name="OSPFv3MDR"/>
|
||||
<service name="IPForward"/>
|
||||
</services>
|
||||
</device>
|
||||
</devices>
|
||||
<links>
|
||||
<link node1="3" node2="1">
|
||||
<iface2 nem="1" id="0" name="eth0" mac="02:02:00:00:00:01" ip4="10.0.0.1" ip4_mask="32" ip6="2001::1" ip6_mask="128"/>
|
||||
</link>
|
||||
<link node1="3" node2="2">
|
||||
<iface2 nem="2" id="0" name="eth0" mac="02:02:00:00:00:02" ip4="10.0.0.2" ip4_mask="32" ip6="2001::2" ip6_mask="128"/>
|
||||
</link>
|
||||
</links>
|
||||
<emane_global_configuration>
|
||||
<emulator>
|
||||
<configuration name="antennaprofilemanifesturi" value=""/>
|
||||
<configuration name="controlportendpoint" value="0.0.0.0:47000"/>
|
||||
<configuration name="eventservicedevice" value="ctrl0"/>
|
||||
<configuration name="eventservicegroup" value="224.1.2.8:45703"/>
|
||||
<configuration name="eventservicettl" value="1"/>
|
||||
<configuration name="otamanagerchannelenable" value="1"/>
|
||||
<configuration name="otamanagerdevice" value="ctrl0"/>
|
||||
<configuration name="otamanagergroup" value="224.1.2.8:45702"/>
|
||||
<configuration name="otamanagerloopback" value="0"/>
|
||||
<configuration name="otamanagermtu" value="0"/>
|
||||
<configuration name="otamanagerpartcheckthreshold" value="2"/>
|
||||
<configuration name="otamanagerparttimeoutthreshold" value="5"/>
|
||||
<configuration name="otamanagerttl" value="1"/>
|
||||
<configuration name="stats.event.maxeventcountrows" value="0"/>
|
||||
<configuration name="stats.ota.maxeventcountrows" value="0"/>
|
||||
<configuration name="stats.ota.maxpacketcountrows" value="0"/>
|
||||
</emulator>
|
||||
<core>
|
||||
<configuration name="platform_id_start" value="1"/>
|
||||
<configuration name="nem_id_start" value="1"/>
|
||||
<configuration name="link_enabled" value="1"/>
|
||||
<configuration name="loss_threshold" value="30"/>
|
||||
<configuration name="link_interval" value="1"/>
|
||||
<configuration name="link_timeout" value="4"/>
|
||||
</core>
|
||||
</emane_global_configuration>
|
||||
<emane_configurations>
|
||||
<emane_configuration node="3" model="emane_rfpipe">
|
||||
<mac>
|
||||
<configuration name="datarate" value="1000000"/>
|
||||
<configuration name="delay" value="0.000000"/>
|
||||
<configuration name="enablepromiscuousmode" value="0"/>
|
||||
<configuration name="flowcontrolenable" value="0"/>
|
||||
<configuration name="flowcontroltokens" value="10"/>
|
||||
<configuration name="jitter" value="0.000000"/>
|
||||
<configuration name="neighbormetricdeletetime" value="60.000000"/>
|
||||
<configuration name="pcrcurveuri" value="/usr/share/emane/xml/models/mac/rfpipe/rfpipepcr.xml"/>
|
||||
<configuration name="radiometricenable" value="0"/>
|
||||
<configuration name="radiometricreportinterval" value="1.000000"/>
|
||||
</mac>
|
||||
<phy>
|
||||
<configuration name="bandwidth" value="1000000"/>
|
||||
<configuration name="fading.model" value="none"/>
|
||||
<configuration name="fading.nakagami.distance0" value="100.000000"/>
|
||||
<configuration name="fading.nakagami.distance1" value="250.000000"/>
|
||||
<configuration name="fading.nakagami.m0" value="0.750000"/>
|
||||
<configuration name="fading.nakagami.m1" value="1.000000"/>
|
||||
<configuration name="fading.nakagami.m2" value="200.000000"/>
|
||||
<configuration name="fixedantennagain" value="0.000000"/>
|
||||
<configuration name="fixedantennagainenable" value="1"/>
|
||||
<configuration name="frequency" value="2347000000"/>
|
||||
<configuration name="frequencyofinterest" value="2347000000"/>
|
||||
<configuration name="noisebinsize" value="20"/>
|
||||
<configuration name="noisemaxclampenable" value="0"/>
|
||||
<configuration name="noisemaxmessagepropagation" value="200000"/>
|
||||
<configuration name="noisemaxsegmentduration" value="1000000"/>
|
||||
<configuration name="noisemaxsegmentoffset" value="300000"/>
|
||||
<configuration name="noisemode" value="none"/>
|
||||
<configuration name="propagationmodel" value="2ray"/>
|
||||
<configuration name="subid" value="1"/>
|
||||
<configuration name="systemnoisefigure" value="4.000000"/>
|
||||
<configuration name="timesyncthreshold" value="10000"/>
|
||||
<configuration name="txpower" value="0.000000"/>
|
||||
</phy>
|
||||
<external>
|
||||
<configuration name="external" value="0"/>
|
||||
<configuration name="platformendpoint" value="127.0.0.1:40001"/>
|
||||
<configuration name="transportendpoint" value="127.0.0.1:50002"/>
|
||||
</external>
|
||||
</emane_configuration>
|
||||
</emane_configurations>
|
||||
<session_origin lat="47.5791667" lon="-122.132322" alt="2.0" scale="150.0"/>
|
||||
<session_options>
|
||||
<configuration name="controlnet" value="172.16.0.0/24"/>
|
||||
<configuration name="controlnet0" value=""/>
|
||||
<configuration name="controlnet1" value=""/>
|
||||
<configuration name="controlnet2" value=""/>
|
||||
<configuration name="controlnet3" value=""/>
|
||||
<configuration name="controlnet_updown_script" value=""/>
|
||||
<configuration name="enablerj45" value="1"/>
|
||||
<configuration name="preservedir" value="0"/>
|
||||
<configuration name="enablesdt" value="0"/>
|
||||
<configuration name="sdturl" value="tcp://127.0.0.1:50000/"/>
|
||||
<configuration name="ovs" value="0"/>
|
||||
</session_options>
|
||||
<session_metadata>
|
||||
<configuration name="canvas c1" value="{name {Canvas1}}"/>
|
||||
<configuration name="global_options" value="interface_names=no ip_addresses=yes ipv6_addresses=yes node_labels=yes link_labels=yes show_api=no background_images=no annotations=yes grid=yes traffic_start=0"/>
|
||||
</session_metadata>
|
||||
<default_services>
|
||||
<node type="mdr">
|
||||
<service name="zebra"/>
|
||||
<service name="OSPFv3MDR"/>
|
||||
<service name="IPForward"/>
|
||||
</node>
|
||||
<node type="PC">
|
||||
<service name="DefaultRoute"/>
|
||||
</node>
|
||||
<node type="prouter"/>
|
||||
<node type="router">
|
||||
<service name="zebra"/>
|
||||
<service name="OSPFv2"/>
|
||||
<service name="OSPFv3"/>
|
||||
<service name="IPForward"/>
|
||||
</node>
|
||||
<node type="host">
|
||||
<service name="DefaultRoute"/>
|
||||
<service name="SSH"/>
|
||||
</node>
|
||||
</default_services>
|
||||
</scenario>
|
146
daemon/core/gui/data/xmls/emane-demo-precomputed.xml
Normal file
146
daemon/core/gui/data/xmls/emane-demo-precomputed.xml
Normal file
|
@ -0,0 +1,146 @@
|
|||
<?xml version='1.0' encoding='UTF-8'?>
|
||||
<scenario name="/home/developer/.core/configs/emane-demo-precomputed.xml">
|
||||
<networks>
|
||||
<network id="3" name="wlan3" model="emane_rfpipe" type="EMANE">
|
||||
<position x="282" y="317" lat="47.57628519861569" lon="-122.12852212634816" alt="2.0"/>
|
||||
</network>
|
||||
</networks>
|
||||
<devices>
|
||||
<device id="1" name="n1" type="mdr" class="" image="">
|
||||
<position x="153" y="172" lat="47.57760325520506" lon="-122.13026036642295" alt="2.0"/>
|
||||
<services>
|
||||
<service name="zebra"/>
|
||||
<service name="OSPFv3MDR"/>
|
||||
<service name="IPForward"/>
|
||||
</services>
|
||||
</device>
|
||||
<device id="2" name="n2" type="mdr" class="" image="">
|
||||
<position x="393" y="171" lat="47.57761234513531" lon="-122.12702643140011" alt="2.0"/>
|
||||
<services>
|
||||
<service name="zebra"/>
|
||||
<service name="OSPFv3MDR"/>
|
||||
<service name="IPForward"/>
|
||||
</services>
|
||||
</device>
|
||||
</devices>
|
||||
<links>
|
||||
<link node1="3" node2="1">
|
||||
<iface2 nem="1" id="0" name="eth0" mac="02:02:00:00:00:01" ip4="10.0.0.1" ip4_mask="32" ip6="2001::1" ip6_mask="128"/>
|
||||
</link>
|
||||
<link node1="3" node2="2">
|
||||
<iface2 nem="2" id="0" name="eth0" mac="02:02:00:00:00:02" ip4="10.0.0.2" ip4_mask="32" ip6="2001::2" ip6_mask="128"/>
|
||||
</link>
|
||||
</links>
|
||||
<emane_global_configuration>
|
||||
<emulator>
|
||||
<configuration name="antennaprofilemanifesturi" value=""/>
|
||||
<configuration name="controlportendpoint" value="0.0.0.0:47000"/>
|
||||
<configuration name="eventservicedevice" value="ctrl0"/>
|
||||
<configuration name="eventservicegroup" value="224.1.2.8:45703"/>
|
||||
<configuration name="eventservicettl" value="1"/>
|
||||
<configuration name="otamanagerchannelenable" value="1"/>
|
||||
<configuration name="otamanagerdevice" value="ctrl0"/>
|
||||
<configuration name="otamanagergroup" value="224.1.2.8:45702"/>
|
||||
<configuration name="otamanagerloopback" value="0"/>
|
||||
<configuration name="otamanagermtu" value="0"/>
|
||||
<configuration name="otamanagerpartcheckthreshold" value="2"/>
|
||||
<configuration name="otamanagerparttimeoutthreshold" value="5"/>
|
||||
<configuration name="otamanagerttl" value="1"/>
|
||||
<configuration name="stats.event.maxeventcountrows" value="0"/>
|
||||
<configuration name="stats.ota.maxeventcountrows" value="0"/>
|
||||
<configuration name="stats.ota.maxpacketcountrows" value="0"/>
|
||||
</emulator>
|
||||
<core>
|
||||
<configuration name="platform_id_start" value="1"/>
|
||||
<configuration name="nem_id_start" value="1"/>
|
||||
<configuration name="link_enabled" value="1"/>
|
||||
<configuration name="loss_threshold" value="30"/>
|
||||
<configuration name="link_interval" value="1"/>
|
||||
<configuration name="link_timeout" value="4"/>
|
||||
</core>
|
||||
</emane_global_configuration>
|
||||
<emane_configurations>
|
||||
<emane_configuration node="3" model="emane_rfpipe">
|
||||
<mac>
|
||||
<configuration name="datarate" value="1000000"/>
|
||||
<configuration name="delay" value="0.000000"/>
|
||||
<configuration name="enablepromiscuousmode" value="0"/>
|
||||
<configuration name="flowcontrolenable" value="0"/>
|
||||
<configuration name="flowcontroltokens" value="10"/>
|
||||
<configuration name="jitter" value="0.000000"/>
|
||||
<configuration name="neighbormetricdeletetime" value="60.000000"/>
|
||||
<configuration name="pcrcurveuri" value="/usr/share/emane/xml/models/mac/rfpipe/rfpipepcr.xml"/>
|
||||
<configuration name="radiometricenable" value="0"/>
|
||||
<configuration name="radiometricreportinterval" value="1.000000"/>
|
||||
</mac>
|
||||
<phy>
|
||||
<configuration name="bandwidth" value="1000000"/>
|
||||
<configuration name="fading.model" value="none"/>
|
||||
<configuration name="fading.nakagami.distance0" value="100.000000"/>
|
||||
<configuration name="fading.nakagami.distance1" value="250.000000"/>
|
||||
<configuration name="fading.nakagami.m0" value="0.750000"/>
|
||||
<configuration name="fading.nakagami.m1" value="1.000000"/>
|
||||
<configuration name="fading.nakagami.m2" value="200.000000"/>
|
||||
<configuration name="fixedantennagain" value="0.000000"/>
|
||||
<configuration name="fixedantennagainenable" value="1"/>
|
||||
<configuration name="frequency" value="2347000000"/>
|
||||
<configuration name="frequencyofinterest" value="2347000000"/>
|
||||
<configuration name="noisebinsize" value="20"/>
|
||||
<configuration name="noisemaxclampenable" value="0"/>
|
||||
<configuration name="noisemaxmessagepropagation" value="200000"/>
|
||||
<configuration name="noisemaxsegmentduration" value="1000000"/>
|
||||
<configuration name="noisemaxsegmentoffset" value="300000"/>
|
||||
<configuration name="noisemode" value="none"/>
|
||||
<configuration name="propagationmodel" value="precomputed"/>
|
||||
<configuration name="subid" value="1"/>
|
||||
<configuration name="systemnoisefigure" value="4.000000"/>
|
||||
<configuration name="timesyncthreshold" value="10000"/>
|
||||
<configuration name="txpower" value="0.000000"/>
|
||||
</phy>
|
||||
<external>
|
||||
<configuration name="external" value="0"/>
|
||||
<configuration name="platformendpoint" value="127.0.0.1:40001"/>
|
||||
<configuration name="transportendpoint" value="127.0.0.1:50002"/>
|
||||
</external>
|
||||
</emane_configuration>
|
||||
</emane_configurations>
|
||||
<session_origin lat="47.5791667" lon="-122.132322" alt="2.0" scale="150.0"/>
|
||||
<session_options>
|
||||
<configuration name="controlnet" value="172.16.0.0/24"/>
|
||||
<configuration name="controlnet0" value=""/>
|
||||
<configuration name="controlnet1" value=""/>
|
||||
<configuration name="controlnet2" value=""/>
|
||||
<configuration name="controlnet3" value=""/>
|
||||
<configuration name="controlnet_updown_script" value=""/>
|
||||
<configuration name="enablerj45" value="1"/>
|
||||
<configuration name="preservedir" value="0"/>
|
||||
<configuration name="enablesdt" value="0"/>
|
||||
<configuration name="sdturl" value="tcp://127.0.0.1:50000/"/>
|
||||
<configuration name="ovs" value="0"/>
|
||||
</session_options>
|
||||
<session_metadata>
|
||||
<configuration name="canvas c1" value="{name {Canvas1}}"/>
|
||||
<configuration name="global_options" value="interface_names=no ip_addresses=yes ipv6_addresses=yes node_labels=yes link_labels=yes show_api=no background_images=no annotations=yes grid=yes traffic_start=0"/>
|
||||
</session_metadata>
|
||||
<default_services>
|
||||
<node type="mdr">
|
||||
<service name="zebra"/>
|
||||
<service name="OSPFv3MDR"/>
|
||||
<service name="IPForward"/>
|
||||
</node>
|
||||
<node type="PC">
|
||||
<service name="DefaultRoute"/>
|
||||
</node>
|
||||
<node type="prouter"/>
|
||||
<node type="router">
|
||||
<service name="zebra"/>
|
||||
<service name="OSPFv2"/>
|
||||
<service name="OSPFv3"/>
|
||||
<service name="IPForward"/>
|
||||
</node>
|
||||
<node type="host">
|
||||
<service name="DefaultRoute"/>
|
||||
<service name="SSH"/>
|
||||
</node>
|
||||
</default_services>
|
||||
</scenario>
|
|
@ -49,16 +49,18 @@ class LinkConfigurationDialog(Dialog):
|
|||
self.jitter: tk.StringVar = tk.StringVar()
|
||||
self.loss: tk.StringVar = tk.StringVar()
|
||||
self.duplicate: tk.StringVar = tk.StringVar()
|
||||
self.buffer: tk.StringVar = tk.StringVar()
|
||||
|
||||
self.down_bandwidth: tk.StringVar = tk.StringVar()
|
||||
self.down_delay: tk.StringVar = tk.StringVar()
|
||||
self.down_jitter: tk.StringVar = tk.StringVar()
|
||||
self.down_loss: tk.StringVar = tk.StringVar()
|
||||
self.down_duplicate: tk.StringVar = tk.StringVar()
|
||||
self.down_buffer: tk.StringVar = tk.StringVar()
|
||||
|
||||
self.color: tk.StringVar = tk.StringVar(value="#000000")
|
||||
self.color: tk.StringVar = tk.StringVar(value=self.edge.color)
|
||||
self.color_button: Optional[tk.Button] = None
|
||||
self.width: tk.DoubleVar = tk.DoubleVar()
|
||||
self.width: tk.DoubleVar = tk.DoubleVar(value=self.edge.width)
|
||||
|
||||
self.load_link_config()
|
||||
self.symmetric_frame: Optional[ttk.Frame] = None
|
||||
|
@ -68,10 +70,14 @@ class LinkConfigurationDialog(Dialog):
|
|||
|
||||
def draw(self) -> None:
|
||||
self.top.columnconfigure(0, weight=1)
|
||||
source_name = self.app.canvas.nodes[self.edge.src].core_node.name
|
||||
dest_name = self.app.canvas.nodes[self.edge.dst].core_node.name
|
||||
src_label = self.app.canvas.nodes[self.edge.src].core_node.name
|
||||
if self.edge.link.iface1:
|
||||
src_label += f":{self.edge.link.iface1.name}"
|
||||
dst_label = self.app.canvas.nodes[self.edge.dst].core_node.name
|
||||
if self.edge.link.iface2:
|
||||
dst_label += f":{self.edge.link.iface2.name}"
|
||||
label = ttk.Label(
|
||||
self.top, text=f"Link from {source_name} to {dest_name}", anchor=tk.CENTER
|
||||
self.top, text=f"{src_label} to {dst_label}", anchor=tk.CENTER
|
||||
)
|
||||
label.grid(row=0, column=0, sticky=tk.EW, pady=PADY)
|
||||
|
||||
|
@ -183,6 +189,19 @@ class LinkConfigurationDialog(Dialog):
|
|||
entry.grid(row=row, column=2, sticky=tk.EW, pady=PADY)
|
||||
row = row + 1
|
||||
|
||||
label = ttk.Label(frame, text="Buffer (Packets)")
|
||||
label.grid(row=row, column=0, sticky=tk.EW)
|
||||
entry = validation.PositiveIntEntry(
|
||||
frame, empty_enabled=False, textvariable=self.buffer
|
||||
)
|
||||
entry.grid(row=row, column=1, sticky=tk.EW, pady=PADY)
|
||||
if not self.is_symmetric:
|
||||
entry = validation.PositiveIntEntry(
|
||||
frame, empty_enabled=False, textvariable=self.down_buffer
|
||||
)
|
||||
entry.grid(row=row, column=2, sticky=tk.EW, pady=PADY)
|
||||
row = row + 1
|
||||
|
||||
label = ttk.Label(frame, text="Color")
|
||||
label.grid(row=row, column=0, sticky=tk.EW)
|
||||
self.color_button = tk.Button(
|
||||
|
@ -213,16 +232,22 @@ class LinkConfigurationDialog(Dialog):
|
|||
self.color_button.config(background=color)
|
||||
|
||||
def click_apply(self) -> None:
|
||||
self.app.canvas.itemconfigure(self.edge.id, width=self.width.get())
|
||||
self.app.canvas.itemconfigure(self.edge.id, fill=self.color.get())
|
||||
self.edge.width = self.width.get()
|
||||
self.edge.color = self.color.get()
|
||||
link = self.edge.link
|
||||
bandwidth = get_int(self.bandwidth)
|
||||
jitter = get_int(self.jitter)
|
||||
delay = get_int(self.delay)
|
||||
duplicate = get_int(self.duplicate)
|
||||
buffer = get_int(self.buffer)
|
||||
loss = get_float(self.loss)
|
||||
options = LinkOptions(
|
||||
bandwidth=bandwidth, jitter=jitter, delay=delay, dup=duplicate, loss=loss
|
||||
bandwidth=bandwidth,
|
||||
jitter=jitter,
|
||||
delay=delay,
|
||||
dup=duplicate,
|
||||
loss=loss,
|
||||
buffer=buffer,
|
||||
)
|
||||
link.options = options
|
||||
iface1_id = link.iface1.id if link.iface1 else None
|
||||
|
@ -239,6 +264,7 @@ class LinkConfigurationDialog(Dialog):
|
|||
down_jitter = get_int(self.down_jitter)
|
||||
down_delay = get_int(self.down_delay)
|
||||
down_duplicate = get_int(self.down_duplicate)
|
||||
down_buffer = get_int(self.down_buffer)
|
||||
down_loss = get_float(self.down_loss)
|
||||
options = LinkOptions(
|
||||
bandwidth=down_bandwidth,
|
||||
|
@ -246,6 +272,7 @@ class LinkConfigurationDialog(Dialog):
|
|||
delay=down_delay,
|
||||
dup=down_duplicate,
|
||||
loss=down_loss,
|
||||
buffer=down_buffer,
|
||||
unidirectional=True,
|
||||
)
|
||||
self.edge.asymmetric_link = Link(
|
||||
|
@ -265,7 +292,8 @@ class LinkConfigurationDialog(Dialog):
|
|||
self.app.core.edit_link(self.edge.asymmetric_link)
|
||||
|
||||
# update edge label
|
||||
self.edge.draw_link_options()
|
||||
self.edge.redraw()
|
||||
self.edge.check_options()
|
||||
self.destroy()
|
||||
|
||||
def change_symmetry(self) -> None:
|
||||
|
@ -299,6 +327,7 @@ class LinkConfigurationDialog(Dialog):
|
|||
self.duplicate.set(str(link.options.dup))
|
||||
self.loss.set(str(link.options.loss))
|
||||
self.delay.set(str(link.options.delay))
|
||||
self.buffer.set(str(link.options.buffer))
|
||||
if not self.is_symmetric:
|
||||
asym_link = self.edge.asymmetric_link
|
||||
self.down_bandwidth.set(str(asym_link.options.bandwidth))
|
||||
|
@ -306,3 +335,4 @@ class LinkConfigurationDialog(Dialog):
|
|||
self.down_duplicate.set(str(asym_link.options.dup))
|
||||
self.down_loss.set(str(asym_link.options.loss))
|
||||
self.down_delay.set(str(asym_link.options.delay))
|
||||
self.down_buffer.set(str(asym_link.options.buffer))
|
||||
|
|
|
@ -14,19 +14,23 @@ from core.gui.utils import bandwidth_text, delay_jitter_text
|
|||
if TYPE_CHECKING:
|
||||
from core.gui.graph.graph import CanvasGraph
|
||||
|
||||
TEXT_DISTANCE: float = 0.30
|
||||
TEXT_DISTANCE: int = 60
|
||||
EDGE_WIDTH: int = 3
|
||||
EDGE_COLOR: str = "#ff0000"
|
||||
EDGE_LOSS: float = 100.0
|
||||
WIRELESS_WIDTH: float = 3
|
||||
WIRELESS_COLOR: str = "#009933"
|
||||
ARC_DISTANCE: int = 50
|
||||
|
||||
|
||||
def create_edge_token(src: int, dst: int, network: int = None) -> Tuple[int, ...]:
|
||||
values = [src, dst]
|
||||
if network is not None:
|
||||
values.append(network)
|
||||
return tuple(sorted(values))
|
||||
def create_wireless_token(src: int, dst: int, network: int) -> str:
|
||||
return f"{src}-{dst}-{network}"
|
||||
|
||||
|
||||
def create_edge_token(link: Link) -> str:
|
||||
iface1_id = link.iface1.id if link.iface1 else None
|
||||
iface2_id = link.iface2.id if link.iface2 else None
|
||||
return f"{link.node1_id}-{iface1_id}-{link.node2_id}-{iface2_id}"
|
||||
|
||||
|
||||
def arc_edges(edges) -> None:
|
||||
|
@ -67,17 +71,13 @@ class Edge:
|
|||
self.src: int = src
|
||||
self.dst: int = dst
|
||||
self.arc: int = 0
|
||||
self.token: Optional[Tuple[int, ...]] = None
|
||||
self.token: Optional[str] = None
|
||||
self.src_label: Optional[int] = None
|
||||
self.middle_label: Optional[int] = None
|
||||
self.dst_label: Optional[int] = None
|
||||
self.color: str = EDGE_COLOR
|
||||
self.width: int = EDGE_WIDTH
|
||||
|
||||
@classmethod
|
||||
def create_token(cls, src: int, dst: int) -> Tuple[int, ...]:
|
||||
return tuple(sorted([src, dst]))
|
||||
|
||||
def scaled_width(self) -> float:
|
||||
return self.width * self.canvas.app.app_scale
|
||||
|
||||
|
@ -156,15 +156,17 @@ class Edge:
|
|||
|
||||
def node_label_positions(self) -> Tuple[Tuple[float, float], Tuple[float, float]]:
|
||||
src_x, src_y, _, _, dst_x, dst_y = self.canvas.coords(self.id)
|
||||
v1 = dst_x - src_x
|
||||
v2 = dst_y - src_y
|
||||
ux = TEXT_DISTANCE * v1
|
||||
uy = TEXT_DISTANCE * v2
|
||||
src_x = src_x + ux
|
||||
src_y = src_y + uy
|
||||
dst_x = dst_x - ux
|
||||
dst_y = dst_y - uy
|
||||
return (src_x, src_y), (dst_x, dst_y)
|
||||
v_x, v_y = dst_x - src_x, dst_y - src_y
|
||||
v_len = math.sqrt(v_x ** 2 + v_y ** 2)
|
||||
if v_len == 0:
|
||||
u_x, u_y = 0.0, 0.0
|
||||
else:
|
||||
u_x, u_y = v_x / v_len, v_y / v_len
|
||||
offset_x, offset_y = TEXT_DISTANCE * u_x, TEXT_DISTANCE * u_y
|
||||
return (
|
||||
(src_x + offset_x, src_y + offset_y),
|
||||
(dst_x - offset_x, dst_y - offset_y),
|
||||
)
|
||||
|
||||
def src_label_text(self, text: str) -> None:
|
||||
if self.src_label is None:
|
||||
|
@ -240,15 +242,17 @@ class CanvasWirelessEdge(Edge):
|
|||
canvas: "CanvasGraph",
|
||||
src: int,
|
||||
dst: int,
|
||||
network_id: int,
|
||||
token: str,
|
||||
src_pos: Tuple[float, float],
|
||||
dst_pos: Tuple[float, float],
|
||||
token: Tuple[int, ...],
|
||||
link: Link,
|
||||
) -> None:
|
||||
logging.debug("drawing wireless link from node %s to node %s", src, dst)
|
||||
super().__init__(canvas, src, dst)
|
||||
self.network_id: int = network_id
|
||||
self.link: Link = link
|
||||
self.token: Tuple[int, ...] = token
|
||||
self.token: str = token
|
||||
self.width: float = WIRELESS_WIDTH
|
||||
color = link.color if link.color else WIRELESS_COLOR
|
||||
self.color: str = color
|
||||
|
@ -282,11 +286,10 @@ class CanvasEdge(Edge):
|
|||
Create an instance of canvas edge object
|
||||
"""
|
||||
super().__init__(canvas, src)
|
||||
self.src_iface: Optional[Interface] = None
|
||||
self.dst_iface: Optional[Interface] = None
|
||||
self.text_src: Optional[int] = None
|
||||
self.text_dst: Optional[int] = None
|
||||
self.link: Optional[Link] = None
|
||||
self.linked_wireless: bool = False
|
||||
self.asymmetric_link: Optional[Link] = None
|
||||
self.throughput: Optional[float] = None
|
||||
self.draw(src_pos, dst_pos, tk.NORMAL)
|
||||
|
@ -303,10 +306,6 @@ class CanvasEdge(Edge):
|
|||
self.canvas.tag_bind(self.id, "<ButtonRelease-3>", self.show_context)
|
||||
self.canvas.tag_bind(self.id, "<Button-1>", self.show_info)
|
||||
|
||||
def set_link(self, link: Link) -> None:
|
||||
self.link = link
|
||||
self.draw_labels()
|
||||
|
||||
def iface_label(self, iface: Interface) -> str:
|
||||
label = ""
|
||||
if iface.name and self.canvas.show_iface_names.get():
|
||||
|
@ -332,12 +331,25 @@ class CanvasEdge(Edge):
|
|||
src_text, dst_text = self.create_node_labels()
|
||||
self.src_label_text(src_text)
|
||||
self.dst_label_text(dst_text)
|
||||
self.draw_link_options()
|
||||
if not self.linked_wireless:
|
||||
self.draw_link_options()
|
||||
|
||||
def redraw(self) -> None:
|
||||
super().redraw()
|
||||
self.draw_labels()
|
||||
|
||||
def check_options(self) -> None:
|
||||
if not self.link.options:
|
||||
return
|
||||
if self.link.options.loss == EDGE_LOSS:
|
||||
state = tk.HIDDEN
|
||||
self.canvas.addtag_withtag(tags.LOSS_EDGES, self.id)
|
||||
else:
|
||||
state = tk.NORMAL
|
||||
self.canvas.dtag(self.id, tags.LOSS_EDGES)
|
||||
if self.canvas.show_loss_links.state() == tk.HIDDEN:
|
||||
self.canvas.itemconfigure(self.id, state=state)
|
||||
|
||||
def set_throughput(self, throughput: float) -> None:
|
||||
throughput = 0.001 * throughput
|
||||
text = f"{throughput:.3f} kbps"
|
||||
|
@ -350,36 +362,21 @@ class CanvasEdge(Edge):
|
|||
width = self.scaled_width()
|
||||
self.canvas.itemconfig(self.id, fill=color, width=width)
|
||||
|
||||
def complete(self, dst: int) -> None:
|
||||
def clear_throughput(self) -> None:
|
||||
self.clear_middle_label()
|
||||
if not self.linked_wireless:
|
||||
self.draw_link_options()
|
||||
|
||||
def complete(self, dst: int, linked_wireless: bool) -> None:
|
||||
self.dst = dst
|
||||
self.token = create_edge_token(self.src, self.dst)
|
||||
self.linked_wireless = linked_wireless
|
||||
dst_pos = self.canvas.coords(self.dst)
|
||||
self.move_dst(dst_pos)
|
||||
self.check_wireless()
|
||||
logging.debug("Draw wired link from node %s to node %s", self.src, dst)
|
||||
|
||||
def is_wireless(self) -> bool:
|
||||
src_node = self.canvas.nodes[self.src]
|
||||
dst_node = self.canvas.nodes[self.dst]
|
||||
src_node_type = src_node.core_node.type
|
||||
dst_node_type = dst_node.core_node.type
|
||||
is_src_wireless = NodeUtils.is_wireless_node(src_node_type)
|
||||
is_dst_wireless = NodeUtils.is_wireless_node(dst_node_type)
|
||||
|
||||
# update the wlan/EMANE network
|
||||
wlan_network = self.canvas.wireless_network
|
||||
if is_src_wireless and not is_dst_wireless:
|
||||
if self.src not in wlan_network:
|
||||
wlan_network[self.src] = set()
|
||||
wlan_network[self.src].add(self.dst)
|
||||
elif not is_src_wireless and is_dst_wireless:
|
||||
if self.dst not in wlan_network:
|
||||
wlan_network[self.dst] = set()
|
||||
wlan_network[self.dst].add(self.src)
|
||||
return is_src_wireless or is_dst_wireless
|
||||
logging.debug("draw wired link from node %s to node %s", self.src, dst)
|
||||
|
||||
def check_wireless(self) -> None:
|
||||
if self.is_wireless():
|
||||
if self.linked_wireless:
|
||||
self.canvas.itemconfig(self.id, state=tk.HIDDEN)
|
||||
self.canvas.dtag(self.id, tags.EDGE)
|
||||
self._check_antenna()
|
||||
|
|
|
@ -21,8 +21,10 @@ from core.gui.graph.edges import (
|
|||
EDGE_WIDTH,
|
||||
CanvasEdge,
|
||||
CanvasWirelessEdge,
|
||||
Edge,
|
||||
arc_edges,
|
||||
create_edge_token,
|
||||
create_wireless_token,
|
||||
)
|
||||
from core.gui.graph.enums import GraphMode, ScaleOption
|
||||
from core.gui.graph.node import CanvasNode
|
||||
|
@ -69,9 +71,9 @@ class CanvasGraph(tk.Canvas):
|
|||
self.selected: Optional[int] = None
|
||||
self.node_draw: Optional[NodeDraw] = None
|
||||
self.nodes: Dict[int, CanvasNode] = {}
|
||||
self.edges: Dict[int, CanvasEdge] = {}
|
||||
self.edges: Dict[str, CanvasEdge] = {}
|
||||
self.shapes: Dict[int, Shape] = {}
|
||||
self.wireless_edges: Dict[Tuple[int, ...], CanvasWirelessEdge] = {}
|
||||
self.wireless_edges: Dict[str, CanvasWirelessEdge] = {}
|
||||
|
||||
# map wireless/EMANE node to the set of MDRs connected to that node
|
||||
self.wireless_network: Dict[int, Set[int]] = {}
|
||||
|
@ -108,6 +110,7 @@ class CanvasGraph(tk.Canvas):
|
|||
self.show_wireless: ShowVar = ShowVar(self, tags.WIRELESS_EDGE, value=True)
|
||||
self.show_grid: ShowVar = ShowVar(self, tags.GRIDLINE, value=True)
|
||||
self.show_annotations: ShowVar = ShowVar(self, tags.ANNOTATION, value=True)
|
||||
self.show_loss_links: ShowVar = ShowVar(self, tags.LOSS_EDGES, value=True)
|
||||
self.show_iface_names: BooleanVar = BooleanVar(value=False)
|
||||
self.show_ip4s: BooleanVar = BooleanVar(value=True)
|
||||
self.show_ip6s: BooleanVar = BooleanVar(value=True)
|
||||
|
@ -145,6 +148,7 @@ class CanvasGraph(tk.Canvas):
|
|||
self.show_iface_names.set(False)
|
||||
self.show_ip4s.set(True)
|
||||
self.show_ip6s.set(True)
|
||||
self.show_loss_links.set(True)
|
||||
|
||||
# delete any existing drawn items
|
||||
for tag in tags.RESET_TAGS:
|
||||
|
@ -206,14 +210,9 @@ class CanvasGraph(tk.Canvas):
|
|||
iface_id = iface_throughput.iface_id
|
||||
throughput = iface_throughput.throughput
|
||||
iface_to_edge_id = (node_id, iface_id)
|
||||
token = self.core.iface_to_edge.get(iface_to_edge_id)
|
||||
if not token:
|
||||
continue
|
||||
edge = self.edges.get(token)
|
||||
edge = self.core.iface_to_edge.get(iface_to_edge_id)
|
||||
if edge:
|
||||
edge.set_throughput(throughput)
|
||||
else:
|
||||
del self.core.iface_to_edge[iface_to_edge_id]
|
||||
|
||||
def draw_grid(self) -> None:
|
||||
"""
|
||||
|
@ -230,7 +229,7 @@ class CanvasGraph(tk.Canvas):
|
|||
self.tag_lower(self.rect)
|
||||
|
||||
def add_wired_edge(self, src: CanvasNode, dst: CanvasNode, link: Link) -> None:
|
||||
token = create_edge_token(src.id, dst.id)
|
||||
token = create_edge_token(link)
|
||||
if token in self.edges and link.options.unidirectional:
|
||||
edge = self.edges[token]
|
||||
edge.asymmetric_link = link
|
||||
|
@ -240,71 +239,52 @@ class CanvasGraph(tk.Canvas):
|
|||
src_pos = (node1.position.x, node1.position.y)
|
||||
dst_pos = (node2.position.x, node2.position.y)
|
||||
edge = CanvasEdge(self, src.id, src_pos, dst_pos)
|
||||
edge.token = token
|
||||
edge.dst = dst.id
|
||||
edge.set_link(link)
|
||||
edge.check_wireless()
|
||||
src.edges.add(edge)
|
||||
dst.edges.add(edge)
|
||||
self.edges[edge.token] = edge
|
||||
self.core.links[edge.token] = edge
|
||||
if link.iface1:
|
||||
iface1 = link.iface1
|
||||
self.core.iface_to_edge[(node1.id, iface1.id)] = token
|
||||
src.ifaces[iface1.id] = iface1
|
||||
edge.src_iface = iface1
|
||||
if link.iface2:
|
||||
iface2 = link.iface2
|
||||
self.core.iface_to_edge[(node2.id, iface2.id)] = edge.token
|
||||
dst.ifaces[iface2.id] = iface2
|
||||
edge.dst_iface = iface2
|
||||
self.complete_edge(src, dst, edge, link)
|
||||
|
||||
def delete_wired_edge(self, src: CanvasNode, dst: CanvasNode) -> None:
|
||||
token = create_edge_token(src.id, dst.id)
|
||||
def delete_wired_edge(self, link: Link) -> None:
|
||||
token = create_edge_token(link)
|
||||
edge = self.edges.get(token)
|
||||
if not edge:
|
||||
return
|
||||
self.delete_edge(edge)
|
||||
if edge:
|
||||
self.delete_edge(edge)
|
||||
|
||||
def update_wired_edge(self, src: CanvasNode, dst: CanvasNode, link: Link) -> None:
|
||||
token = create_edge_token(src.id, dst.id)
|
||||
def update_wired_edge(self, link: Link) -> None:
|
||||
token = create_edge_token(link)
|
||||
edge = self.edges.get(token)
|
||||
if not edge:
|
||||
return
|
||||
edge.link.options = deepcopy(link.options)
|
||||
if edge:
|
||||
edge.link.options = deepcopy(link.options)
|
||||
edge.draw_link_options()
|
||||
edge.check_options()
|
||||
|
||||
def add_wireless_edge(self, src: CanvasNode, dst: CanvasNode, link: Link) -> None:
|
||||
network_id = link.network_id if link.network_id else None
|
||||
token = create_edge_token(src.id, dst.id, network_id)
|
||||
token = create_wireless_token(src.id, dst.id, network_id)
|
||||
if token in self.wireless_edges:
|
||||
logging.warning("ignoring link that already exists: %s", link)
|
||||
return
|
||||
src_pos = self.coords(src.id)
|
||||
dst_pos = self.coords(dst.id)
|
||||
edge = CanvasWirelessEdge(self, src.id, dst.id, src_pos, dst_pos, token, link)
|
||||
edge = CanvasWirelessEdge(
|
||||
self, src.id, dst.id, network_id, token, src_pos, dst_pos, link
|
||||
)
|
||||
self.wireless_edges[token] = edge
|
||||
src.wireless_edges.add(edge)
|
||||
dst.wireless_edges.add(edge)
|
||||
self.tag_raise(src.id)
|
||||
self.tag_raise(dst.id)
|
||||
# update arcs when there are multiple links
|
||||
common_edges = list(src.wireless_edges & dst.wireless_edges)
|
||||
arc_edges(common_edges)
|
||||
self.arc_common_edges(edge)
|
||||
|
||||
def delete_wireless_edge(
|
||||
self, src: CanvasNode, dst: CanvasNode, link: Link
|
||||
) -> None:
|
||||
network_id = link.network_id if link.network_id else None
|
||||
token = create_edge_token(src.id, dst.id, network_id)
|
||||
token = create_wireless_token(src.id, dst.id, network_id)
|
||||
if token not in self.wireless_edges:
|
||||
return
|
||||
edge = self.wireless_edges.pop(token)
|
||||
edge.delete()
|
||||
src.wireless_edges.remove(edge)
|
||||
dst.wireless_edges.remove(edge)
|
||||
# update arcs when there are multiple links
|
||||
common_edges = list(src.wireless_edges & dst.wireless_edges)
|
||||
arc_edges(common_edges)
|
||||
self.arc_common_edges(edge)
|
||||
|
||||
def update_wireless_edge(
|
||||
self, src: CanvasNode, dst: CanvasNode, link: Link
|
||||
|
@ -312,7 +292,7 @@ class CanvasGraph(tk.Canvas):
|
|||
if not link.label:
|
||||
return
|
||||
network_id = link.network_id if link.network_id else None
|
||||
token = create_edge_token(src.id, dst.id, network_id)
|
||||
token = create_wireless_token(src.id, dst.id, network_id)
|
||||
if token not in self.wireless_edges:
|
||||
self.add_wireless_edge(src, dst, link)
|
||||
else:
|
||||
|
@ -453,12 +433,6 @@ class CanvasGraph(tk.Canvas):
|
|||
edge.delete()
|
||||
return
|
||||
|
||||
# ignore repeated edges
|
||||
token = create_edge_token(edge.src, self.selected)
|
||||
if token in self.edges:
|
||||
edge.delete()
|
||||
return
|
||||
|
||||
# rj45 nodes can only support one link
|
||||
if NodeUtils.is_rj45_node(src_node.core_node.type) and src_node.edges:
|
||||
edge.delete()
|
||||
|
@ -467,13 +441,23 @@ class CanvasGraph(tk.Canvas):
|
|||
edge.delete()
|
||||
return
|
||||
|
||||
# set dst node and snap edge to center
|
||||
edge.complete(self.selected)
|
||||
# only 1 link between bridge based nodes
|
||||
is_src_bridge = NodeUtils.is_bridge_node(src_node.core_node)
|
||||
is_dst_bridge = NodeUtils.is_bridge_node(dst_node.core_node)
|
||||
common_links = src_node.edges & dst_node.edges
|
||||
if all([is_src_bridge, is_dst_bridge, common_links]):
|
||||
edge.delete()
|
||||
return
|
||||
|
||||
self.edges[edge.token] = edge
|
||||
src_node.edges.add(edge)
|
||||
dst_node.edges.add(edge)
|
||||
self.core.create_link(edge, src_node, dst_node)
|
||||
# finalize edge creation
|
||||
self.complete_edge(src_node, dst_node, edge)
|
||||
|
||||
def arc_common_edges(self, edge: Edge) -> None:
|
||||
src_node = self.nodes[edge.src]
|
||||
dst_node = self.nodes[edge.dst]
|
||||
common_edges = list(src_node.edges & dst_node.edges)
|
||||
common_edges += list(src_node.wireless_edges & dst_node.wireless_edges)
|
||||
arc_edges(common_edges)
|
||||
|
||||
def select_object(self, object_id: int, choose_multiple: bool = False) -> None:
|
||||
"""
|
||||
|
@ -532,10 +516,10 @@ class CanvasGraph(tk.Canvas):
|
|||
edge.delete()
|
||||
# update node connected to edge being deleted
|
||||
other_id = edge.src
|
||||
other_iface = edge.src_iface
|
||||
other_iface = edge.link.iface1
|
||||
if edge.src == object_id:
|
||||
other_id = edge.dst
|
||||
other_iface = edge.dst_iface
|
||||
other_iface = edge.link.iface2
|
||||
other_node = self.nodes[other_id]
|
||||
other_node.edges.remove(edge)
|
||||
if other_iface:
|
||||
|
@ -557,12 +541,12 @@ class CanvasGraph(tk.Canvas):
|
|||
del self.edges[edge.token]
|
||||
src_node = self.nodes[edge.src]
|
||||
src_node.edges.discard(edge)
|
||||
if edge.src_iface:
|
||||
del src_node.ifaces[edge.src_iface.id]
|
||||
if edge.link.iface1:
|
||||
del src_node.ifaces[edge.link.iface1.id]
|
||||
dst_node = self.nodes[edge.dst]
|
||||
dst_node.edges.discard(edge)
|
||||
if edge.dst_iface:
|
||||
del dst_node.ifaces[edge.dst_iface.id]
|
||||
if edge.link.iface2:
|
||||
del dst_node.ifaces[edge.link.iface2.id]
|
||||
src_wireless = NodeUtils.is_wireless_node(src_node.core_node.type)
|
||||
if src_wireless:
|
||||
dst_node.delete_antenna()
|
||||
|
@ -570,6 +554,7 @@ class CanvasGraph(tk.Canvas):
|
|||
if dst_wireless:
|
||||
src_node.delete_antenna()
|
||||
self.core.deleted_canvas_edges([edge])
|
||||
self.arc_common_edges(edge)
|
||||
|
||||
def zoom(self, event: tk.Event, factor: float = None) -> None:
|
||||
if not factor:
|
||||
|
@ -901,19 +886,41 @@ class CanvasGraph(tk.Canvas):
|
|||
def is_selection_mode(self) -> bool:
|
||||
return self.mode == GraphMode.SELECT
|
||||
|
||||
def create_edge(self, source: CanvasNode, dest: CanvasNode) -> None:
|
||||
def create_edge(self, src: CanvasNode, dst: CanvasNode) -> CanvasEdge:
|
||||
"""
|
||||
create an edge between source node and destination node
|
||||
"""
|
||||
token = create_edge_token(source.id, dest.id)
|
||||
if token not in self.edges:
|
||||
pos = (source.core_node.position.x, source.core_node.position.y)
|
||||
edge = CanvasEdge(self, source.id, pos, pos)
|
||||
edge.complete(dest.id)
|
||||
self.edges[edge.token] = edge
|
||||
self.nodes[source.id].edges.add(edge)
|
||||
self.nodes[dest.id].edges.add(edge)
|
||||
self.core.create_link(edge, source, dest)
|
||||
pos = (src.core_node.position.x, src.core_node.position.y)
|
||||
edge = CanvasEdge(self, src.id, pos, pos)
|
||||
self.complete_edge(src, dst, edge)
|
||||
return edge
|
||||
|
||||
def complete_edge(
|
||||
self,
|
||||
src: CanvasNode,
|
||||
dst: CanvasNode,
|
||||
edge: CanvasEdge,
|
||||
link: Optional[Link] = None,
|
||||
) -> None:
|
||||
linked_wireless = self.is_linked_wireless(src.id, dst.id)
|
||||
edge.complete(dst.id, linked_wireless)
|
||||
if link is None:
|
||||
link = self.core.create_link(edge, src, dst)
|
||||
edge.link = link
|
||||
if link.iface1:
|
||||
iface1 = link.iface1
|
||||
src.ifaces[iface1.id] = iface1
|
||||
if link.iface2:
|
||||
iface2 = link.iface2
|
||||
dst.ifaces[iface2.id] = iface2
|
||||
src.edges.add(edge)
|
||||
dst.edges.add(edge)
|
||||
edge.token = create_edge_token(edge.link)
|
||||
self.arc_common_edges(edge)
|
||||
edge.draw_labels()
|
||||
edge.check_options()
|
||||
self.edges[edge.token] = edge
|
||||
self.core.save_edge(edge, src, dst)
|
||||
|
||||
def copy(self) -> None:
|
||||
if self.core.is_runtime():
|
||||
|
@ -967,13 +974,12 @@ class CanvasGraph(tk.Canvas):
|
|||
if edge.src not in to_copy_ids or edge.dst not in to_copy_ids:
|
||||
if canvas_node.id == edge.src:
|
||||
dst_node = self.nodes[edge.dst]
|
||||
self.create_edge(node, dst_node)
|
||||
token = create_edge_token(node.id, dst_node.id)
|
||||
copy_edge = self.create_edge(node, dst_node)
|
||||
elif canvas_node.id == edge.dst:
|
||||
src_node = self.nodes[edge.src]
|
||||
self.create_edge(src_node, node)
|
||||
token = create_edge_token(src_node.id, node.id)
|
||||
copy_edge = self.edges[token]
|
||||
copy_edge = self.create_edge(src_node, node)
|
||||
else:
|
||||
continue
|
||||
copy_link = copy_edge.link
|
||||
iface1_id = copy_link.iface1.id if copy_link.iface1 else None
|
||||
iface2_id = copy_link.iface2.id if copy_link.iface2 else None
|
||||
|
@ -1000,13 +1006,11 @@ class CanvasGraph(tk.Canvas):
|
|||
|
||||
# copy link and link config
|
||||
for edge in to_copy_edges:
|
||||
src_node_id = copy_map[edge.token[0]]
|
||||
dst_node_id = copy_map[edge.token[1]]
|
||||
src_node_id = copy_map[edge.src]
|
||||
dst_node_id = copy_map[edge.dst]
|
||||
src_node_copy = self.nodes[src_node_id]
|
||||
dst_node_copy = self.nodes[dst_node_id]
|
||||
self.create_edge(src_node_copy, dst_node_copy)
|
||||
token = create_edge_token(src_node_copy.id, dst_node_copy.id)
|
||||
copy_edge = self.edges[token]
|
||||
copy_edge = self.create_edge(src_node_copy, dst_node_copy)
|
||||
copy_link = copy_edge.link
|
||||
iface1_id = copy_link.iface1.id if copy_link.iface1 else None
|
||||
iface2_id = copy_link.iface2.id if copy_link.iface2 else None
|
||||
|
@ -1035,10 +1039,29 @@ class CanvasGraph(tk.Canvas):
|
|||
)
|
||||
self.tag_raise(tags.NODE)
|
||||
|
||||
def is_linked_wireless(self, src: int, dst: int) -> bool:
|
||||
src_node = self.nodes[src]
|
||||
dst_node = self.nodes[dst]
|
||||
src_node_type = src_node.core_node.type
|
||||
dst_node_type = dst_node.core_node.type
|
||||
is_src_wireless = NodeUtils.is_wireless_node(src_node_type)
|
||||
is_dst_wireless = NodeUtils.is_wireless_node(dst_node_type)
|
||||
|
||||
# update the wlan/EMANE network
|
||||
wlan_network = self.wireless_network
|
||||
if is_src_wireless and not is_dst_wireless:
|
||||
if src not in wlan_network:
|
||||
wlan_network[src] = set()
|
||||
wlan_network[src].add(dst)
|
||||
elif not is_src_wireless and is_dst_wireless:
|
||||
if dst not in wlan_network:
|
||||
wlan_network[dst] = set()
|
||||
wlan_network[dst].add(src)
|
||||
return is_src_wireless or is_dst_wireless
|
||||
|
||||
def clear_throughputs(self) -> None:
|
||||
for edge in self.edges.values():
|
||||
edge.clear_middle_label()
|
||||
edge.draw_link_options()
|
||||
edge.clear_throughput()
|
||||
|
||||
def scale_graph(self) -> None:
|
||||
for nid, canvas_node in self.nodes.items():
|
||||
|
|
|
@ -247,14 +247,18 @@ class CanvasNode:
|
|||
)
|
||||
unlink_menu = tk.Menu(self.context)
|
||||
for edge in self.edges:
|
||||
other_id = edge.src
|
||||
if self.id == other_id:
|
||||
link = edge.link
|
||||
if self.id == edge.src:
|
||||
other_id = edge.dst
|
||||
other_iface = link.iface2.name if link.iface2 else None
|
||||
else:
|
||||
other_id = edge.src
|
||||
other_iface = link.iface1.name if link.iface1 else None
|
||||
other_node = self.canvas.nodes[other_id]
|
||||
other_name = other_node.core_node.name
|
||||
label = f"{other_name}:{other_iface}" if other_iface else other_name
|
||||
func_unlink = functools.partial(self.click_unlink, edge)
|
||||
unlink_menu.add_command(
|
||||
label=other_node.core_node.name, command=func_unlink
|
||||
)
|
||||
unlink_menu.add_command(label=label, command=func_unlink)
|
||||
themes.style_menu(unlink_menu)
|
||||
self.context.add_cascade(label="Unlink", menu=unlink_menu)
|
||||
edit_menu = tk.Menu(self.context)
|
||||
|
@ -318,10 +322,10 @@ class CanvasNode:
|
|||
for edge in self.edges:
|
||||
if self.id == edge.src:
|
||||
other_id = edge.dst
|
||||
edge_iface_id = edge.src_iface.id
|
||||
edge_iface_id = edge.link.iface1.id
|
||||
else:
|
||||
other_id = edge.src
|
||||
edge_iface_id = edge.dst_iface.id
|
||||
edge_iface_id = edge.link.iface2.id
|
||||
if edge_iface_id != iface_id:
|
||||
continue
|
||||
other_node = self.canvas.nodes[other_id]
|
||||
|
|
|
@ -5,6 +5,7 @@ GRIDLINE: str = "gridline"
|
|||
SHAPE: str = "shape"
|
||||
SHAPE_TEXT: str = "shapetext"
|
||||
EDGE: str = "edge"
|
||||
LOSS_EDGES: str = "loss-edge"
|
||||
LINK_LABEL: str = "linklabel"
|
||||
WIRELESS_EDGE: str = "wireless"
|
||||
ANTENNA: str = "antenna"
|
||||
|
|
|
@ -196,10 +196,10 @@ class InterfaceManager:
|
|||
for edge in canvas_node.edges:
|
||||
src_node = canvas.nodes[edge.src]
|
||||
dst_node = canvas.nodes[edge.dst]
|
||||
iface = edge.src_iface
|
||||
iface = edge.link.iface1
|
||||
check_node = src_node
|
||||
if src_node == canvas_node:
|
||||
iface = edge.dst_iface
|
||||
iface = edge.link.iface2
|
||||
check_node = dst_node
|
||||
if check_node.core_node.id in visited:
|
||||
continue
|
||||
|
|
|
@ -172,6 +172,11 @@ class Menubar(tk.Menu):
|
|||
command=self.canvas.show_links.click_handler,
|
||||
variable=self.canvas.show_links,
|
||||
)
|
||||
menu.add_checkbutton(
|
||||
label="Loss Links",
|
||||
command=self.canvas.show_loss_links.click_handler,
|
||||
variable=self.canvas.show_loss_links,
|
||||
)
|
||||
menu.add_checkbutton(
|
||||
label="Wireless Links",
|
||||
command=self.canvas.show_wireless.click_handler,
|
||||
|
|
|
@ -62,12 +62,17 @@ class NodeUtils:
|
|||
IMAGE_NODES: Set[NodeType] = {NodeType.DOCKER, NodeType.LXC}
|
||||
WIRELESS_NODES: Set[NodeType] = {NodeType.WIRELESS_LAN, NodeType.EMANE}
|
||||
RJ45_NODES: Set[NodeType] = {NodeType.RJ45}
|
||||
BRIDGE_NODES: Set[NodeType] = {NodeType.HUB, NodeType.SWITCH}
|
||||
IGNORE_NODES: Set[NodeType] = {NodeType.CONTROL_NET}
|
||||
MOBILITY_NODES: Set[NodeType] = {NodeType.WIRELESS_LAN, NodeType.EMANE}
|
||||
NODE_MODELS: Set[str] = {"router", "host", "PC", "mdr", "prouter"}
|
||||
ROUTER_NODES: Set[str] = {"router", "mdr"}
|
||||
ANTENNA_ICON: PhotoImage = None
|
||||
|
||||
@classmethod
|
||||
def is_bridge_node(cls, node: Node) -> bool:
|
||||
return node.type in cls.BRIDGE_NODES
|
||||
|
||||
@classmethod
|
||||
def is_mobility(cls, node: Node) -> bool:
|
||||
return node.type in cls.MOBILITY_NODES
|
||||
|
|
|
@ -30,8 +30,6 @@ if TYPE_CHECKING:
|
|||
CoreServices = List[Union[CoreService, Type[CoreService]]]
|
||||
ConfigServiceType = Type[ConfigService]
|
||||
|
||||
_DEFAULT_MTU = 1500
|
||||
|
||||
|
||||
class NodeBase(abc.ABC):
|
||||
"""
|
||||
|
|
|
@ -19,6 +19,8 @@ if TYPE_CHECKING:
|
|||
from core.emulator.session import Session
|
||||
from core.nodes.base import CoreNetworkBase, CoreNode
|
||||
|
||||
DEFAULT_MTU: int = 1500
|
||||
|
||||
|
||||
class CoreInterface:
|
||||
"""
|
||||
|
@ -338,7 +340,7 @@ class Veth(CoreInterface):
|
|||
node: "CoreNode",
|
||||
name: str,
|
||||
localname: str,
|
||||
mtu: int = 1500,
|
||||
mtu: int = DEFAULT_MTU,
|
||||
server: "DistributedServer" = None,
|
||||
start: bool = True,
|
||||
) -> None:
|
||||
|
@ -403,7 +405,7 @@ class TunTap(CoreInterface):
|
|||
node: "CoreNode",
|
||||
name: str,
|
||||
localname: str,
|
||||
mtu: int = 1500,
|
||||
mtu: int = DEFAULT_MTU,
|
||||
server: "DistributedServer" = None,
|
||||
start: bool = True,
|
||||
) -> None:
|
||||
|
|
|
@ -3,6 +3,7 @@ Defines network nodes used within core.
|
|||
"""
|
||||
|
||||
import logging
|
||||
import math
|
||||
import threading
|
||||
import time
|
||||
from typing import TYPE_CHECKING, Callable, Dict, List, Optional, Type
|
||||
|
@ -447,77 +448,63 @@ class CoreNetwork(CoreNetworkBase):
|
|||
:param iface2: interface two
|
||||
:return: nothing
|
||||
"""
|
||||
devname = iface.localname
|
||||
tc = f"{TC} qdisc replace dev {devname}"
|
||||
parent = "root"
|
||||
changed = False
|
||||
bw = options.bandwidth
|
||||
if iface.setparam("bw", bw):
|
||||
# from tc-tbf(8): minimum value for burst is rate / kernel_hz
|
||||
burst = max(2 * iface.mtu, int(bw / 1000))
|
||||
# max IP payload
|
||||
limit = 0xFFFF
|
||||
tbf = f"tbf rate {bw} burst {burst} limit {limit}"
|
||||
if bw > 0:
|
||||
if self.up:
|
||||
cmd = f"{tc} {parent} handle 1: {tbf}"
|
||||
iface.host_cmd(cmd)
|
||||
iface.setparam("has_tbf", True)
|
||||
changed = True
|
||||
elif iface.getparam("has_tbf") and bw <= 0:
|
||||
if self.up:
|
||||
cmd = f"{TC} qdisc delete dev {devname} {parent}"
|
||||
iface.host_cmd(cmd)
|
||||
iface.setparam("has_tbf", False)
|
||||
# removing the parent removes the child
|
||||
iface.setparam("has_netem", False)
|
||||
changed = True
|
||||
if iface.getparam("has_tbf"):
|
||||
parent = "parent 1:1"
|
||||
netem = "netem"
|
||||
delay = options.delay
|
||||
changed = max(changed, iface.setparam("delay", delay))
|
||||
loss = options.loss
|
||||
if loss is not None:
|
||||
loss = float(loss)
|
||||
changed = max(changed, iface.setparam("loss", loss))
|
||||
duplicate = options.dup
|
||||
if duplicate is not None:
|
||||
duplicate = int(duplicate)
|
||||
changed = max(changed, iface.setparam("duplicate", duplicate))
|
||||
jitter = options.jitter
|
||||
changed = max(changed, iface.setparam("jitter", jitter))
|
||||
# determine if any settings have changed
|
||||
changed = any(
|
||||
[
|
||||
iface.setparam("bw", options.bandwidth),
|
||||
iface.setparam("delay", options.delay),
|
||||
iface.setparam("loss", options.loss),
|
||||
iface.setparam("duplicate", options.dup),
|
||||
iface.setparam("jitter", options.jitter),
|
||||
iface.setparam("buffer", options.buffer),
|
||||
]
|
||||
)
|
||||
if not changed:
|
||||
return
|
||||
# jitter and delay use the same delay statement
|
||||
if delay is not None:
|
||||
netem += f" delay {delay}us"
|
||||
if jitter is not None:
|
||||
if delay is None:
|
||||
netem += f" delay 0us {jitter}us 25%"
|
||||
else:
|
||||
netem += f" {jitter}us 25%"
|
||||
|
||||
if loss is not None and loss > 0:
|
||||
netem += f" loss {min(loss, 100)}%"
|
||||
if duplicate is not None and duplicate > 0:
|
||||
netem += f" duplicate {min(duplicate, 100)}%"
|
||||
|
||||
delay_check = delay is None or delay <= 0
|
||||
jitter_check = jitter is None or jitter <= 0
|
||||
loss_check = loss is None or loss <= 0
|
||||
duplicate_check = duplicate is None or duplicate <= 0
|
||||
if all([delay_check, jitter_check, loss_check, duplicate_check]):
|
||||
# possibly remove netem if it exists and parent queue wasn't removed
|
||||
# delete tc configuration or create and add it
|
||||
devname = iface.localname
|
||||
if all(
|
||||
[
|
||||
options.delay is None or options.delay <= 0,
|
||||
options.jitter is None or options.jitter <= 0,
|
||||
options.loss is None or options.loss <= 0,
|
||||
options.dup is None or options.dup <= 0,
|
||||
options.bandwidth is None or options.bandwidth <= 0,
|
||||
options.buffer is None or options.buffer <= 0,
|
||||
]
|
||||
):
|
||||
if not iface.getparam("has_netem"):
|
||||
return
|
||||
if self.up:
|
||||
cmd = f"{TC} qdisc delete dev {devname} {parent} handle 10:"
|
||||
cmd = f"{TC} qdisc delete dev {devname} root handle 10:"
|
||||
iface.host_cmd(cmd)
|
||||
iface.setparam("has_netem", False)
|
||||
elif len(netem) > 1:
|
||||
else:
|
||||
netem = ""
|
||||
if options.bandwidth is not None:
|
||||
limit = 1000
|
||||
bw = options.bandwidth / 1000
|
||||
if options.buffer is not None and options.buffer > 0:
|
||||
limit = options.buffer
|
||||
elif options.delay and options.bandwidth:
|
||||
delay = options.delay / 1000
|
||||
limit = max(2, math.ceil((2 * bw * delay) / (8 * iface.mtu)))
|
||||
netem += f" rate {bw}kbit"
|
||||
netem += f" limit {limit}"
|
||||
if options.delay is not None:
|
||||
netem += f" delay {options.delay}us"
|
||||
if options.jitter is not None:
|
||||
if options.delay is None:
|
||||
netem += f" delay 0us {options.jitter}us 25%"
|
||||
else:
|
||||
netem += f" {options.jitter}us 25%"
|
||||
if options.loss is not None and options.loss > 0:
|
||||
netem += f" loss {min(options.loss, 100)}%"
|
||||
if options.dup is not None and options.dup > 0:
|
||||
netem += f" duplicate {min(options.dup, 100)}%"
|
||||
if self.up:
|
||||
cmd = f"{TC} qdisc replace dev {devname} {parent} handle 10: {netem}"
|
||||
cmd = f"{TC} qdisc replace dev {devname} root handle 10: netem {netem}"
|
||||
iface.host_cmd(cmd)
|
||||
iface.setparam("has_netem", True)
|
||||
|
||||
|
|
|
@ -13,7 +13,7 @@ from core.emulator.enumerations import NodeTypes, TransportType
|
|||
from core.errors import CoreCommandError, CoreError
|
||||
from core.executables import MOUNT, TEST, UMOUNT
|
||||
from core.nodes.base import CoreNetworkBase, CoreNodeBase
|
||||
from core.nodes.interface import CoreInterface
|
||||
from core.nodes.interface import DEFAULT_MTU, CoreInterface
|
||||
from core.nodes.network import CoreNetwork, GreTap
|
||||
|
||||
if TYPE_CHECKING:
|
||||
|
@ -252,7 +252,7 @@ class Rj45Node(CoreNodeBase):
|
|||
session: "Session",
|
||||
_id: int = None,
|
||||
name: str = None,
|
||||
mtu: int = 1500,
|
||||
mtu: int = DEFAULT_MTU,
|
||||
server: DistributedServer = None,
|
||||
) -> None:
|
||||
"""
|
||||
|
|
|
@ -8,7 +8,7 @@ import netaddr
|
|||
|
||||
from core.emane.nodes import EmaneNet
|
||||
from core.nodes.base import CoreNode
|
||||
from core.nodes.interface import CoreInterface
|
||||
from core.nodes.interface import DEFAULT_MTU, CoreInterface
|
||||
from core.nodes.network import PtpNet, WlanNode
|
||||
from core.nodes.physical import Rj45Node
|
||||
from core.services.coreservices import CoreService
|
||||
|
@ -384,7 +384,7 @@ class FRROspfv2(FrrService):
|
|||
mtu-ignore command. This is needed when e.g. a node is linked via a
|
||||
GreTap device.
|
||||
"""
|
||||
if iface.mtu != 1500:
|
||||
if iface.mtu != DEFAULT_MTU:
|
||||
# a workaround for PhysicalNode GreTap, which has no knowledge of
|
||||
# the other nodes/nets
|
||||
return " ip ospf mtu-ignore\n"
|
||||
|
|
|
@ -8,7 +8,7 @@ import netaddr
|
|||
from core.emane.nodes import EmaneNet
|
||||
from core.emulator.enumerations import LinkTypes
|
||||
from core.nodes.base import CoreNode
|
||||
from core.nodes.interface import CoreInterface
|
||||
from core.nodes.interface import DEFAULT_MTU, CoreInterface
|
||||
from core.nodes.network import PtpNet, WlanNode
|
||||
from core.nodes.physical import Rj45Node
|
||||
from core.services.coreservices import CoreService
|
||||
|
@ -301,7 +301,7 @@ class Ospfv2(QuaggaService):
|
|||
mtu-ignore command. This is needed when e.g. a node is linked via a
|
||||
GreTap device.
|
||||
"""
|
||||
if iface.mtu != 1500:
|
||||
if iface.mtu != DEFAULT_MTU:
|
||||
# a workaround for PhysicalNode GreTap, which has no knowledge of
|
||||
# the other nodes/nets
|
||||
return " ip ospf mtu-ignore\n"
|
||||
|
|
|
@ -603,7 +603,7 @@ fi;
|
|||
class RadvdService(UtilService):
|
||||
name: str = "radvd"
|
||||
configs: Tuple[str, ...] = ("/etc/radvd/radvd.conf",)
|
||||
dirs: Tuple[str, ...] = ("/etc/radvd",)
|
||||
dirs: Tuple[str, ...] = ("/etc/radvd", "/var/run/radvd")
|
||||
startup: Tuple[str, ...] = (
|
||||
"radvd -C /etc/radvd/radvd.conf -m logfile -l /var/log/radvd.log",
|
||||
)
|
||||
|
|
|
@ -15,6 +15,7 @@ import random
|
|||
import shlex
|
||||
import shutil
|
||||
import sys
|
||||
from pathlib import Path
|
||||
from subprocess import PIPE, STDOUT, Popen
|
||||
from typing import (
|
||||
TYPE_CHECKING,
|
||||
|
@ -315,27 +316,25 @@ def sysctl_devname(devname: str) -> Optional[str]:
|
|||
return devname.replace(".", "/")
|
||||
|
||||
|
||||
def load_config(filename: str, d: Dict[str, str]) -> None:
|
||||
def load_config(file_path: Path, d: Dict[str, str]) -> None:
|
||||
"""
|
||||
Read key=value pairs from a file, into a dict. Skip comments; strip newline
|
||||
characters and spacing.
|
||||
|
||||
:param filename: file to read into a dictionary
|
||||
:param d: dictionary to read file into
|
||||
:param file_path: file path to read data from
|
||||
:param d: dictionary to config into
|
||||
:return: nothing
|
||||
"""
|
||||
with open(filename, "r") as f:
|
||||
with file_path.open("r") as f:
|
||||
lines = f.readlines()
|
||||
|
||||
for line in lines:
|
||||
if line[:1] == "#":
|
||||
continue
|
||||
|
||||
try:
|
||||
key, value = line.split("=", 1)
|
||||
d[key] = value.strip()
|
||||
except ValueError:
|
||||
logging.exception("error reading file to dict: %s", filename)
|
||||
logging.exception("error reading file to dict: %s", file_path)
|
||||
|
||||
|
||||
def load_classes(path: str, clazz: Generic[T]) -> T:
|
||||
|
|
|
@ -192,12 +192,13 @@ def build_platform_xml(
|
|||
add_param(nem_element, platform_endpoint, config[platform_endpoint])
|
||||
transport_endpoint = "transportendpoint"
|
||||
add_param(nem_element, transport_endpoint, config[transport_endpoint])
|
||||
else:
|
||||
transport_name = transport_file_name(iface)
|
||||
transport_element = etree.SubElement(
|
||||
nem_element, "transport", definition=transport_name
|
||||
)
|
||||
add_param(transport_element, "device", iface.name)
|
||||
|
||||
# define transport element
|
||||
transport_name = transport_file_name(iface)
|
||||
transport_element = etree.SubElement(
|
||||
nem_element, "transport", definition=transport_name
|
||||
)
|
||||
add_param(transport_element, "device", iface.name)
|
||||
|
||||
# add nem element to platform element
|
||||
platform_element.append(nem_element)
|
||||
|
|
|
@ -1,7 +1,9 @@
|
|||
"""
|
||||
Example custom emane model.
|
||||
"""
|
||||
from typing import Dict, List, Optional, Set
|
||||
|
||||
from core.config import Configuration
|
||||
from core.emane import emanemanifest, emanemodel
|
||||
|
||||
|
||||
|
@ -9,41 +11,45 @@ class ExampleModel(emanemodel.EmaneModel):
|
|||
"""
|
||||
Custom emane model.
|
||||
|
||||
:var str name: defines the emane model name that will show up in the GUI
|
||||
:cvar name: defines the emane model name that will show up in the GUI
|
||||
|
||||
Mac Definition:
|
||||
:var str mac_library: defines that mac library that the model will reference
|
||||
:var str mac_xml: defines the mac manifest file that will be parsed to obtain configuration options,
|
||||
:cvar mac_library: defines that mac library that the model will reference
|
||||
:cvar mac_xml: defines the mac manifest file that will be parsed to obtain configuration options,
|
||||
that will be displayed within the GUI
|
||||
:var dict mac_mac_defaults: allows you to override options that are maintained within the manifest file above
|
||||
:var list mac_mac_config: parses the manifest file and converts configurations into core supported formats
|
||||
:cvar mac_defaults: allows you to override options that are maintained within the manifest file above
|
||||
:cvar mac_config: parses the manifest file and converts configurations into core supported formats
|
||||
|
||||
Phy Definition:
|
||||
NOTE: phy configuration will default to the universal model as seen below and the below section does not
|
||||
have to be included
|
||||
:var str phy_library: defines that phy library that the model will reference, used if you need to
|
||||
:cvar phy_library: defines that phy library that the model will reference, used if you need to
|
||||
provide a custom phy
|
||||
:var str phy_xml: defines the phy manifest file that will be parsed to obtain configuration options,
|
||||
:cvar phy_xml: defines the phy manifest file that will be parsed to obtain configuration options,
|
||||
that will be displayed within the GUI
|
||||
:var dict phy_defaults: allows you to override options that are maintained within the manifest file above
|
||||
:cvar phy_defaults: allows you to override options that are maintained within the manifest file above
|
||||
or for the default universal model
|
||||
:var list phy_config: parses the manifest file and converts configurations into core supported formats
|
||||
:cvar phy_config: parses the manifest file and converts configurations into core supported formats
|
||||
|
||||
Custom Override Options:
|
||||
NOTE: these options default to what's seen below and do not have to be included
|
||||
:var set config_ignore: allows you to ignore options within phy/mac, used typically if you needed to add
|
||||
:cvar config_ignore: allows you to ignore options within phy/mac, used typically if you needed to add
|
||||
a custom option for display within the gui
|
||||
"""
|
||||
|
||||
name = "emane_example"
|
||||
mac_library = "rfpipemaclayer"
|
||||
mac_xml = "/usr/share/emane/manifest/rfpipemaclayer.xml"
|
||||
mac_defaults = {
|
||||
name: str = "emane_example"
|
||||
mac_library: str = "rfpipemaclayer"
|
||||
mac_xml: str = "/usr/share/emane/manifest/rfpipemaclayer.xml"
|
||||
mac_defaults: Dict[str, str] = {
|
||||
"pcrcurveuri": "/usr/share/emane/xml/models/mac/rfpipe/rfpipepcr.xml"
|
||||
}
|
||||
mac_config = emanemanifest.parse(mac_xml, mac_defaults)
|
||||
phy_library = None
|
||||
phy_xml = "/usr/share/emane/manifest/emanephy.xml"
|
||||
phy_defaults = {"subid": "1", "propagationmodel": "2ray", "noisemode": "none"}
|
||||
phy_config = emanemanifest.parse(phy_xml, phy_defaults)
|
||||
config_ignore = set()
|
||||
mac_config: List[Configuration] = emanemanifest.parse(mac_xml, mac_defaults)
|
||||
phy_library: Optional[str] = None
|
||||
phy_xml: str = "/usr/share/emane/manifest/emanephy.xml"
|
||||
phy_defaults: Dict[str, str] = {
|
||||
"subid": "1",
|
||||
"propagationmodel": "2ray",
|
||||
"noisemode": "none",
|
||||
}
|
||||
phy_config: List[Configuration] = emanemanifest.parse(phy_xml, phy_defaults)
|
||||
config_ignore: Set[str] = set()
|
||||
|
|
115
daemon/examples/myservices/exampleservice.py
Normal file
115
daemon/examples/myservices/exampleservice.py
Normal file
|
@ -0,0 +1,115 @@
|
|||
"""
|
||||
Simple example custom service, used to drive shell commands on a node.
|
||||
"""
|
||||
from typing import Tuple
|
||||
|
||||
from core.nodes.base import CoreNode
|
||||
from core.services.coreservices import CoreService, ServiceMode
|
||||
|
||||
|
||||
class ExampleService(CoreService):
|
||||
"""
|
||||
Example Custom CORE Service
|
||||
|
||||
:cvar name: name used as a unique ID for this service and is required, no spaces
|
||||
:cvar group: allows you to group services within the GUI under a common name
|
||||
:cvar executables: executables this service depends on to function, if executable is
|
||||
not on the path, service will not be loaded
|
||||
:cvar dependencies: services that this service depends on for startup, tuple of
|
||||
service names
|
||||
:cvar dirs: directories that this service will create within a node
|
||||
:cvar configs: files that this service will generate, without a full path this file
|
||||
goes in the node's directory e.g. /tmp/pycore.12345/n1.conf/myfile
|
||||
:cvar startup: commands used to start this service, any non-zero exit code will
|
||||
cause a failure
|
||||
:cvar validate: commands used to validate that a service was started, any non-zero
|
||||
exit code will cause a failure
|
||||
:cvar validation_mode: validation mode, used to determine startup success.
|
||||
NON_BLOCKING - runs startup commands, and validates success with validation commands
|
||||
BLOCKING - runs startup commands, and validates success with the startup commands themselves
|
||||
TIMER - runs startup commands, and validates success by waiting for "validation_timer" alone
|
||||
:cvar validation_timer: time in seconds for a service to wait for validation, before
|
||||
determining success in TIMER/NON_BLOCKING modes.
|
||||
:cvar validation_period: period in seconds to wait before retrying validation,
|
||||
only used in NON_BLOCKING mode
|
||||
:cvar shutdown: shutdown commands to stop this service
|
||||
"""
|
||||
|
||||
name: str = "ExampleService"
|
||||
group: str = "Utility"
|
||||
executables: Tuple[str, ...] = ()
|
||||
dependencies: Tuple[str, ...] = ()
|
||||
dirs: Tuple[str, ...] = ()
|
||||
configs: Tuple[str, ...] = ("myservice1.sh", "myservice2.sh")
|
||||
startup: Tuple[str, ...] = tuple(f"sh {x}" for x in configs)
|
||||
validate: Tuple[str, ...] = ()
|
||||
validation_mode: ServiceMode = ServiceMode.NON_BLOCKING
|
||||
validation_timer: int = 5
|
||||
validation_period: float = 0.5
|
||||
shutdown: Tuple[str, ...] = ()
|
||||
|
||||
@classmethod
|
||||
def on_load(cls) -> None:
|
||||
"""
|
||||
Provides a way to run some arbitrary logic when the service is loaded, possibly
|
||||
to help facilitate dynamic settings for the environment.
|
||||
|
||||
:return: nothing
|
||||
"""
|
||||
pass
|
||||
|
||||
@classmethod
|
||||
def get_configs(cls, node: CoreNode) -> Tuple[str, ...]:
|
||||
"""
|
||||
Provides a way to dynamically generate the config files from the node a service
|
||||
will run. Defaults to the class definition and can be left out entirely if not
|
||||
needed.
|
||||
|
||||
:param node: core node that the service is being ran on
|
||||
:return: tuple of config files to create
|
||||
"""
|
||||
return cls.configs
|
||||
|
||||
@classmethod
|
||||
def generate_config(cls, node: CoreNode, filename: str) -> str:
|
||||
"""
|
||||
Returns a string representation for a file, given the node the service is
|
||||
starting on the config filename that this information will be used for. This
|
||||
must be defined, if "configs" are defined.
|
||||
|
||||
:param node: core node that the service is being ran on
|
||||
:param filename: configuration file to generate
|
||||
:return: configuration file content
|
||||
"""
|
||||
cfg = "#!/bin/sh\n"
|
||||
if filename == cls.configs[0]:
|
||||
cfg += "# auto-generated by MyService (sample.py)\n"
|
||||
for iface in node.get_ifaces():
|
||||
cfg += f'echo "Node {node.name} has interface {iface.name}"\n'
|
||||
elif filename == cls.configs[1]:
|
||||
cfg += "echo hello"
|
||||
return cfg
|
||||
|
||||
@classmethod
|
||||
def get_startup(cls, node: CoreNode) -> Tuple[str, ...]:
|
||||
"""
|
||||
Provides a way to dynamically generate the startup commands from the node a
|
||||
service will run. Defaults to the class definition and can be left out entirely
|
||||
if not needed.
|
||||
|
||||
:param node: core node that the service is being ran on
|
||||
:return: tuple of startup commands to run
|
||||
"""
|
||||
return cls.startup
|
||||
|
||||
@classmethod
|
||||
def get_validate(cls, node: CoreNode) -> Tuple[str, ...]:
|
||||
"""
|
||||
Provides a way to dynamically generate the validate commands from the node a
|
||||
service will run. Defaults to the class definition and can be left out entirely
|
||||
if not needed.
|
||||
|
||||
:param node: core node that the service is being ran on
|
||||
:return: tuple of commands to validate service startup with
|
||||
"""
|
||||
return cls.validate
|
|
@ -1,110 +0,0 @@
|
|||
"""
|
||||
Simple example for a user-defined service.
|
||||
"""
|
||||
|
||||
from core.services.coreservices import CoreService, ServiceMode
|
||||
|
||||
|
||||
class MyService(CoreService):
|
||||
"""
|
||||
Custom CORE Service
|
||||
|
||||
:var str name: name used as a unique ID for this service and is required, no spaces
|
||||
:var str group: allows you to group services within the GUI under a common name
|
||||
:var tuple executables: executables this service depends on to function, if executable is
|
||||
not on the path, service will not be loaded
|
||||
:var tuple dependencies: services that this service depends on for startup, tuple of service names
|
||||
:var tuple dirs: directories that this service will create within a node
|
||||
:var tuple configs: files that this service will generate, without a full path this file goes in
|
||||
the node's directory e.g. /tmp/pycore.12345/n1.conf/myfile
|
||||
:var tuple startup: commands used to start this service, any non-zero exit code will cause a failure
|
||||
:var tuple validate: commands used to validate that a service was started, any non-zero exit code
|
||||
will cause a failure
|
||||
:var ServiceMode validation_mode: validation mode, used to determine startup success.
|
||||
NON_BLOCKING - runs startup commands, and validates success with validation commands
|
||||
BLOCKING - runs startup commands, and validates success with the startup commands themselves
|
||||
TIMER - runs startup commands, and validates success by waiting for "validation_timer" alone
|
||||
:var int validation_timer: time in seconds for a service to wait for validation, before determining
|
||||
success in TIMER/NON_BLOCKING modes.
|
||||
:var float validation_validation_period: period in seconds to wait before retrying validation,
|
||||
only used in NON_BLOCKING mode
|
||||
:var tuple shutdown: shutdown commands to stop this service
|
||||
"""
|
||||
|
||||
name = "MyService"
|
||||
group = "Utility"
|
||||
executables = ()
|
||||
dependencies = ()
|
||||
dirs = ()
|
||||
configs = ("myservice1.sh", "myservice2.sh")
|
||||
startup = tuple(f"sh {x}" for x in configs)
|
||||
validate = ()
|
||||
validation_mode = ServiceMode.NON_BLOCKING
|
||||
validation_timer = 5
|
||||
validation_period = 0.5
|
||||
shutdown = ()
|
||||
|
||||
@classmethod
|
||||
def on_load(cls):
|
||||
"""
|
||||
Provides a way to run some arbitrary logic when the service is loaded, possibly to help facilitate
|
||||
dynamic settings for the environment.
|
||||
|
||||
:return: nothing
|
||||
"""
|
||||
pass
|
||||
|
||||
@classmethod
|
||||
def get_configs(cls, node):
|
||||
"""
|
||||
Provides a way to dynamically generate the config files from the node a service will run.
|
||||
Defaults to the class definition and can be left out entirely if not needed.
|
||||
|
||||
:param node: core node that the service is being ran on
|
||||
:return: tuple of config files to create
|
||||
"""
|
||||
return cls.configs
|
||||
|
||||
@classmethod
|
||||
def generate_config(cls, node, filename):
|
||||
"""
|
||||
Returns a string representation for a file, given the node the service is starting on the config filename
|
||||
that this information will be used for. This must be defined, if "configs" are defined.
|
||||
|
||||
:param node: core node that the service is being ran on
|
||||
:param str filename: configuration file to generate
|
||||
:return: configuration file content
|
||||
:rtype: str
|
||||
"""
|
||||
cfg = "#!/bin/sh\n"
|
||||
|
||||
if filename == cls.configs[0]:
|
||||
cfg += "# auto-generated by MyService (sample.py)\n"
|
||||
for iface in node.get_ifaces():
|
||||
cfg += f'echo "Node {node.name} has interface {iface.name}"\n'
|
||||
elif filename == cls.configs[1]:
|
||||
cfg += "echo hello"
|
||||
|
||||
return cfg
|
||||
|
||||
@classmethod
|
||||
def get_startup(cls, node):
|
||||
"""
|
||||
Provides a way to dynamically generate the startup commands from the node a service will run.
|
||||
Defaults to the class definition and can be left out entirely if not needed.
|
||||
|
||||
:param node: core node that the service is being ran on
|
||||
:return: tuple of startup commands to run
|
||||
"""
|
||||
return cls.startup
|
||||
|
||||
@classmethod
|
||||
def get_validate(cls, node):
|
||||
"""
|
||||
Provides a way to dynamically generate the validate commands from the node a service will run.
|
||||
Defaults to the class definition and can be left out entirely if not needed.
|
||||
|
||||
:param node: core node that the service is being ran on
|
||||
:return: tuple of commands to validate service startup with
|
||||
"""
|
||||
return cls.validate
|
|
@ -777,6 +777,7 @@ message LinkOptions {
|
|||
int64 delay = 8;
|
||||
int32 dup = 9;
|
||||
bool unidirectional = 10;
|
||||
int32 buffer = 11;
|
||||
}
|
||||
|
||||
message Interface {
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
[tool.poetry]
|
||||
name = "core"
|
||||
version = "7.2.1"
|
||||
version = "7.3.0"
|
||||
description = "CORE Common Open Research Emulator"
|
||||
authors = ["Boeing Research and Technology"]
|
||||
license = "BSD-2-Clause"
|
||||
|
|
|
@ -83,6 +83,7 @@ class TestLinks:
|
|||
loss = 25
|
||||
dup = 25
|
||||
jitter = 10
|
||||
buffer = 100
|
||||
node1 = session.add_node(CoreNode)
|
||||
node2 = session.add_node(SwitchNode)
|
||||
iface1_data = ip_prefixes.create_iface(node1)
|
||||
|
@ -93,10 +94,16 @@ class TestLinks:
|
|||
assert iface1.getparam("loss") != loss
|
||||
assert iface1.getparam("duplicate") != dup
|
||||
assert iface1.getparam("jitter") != jitter
|
||||
assert iface1.getparam("buffer") != buffer
|
||||
|
||||
# when
|
||||
options = LinkOptions(
|
||||
delay=delay, bandwidth=bandwidth, loss=loss, dup=dup, jitter=jitter
|
||||
delay=delay,
|
||||
bandwidth=bandwidth,
|
||||
loss=loss,
|
||||
dup=dup,
|
||||
jitter=jitter,
|
||||
buffer=buffer,
|
||||
)
|
||||
session.update_link(
|
||||
node1.id, node2.id, iface1_id=iface1_data.id, options=options
|
||||
|
@ -108,6 +115,7 @@ class TestLinks:
|
|||
assert iface1.getparam("loss") == loss
|
||||
assert iface1.getparam("duplicate") == dup
|
||||
assert iface1.getparam("jitter") == jitter
|
||||
assert iface1.getparam("buffer") == buffer
|
||||
|
||||
def test_update_net_to_node(self, session: Session, ip_prefixes: IpPrefixes):
|
||||
# given
|
||||
|
@ -116,6 +124,7 @@ class TestLinks:
|
|||
loss = 25
|
||||
dup = 25
|
||||
jitter = 10
|
||||
buffer = 100
|
||||
node1 = session.add_node(SwitchNode)
|
||||
node2 = session.add_node(CoreNode)
|
||||
iface2_data = ip_prefixes.create_iface(node2)
|
||||
|
@ -126,10 +135,16 @@ class TestLinks:
|
|||
assert iface2.getparam("loss") != loss
|
||||
assert iface2.getparam("duplicate") != dup
|
||||
assert iface2.getparam("jitter") != jitter
|
||||
assert iface2.getparam("buffer") != buffer
|
||||
|
||||
# when
|
||||
options = LinkOptions(
|
||||
delay=delay, bandwidth=bandwidth, loss=loss, dup=dup, jitter=jitter
|
||||
delay=delay,
|
||||
bandwidth=bandwidth,
|
||||
loss=loss,
|
||||
dup=dup,
|
||||
jitter=jitter,
|
||||
buffer=buffer,
|
||||
)
|
||||
session.update_link(
|
||||
node1.id, node2.id, iface2_id=iface2_data.id, options=options
|
||||
|
@ -141,6 +156,7 @@ class TestLinks:
|
|||
assert iface2.getparam("loss") == loss
|
||||
assert iface2.getparam("duplicate") == dup
|
||||
assert iface2.getparam("jitter") == jitter
|
||||
assert iface2.getparam("buffer") == buffer
|
||||
|
||||
def test_update_ptp(self, session: Session, ip_prefixes: IpPrefixes):
|
||||
# given
|
||||
|
@ -149,6 +165,7 @@ class TestLinks:
|
|||
loss = 25
|
||||
dup = 25
|
||||
jitter = 10
|
||||
buffer = 100
|
||||
node1 = session.add_node(CoreNode)
|
||||
node2 = session.add_node(CoreNode)
|
||||
iface1_data = ip_prefixes.create_iface(node1)
|
||||
|
@ -161,15 +178,22 @@ class TestLinks:
|
|||
assert iface1.getparam("loss") != loss
|
||||
assert iface1.getparam("duplicate") != dup
|
||||
assert iface1.getparam("jitter") != jitter
|
||||
assert iface1.getparam("buffer") != buffer
|
||||
assert iface2.getparam("delay") != delay
|
||||
assert iface2.getparam("bw") != bandwidth
|
||||
assert iface2.getparam("loss") != loss
|
||||
assert iface2.getparam("duplicate") != dup
|
||||
assert iface2.getparam("jitter") != jitter
|
||||
assert iface2.getparam("buffer") != buffer
|
||||
|
||||
# when
|
||||
options = LinkOptions(
|
||||
delay=delay, bandwidth=bandwidth, loss=loss, dup=dup, jitter=jitter
|
||||
delay=delay,
|
||||
bandwidth=bandwidth,
|
||||
loss=loss,
|
||||
dup=dup,
|
||||
jitter=jitter,
|
||||
buffer=buffer,
|
||||
)
|
||||
session.update_link(node1.id, node2.id, iface1_data.id, iface2_data.id, options)
|
||||
|
||||
|
@ -179,11 +203,13 @@ class TestLinks:
|
|||
assert iface1.getparam("loss") == loss
|
||||
assert iface1.getparam("duplicate") == dup
|
||||
assert iface1.getparam("jitter") == jitter
|
||||
assert iface1.getparam("buffer") == buffer
|
||||
assert iface2.getparam("delay") == delay
|
||||
assert iface2.getparam("bw") == bandwidth
|
||||
assert iface2.getparam("loss") == loss
|
||||
assert iface2.getparam("duplicate") == dup
|
||||
assert iface2.getparam("jitter") == jitter
|
||||
assert iface2.getparam("buffer") == buffer
|
||||
|
||||
def test_delete_ptp(self, session: Session, ip_prefixes: IpPrefixes):
|
||||
# given
|
||||
|
|
|
@ -50,6 +50,26 @@ can also subscribe to EMANE location events and move the nodes on the canvas
|
|||
as they are moved in the EMANE emulation. This would occur when an Emulation
|
||||
Script Generator, for example, is running a mobility script.
|
||||
|
||||
## EMANE in CORE
|
||||
|
||||
This section will cover some high level topics and examples for running and
|
||||
using EMANE in CORE.
|
||||
|
||||
You can find more detailed tutorials and examples at the
|
||||
[EMANE Tutorial](https://github.com/adjacentlink/emane-tutorial/wiki).
|
||||
|
||||
Every topic below assumes CORE, EMANE, and OSPF MDR have been installed.
|
||||
|
||||
> **WARNING:** demo files will be found within the new `core-pygui`
|
||||
|
||||
|Topic|Model|Description|
|
||||
|---|---|---|
|
||||
|[XML Files](emane/files.md)|RF Pipe|Overview of generated XML files used to drive EMANE|
|
||||
|[GPSD](emane/gpsd.md)|RF Pipe|Overview of running and integrating gpsd with EMANE|
|
||||
|[Precomputed](emane/precomputed.md)|RF Pipe|Overview of using the precomputed propagation model|
|
||||
|[EEL](emane/eel.md)|RF Pipe|Overview of using the Emulation Event Log (EEL) Generator|
|
||||
|[Antenna Profiles](emane/antenna.md)|RF Pipe|Overview of using antenna profiles in EMANE|
|
||||
|
||||
## EMANE Configuration
|
||||
|
||||
The CORE configuration file **/etc/core/core.conf** has options specific to
|
||||
|
@ -96,7 +116,61 @@ placed within the path defined by **emane_models_dir** in the CORE
|
|||
configuration file. This path cannot end in **/emane**.
|
||||
|
||||
Here is an example model with documentation describing functionality:
|
||||
[Example Model](../daemon/examples/myemane/examplemodel.py)
|
||||
```python
|
||||
"""
|
||||
Example custom emane model.
|
||||
"""
|
||||
from typing import Dict, List, Optional, Set
|
||||
|
||||
from core.config import Configuration
|
||||
from core.emane import emanemanifest, emanemodel
|
||||
|
||||
|
||||
class ExampleModel(emanemodel.EmaneModel):
|
||||
"""
|
||||
Custom emane model.
|
||||
|
||||
:cvar name: defines the emane model name that will show up in the GUI
|
||||
|
||||
Mac Definition:
|
||||
:cvar mac_library: defines that mac library that the model will reference
|
||||
:cvar mac_xml: defines the mac manifest file that will be parsed to obtain configuration options,
|
||||
that will be displayed within the GUI
|
||||
:cvar mac_defaults: allows you to override options that are maintained within the manifest file above
|
||||
:cvar mac_config: parses the manifest file and converts configurations into core supported formats
|
||||
|
||||
Phy Definition:
|
||||
NOTE: phy configuration will default to the universal model as seen below and the below section does not
|
||||
have to be included
|
||||
:cvar phy_library: defines that phy library that the model will reference, used if you need to
|
||||
provide a custom phy
|
||||
:cvar phy_xml: defines the phy manifest file that will be parsed to obtain configuration options,
|
||||
that will be displayed within the GUI
|
||||
:cvar phy_defaults: allows you to override options that are maintained within the manifest file above
|
||||
or for the default universal model
|
||||
:cvar phy_config: parses the manifest file and converts configurations into core supported formats
|
||||
|
||||
Custom Override Options:
|
||||
NOTE: these options default to what's seen below and do not have to be included
|
||||
:cvar config_ignore: allows you to ignore options within phy/mac, used typically if you needed to add
|
||||
a custom option for display within the gui
|
||||
"""
|
||||
|
||||
name: str = "emane_example"
|
||||
mac_library: str = "rfpipemaclayer"
|
||||
mac_xml: str = "/usr/share/emane/manifest/rfpipemaclayer.xml"
|
||||
mac_defaults: Dict[str, str] = {
|
||||
"pcrcurveuri": "/usr/share/emane/xml/models/mac/rfpipe/rfpipepcr.xml"
|
||||
}
|
||||
mac_config: List[Configuration] = emanemanifest.parse(mac_xml, mac_defaults)
|
||||
phy_library: Optional[str] = None
|
||||
phy_xml: str = "/usr/share/emane/manifest/emanephy.xml"
|
||||
phy_defaults: Dict[str, str] = {
|
||||
"subid": "1", "propagationmodel": "2ray", "noisemode": "none"
|
||||
}
|
||||
phy_config: List[Configuration] = emanemanifest.parse(phy_xml, phy_defaults)
|
||||
config_ignore: Set[str] = set()
|
||||
```
|
||||
|
||||
## Single PC with EMANE
|
||||
|
||||
|
|
435
docs/emane/antenna.md
Normal file
435
docs/emane/antenna.md
Normal file
|
@ -0,0 +1,435 @@
|
|||
# EMANE Antenna Profiles
|
||||
* Table of Contents
|
||||
{:toc}
|
||||
|
||||
## Overview
|
||||
Introduction to using the EMANE antenna profile in CORE, based on the example
|
||||
EMANE Demo linked below.
|
||||
|
||||
[EMANE Demo 6](https://github.com/adjacentlink/emane-tutorial/wiki/Demonstration-6)
|
||||
for more specifics.
|
||||
|
||||
## Demo Setup
|
||||
We will need to create some files in advance of starting this session.
|
||||
|
||||
Create directory to place antenna profile files.
|
||||
```shell
|
||||
mkdir /tmp/emane
|
||||
```
|
||||
|
||||
Create `/tmp/emane/antennaprofile.xml` with the following contents.
|
||||
```xml
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE profiles SYSTEM "file:///usr/share/emane/dtd/antennaprofile.dtd">
|
||||
<profiles>
|
||||
<profile id="1"
|
||||
antennapatternuri="/tmp/emane/antenna30dsector.xml"
|
||||
blockagepatternuri="/tmp/emane/blockageaft.xml">
|
||||
<placement north="0" east="0" up="0"/>
|
||||
</profile>
|
||||
<profile id="2"
|
||||
antennapatternuri="/tmp/emane/antenna30dsector.xml">
|
||||
<placement north="0" east="0" up="0"/>
|
||||
</profile>
|
||||
</profiles>
|
||||
```
|
||||
|
||||
Create `/tmp/emane/antenna30dsector.xml` with the following contents.
|
||||
```xml
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE antennaprofile SYSTEM "file:///usr/share/emane/dtd/antennaprofile.dtd">
|
||||
|
||||
<!-- 30degree sector antenna pattern with main beam at +6dB and gain decreasing by 3dB every 5 degrees in elevation or bearing.-->
|
||||
<antennaprofile>
|
||||
<antennapattern>
|
||||
<elevation min='-90' max='-16'>
|
||||
<bearing min='0' max='359'>
|
||||
<gain value='-200'/>
|
||||
</bearing>
|
||||
</elevation>
|
||||
<elevation min='-15' max='-11'>
|
||||
<bearing min='0' max='5'>
|
||||
<gain value='0'/>
|
||||
</bearing>
|
||||
<bearing min='6' max='10'>
|
||||
<gain value='-3'/>
|
||||
</bearing>
|
||||
<bearing min='11' max='15'>
|
||||
<gain value='-6'/>
|
||||
</bearing>
|
||||
<bearing min='16' max='344'>
|
||||
<gain value='-200'/>
|
||||
</bearing>
|
||||
<bearing min='345' max='349'>
|
||||
<gain value='-6'/>
|
||||
</bearing>
|
||||
<bearing min='350' max='354'>
|
||||
<gain value='-3'/>
|
||||
</bearing>
|
||||
<bearing min='355' max='359'>
|
||||
<gain value='0'/>
|
||||
</bearing>
|
||||
</elevation>
|
||||
<elevation min='-10' max='-6'>
|
||||
<bearing min='0' max='5'>
|
||||
<gain value='3'/>
|
||||
</bearing>
|
||||
<bearing min='6' max='10'>
|
||||
<gain value='0'/>
|
||||
</bearing>
|
||||
<bearing min='11' max='15'>
|
||||
<gain value='-3'/>
|
||||
</bearing>
|
||||
<bearing min='16' max='344'>
|
||||
<gain value='-200'/>
|
||||
</bearing>
|
||||
<bearing min='345' max='349'>
|
||||
<gain value='-3'/>
|
||||
</bearing>
|
||||
<bearing min='350' max='354'>
|
||||
<gain value='0'/>
|
||||
</bearing>
|
||||
<bearing min='355' max='359'>
|
||||
<gain value='3'/>
|
||||
</bearing>
|
||||
</elevation>
|
||||
<elevation min='-5' max='-1'>
|
||||
<bearing min='0' max='5'>
|
||||
<gain value='6'/>
|
||||
</bearing>
|
||||
<bearing min='6' max='10'>
|
||||
<gain value='3'/>
|
||||
</bearing>
|
||||
<bearing min='11' max='15'>
|
||||
<gain value='0'/>
|
||||
</bearing>
|
||||
<bearing min='16' max='344'>
|
||||
<gain value='-200'/>
|
||||
</bearing>
|
||||
<bearing min='345' max='349'>
|
||||
<gain value='0'/>
|
||||
</bearing>
|
||||
<bearing min='350' max='354'>
|
||||
<gain value='3'/>
|
||||
</bearing>
|
||||
<bearing min='355' max='359'>
|
||||
<gain value='6'/>
|
||||
</bearing>
|
||||
</elevation>
|
||||
<elevation min='0' max='5'>
|
||||
<bearing min='0' max='5'>
|
||||
<gain value='6'/>
|
||||
</bearing>
|
||||
<bearing min='6' max='10'>
|
||||
<gain value='3'/>
|
||||
</bearing>
|
||||
<bearing min='11' max='15'>
|
||||
<gain value='0'/>
|
||||
</bearing>
|
||||
<bearing min='16' max='344'>
|
||||
<gain value='-200'/>
|
||||
</bearing>
|
||||
<bearing min='345' max='349'>
|
||||
<gain value='0'/>
|
||||
</bearing>
|
||||
<bearing min='350' max='354'>
|
||||
<gain value='3'/>
|
||||
</bearing>
|
||||
<bearing min='355' max='359'>
|
||||
<gain value='6'/>
|
||||
</bearing>
|
||||
</elevation>
|
||||
<elevation min='6' max='10'>
|
||||
<bearing min='0' max='5'>
|
||||
<gain value='3'/>
|
||||
</bearing>
|
||||
<bearing min='6' max='10'>
|
||||
<gain value='0'/>
|
||||
</bearing>
|
||||
<bearing min='11' max='15'>
|
||||
<gain value='-3'/>
|
||||
</bearing>
|
||||
<bearing min='16' max='344'>
|
||||
<gain value='-200'/>
|
||||
</bearing>
|
||||
<bearing min='345' max='349'>
|
||||
<gain value='-3'/>
|
||||
</bearing>
|
||||
<bearing min='350' max='354'>
|
||||
<gain value='0'/>
|
||||
</bearing>
|
||||
<bearing min='355' max='359'>
|
||||
<gain value='3'/>
|
||||
</bearing>
|
||||
</elevation>
|
||||
<elevation min='11' max='15'>
|
||||
<bearing min='0' max='5'>
|
||||
<gain value='0'/>
|
||||
</bearing>
|
||||
<bearing min='6' max='10'>
|
||||
<gain value='-3'/>
|
||||
</bearing>
|
||||
<bearing min='11' max='15'>
|
||||
<gain value='-6'/>
|
||||
</bearing>
|
||||
<bearing min='16' max='344'>
|
||||
<gain value='-200'/>
|
||||
</bearing>
|
||||
<bearing min='345' max='349'>
|
||||
<gain value='-6'/>
|
||||
</bearing>
|
||||
<bearing min='350' max='354'>
|
||||
<gain value='-3'/>
|
||||
</bearing>
|
||||
<bearing min='355' max='359'>
|
||||
<gain value='0'/>
|
||||
</bearing>
|
||||
</elevation>
|
||||
<elevation min='16' max='90'>
|
||||
<bearing min='0' max='359'>
|
||||
<gain value='-200'/>
|
||||
</bearing>
|
||||
</elevation>
|
||||
</antennapattern>
|
||||
</antennaprofile>
|
||||
```
|
||||
|
||||
Create `/tmp/emane/blockageaft.xml` with the following contents.
|
||||
```xml
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE antennaprofile SYSTEM "file:///usr/share/emane/dtd/antennaprofile.dtd">
|
||||
|
||||
<!-- blockage pattern: 1) entire aft in bearing (90 to 270) blocked 2) elevation below -10 blocked, 3) elevation from -10 to -1 is at -10dB to -1 dB 3) elevation from 0 to 90 no blockage-->
|
||||
<antennaprofile>
|
||||
<blockagepattern>
|
||||
<elevation min='-90' max='-11'>
|
||||
<bearing min='0' max='359'>
|
||||
<gain value='-200'/>
|
||||
</bearing>
|
||||
</elevation>
|
||||
<elevation min='-10' max='-10'>
|
||||
<bearing min='0' max='89'>
|
||||
<gain value='-10'/>
|
||||
</bearing>
|
||||
<bearing min='90' max='270'>
|
||||
<gain value='-200'/>
|
||||
</bearing>
|
||||
<bearing min='271' max='359'>
|
||||
<gain value='-10'/>
|
||||
</bearing>
|
||||
</elevation>
|
||||
<elevation min='-9' max='-9'>
|
||||
<bearing min='0' max='89'>
|
||||
<gain value='-9'/>
|
||||
</bearing>
|
||||
<bearing min='90' max='270'>
|
||||
<gain value='-200'/>
|
||||
</bearing>
|
||||
<bearing min='271' max='359'>
|
||||
<gain value='-9'/>
|
||||
</bearing>
|
||||
</elevation>
|
||||
<elevation min='-8' max='-8'>
|
||||
<bearing min='0' max='89'>
|
||||
<gain value='-8'/>
|
||||
</bearing>
|
||||
<bearing min='90' max='270'>
|
||||
<gain value='-200'/>
|
||||
</bearing>
|
||||
<bearing min='271' max='359'>
|
||||
<gain value='-8'/>
|
||||
</bearing>
|
||||
</elevation>
|
||||
<elevation min='-7' max='-7'>
|
||||
<bearing min='0' max='89'>
|
||||
<gain value='-7'/>
|
||||
</bearing>
|
||||
<bearing min='90' max='270'>
|
||||
<gain value='-200'/>
|
||||
</bearing>
|
||||
<bearing min='271' max='359'>
|
||||
<gain value='-7'/>
|
||||
</bearing>
|
||||
</elevation>
|
||||
<elevation min='-6' max='-6'>
|
||||
<bearing min='0' max='89'>
|
||||
<gain value='-6'/>
|
||||
</bearing>
|
||||
<bearing min='90' max='270'>
|
||||
<gain value='-200'/>
|
||||
</bearing>
|
||||
<bearing min='271' max='359'>
|
||||
<gain value='-6'/>
|
||||
</bearing>
|
||||
</elevation>
|
||||
<elevation min='-5' max='-5'>
|
||||
<bearing min='0' max='89'>
|
||||
<gain value='-5'/>
|
||||
</bearing>
|
||||
<bearing min='90' max='270'>
|
||||
<gain value='-200'/>
|
||||
</bearing>
|
||||
<bearing min='271' max='359'>
|
||||
<gain value='-5'/>
|
||||
</bearing>
|
||||
</elevation>
|
||||
<elevation min='-4' max='-4'>
|
||||
<bearing min='0' max='89'>
|
||||
<gain value='-4'/>
|
||||
</bearing>
|
||||
<bearing min='90' max='270'>
|
||||
<gain value='-200'/>
|
||||
</bearing>
|
||||
<bearing min='271' max='359'>
|
||||
<gain value='-4'/>
|
||||
</bearing>
|
||||
</elevation>
|
||||
<elevation min='-3' max='-3'>
|
||||
<bearing min='0' max='89'>
|
||||
<gain value='-3'/>
|
||||
</bearing>
|
||||
<bearing min='90' max='270'>
|
||||
<gain value='-200'/>
|
||||
</bearing>
|
||||
<bearing min='271' max='359'>
|
||||
<gain value='-3'/>
|
||||
</bearing>
|
||||
</elevation>
|
||||
<elevation min='-2' max='-2'>
|
||||
<bearing min='0' max='89'>
|
||||
<gain value='-2'/>
|
||||
</bearing>
|
||||
<bearing min='90' max='270'>
|
||||
<gain value='-200'/>
|
||||
</bearing>
|
||||
<bearing min='271' max='359'>
|
||||
<gain value='-2'/>
|
||||
</bearing>
|
||||
</elevation>
|
||||
<elevation min='-1' max='-1'>
|
||||
<bearing min='0' max='89'>
|
||||
<gain value='-1'/>
|
||||
</bearing>
|
||||
<bearing min='90' max='270'>
|
||||
<gain value='-200'/>
|
||||
</bearing>
|
||||
<bearing min='271' max='359'>
|
||||
<gain value='-1'/>
|
||||
</bearing>
|
||||
</elevation>
|
||||
<elevation min='0' max='90'>
|
||||
<bearing min='0' max='89'>
|
||||
<gain value='0'/>
|
||||
</bearing>
|
||||
<bearing min='90' max='270'>
|
||||
<gain value='-200'/>
|
||||
</bearing>
|
||||
<bearing min='271' max='359'>
|
||||
<gain value='0'/>
|
||||
</bearing>
|
||||
</elevation>
|
||||
</blockagepattern>
|
||||
</antennaprofile>
|
||||
```
|
||||
|
||||
## Run Demo
|
||||
1. Select `Open...` within the GUI
|
||||
1. Load `emane-demo-antenna.xml`
|
||||
1. Click ![Start Button](../static/gui/start.gif)
|
||||
1. After startup completes, double click n1 to bring up the nodes terminal
|
||||
|
||||
## Example Demo
|
||||
This demo will cover running an EMANE event service to feed in antenna,
|
||||
location, and pathloss events to demonstrate how antenna profiles
|
||||
can be used.
|
||||
|
||||
### EMANE Event Dump
|
||||
On n1 lets dump EMANE events, so when we later run the EMANE event service
|
||||
you can monitor when and what is sent.
|
||||
|
||||
```shell
|
||||
root@n1:/tmp/pycore.44917/n1.conf# emaneevent-dump -i ctrl0
|
||||
```
|
||||
|
||||
### Send EMANE Events
|
||||
On the host machine create the following to send EMANE events.
|
||||
|
||||
> **WARNING:** make sure to set the `eventservicedevice` to the proper control
|
||||
> network value
|
||||
|
||||
Create `eventservice.xml` with the following contents.
|
||||
```xml
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE eventservice SYSTEM "file:///usr/share/emane/dtd/eventservice.dtd">
|
||||
<eventservice>
|
||||
<param name="eventservicegroup" value="224.1.2.8:45703"/>
|
||||
<param name="eventservicedevice" value="b.9001.da"/>
|
||||
<generator definition="eelgenerator.xml"/>
|
||||
</eventservice>
|
||||
```
|
||||
|
||||
Create `eelgenerator.xml` with the following contents.
|
||||
```xml
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE eventgenerator SYSTEM "file:///usr/share/emane/dtd/eventgenerator.dtd">
|
||||
<eventgenerator library="eelgenerator">
|
||||
<param name="inputfile" value="scenario.eel" />
|
||||
<paramlist name="loader">
|
||||
<item value="commeffect:eelloadercommeffect:delta"/>
|
||||
<item value="location,velocity,orientation:eelloaderlocation:delta"/>
|
||||
<item value="pathloss:eelloaderpathloss:delta"/>
|
||||
<item value="antennaprofile:eelloaderantennaprofile:delta"/>
|
||||
</paramlist>
|
||||
</eventgenerator>
|
||||
```
|
||||
|
||||
Create `scenario.eel` with the following contents.
|
||||
```shell
|
||||
0.0 nem:1 antennaprofile 1,0.0,0.0
|
||||
0.0 nem:4 antennaprofile 2,0.0,0.0
|
||||
#
|
||||
0.0 nem:1 pathloss nem:2,60 nem:3,60 nem:4,60
|
||||
0.0 nem:2 pathloss nem:3,60 nem:4,60
|
||||
0.0 nem:3 pathloss nem:4,60
|
||||
#
|
||||
0.0 nem:1 location gps 40.025495,-74.315441,3.0
|
||||
0.0 nem:2 location gps 40.025495,-74.312501,3.0
|
||||
0.0 nem:3 location gps 40.023235,-74.315441,3.0
|
||||
0.0 nem:4 location gps 40.023235,-74.312501,3.0
|
||||
0.0 nem:4 velocity 180.0,0.0,10.0
|
||||
#
|
||||
30.0 nem:1 velocity 20.0,0.0,10.0
|
||||
30.0 nem:1 orientation 0.0,0.0,10.0
|
||||
30.0 nem:1 antennaprofile 1,60.0,0.0
|
||||
30.0 nem:4 velocity 270.0,0.0,10.0
|
||||
#
|
||||
60.0 nem:1 antennaprofile 1,105.0,0.0
|
||||
60.0 nem:4 antennaprofile 2,45.0,0.0
|
||||
#
|
||||
90.0 nem:1 velocity 90.0,0.0,10.0
|
||||
90.0 nem:1 orientation 0.0,0.0,0.0
|
||||
90.0 nem:1 antennaprofile 1,45.0,0.0
|
||||
```
|
||||
|
||||
Run the EMANE event service, monitor what is output on n1 for events
|
||||
dumped and see the link changes within the CORE GUI.
|
||||
```shell
|
||||
emaneeventservice -l 3 eventservice.xml
|
||||
```
|
||||
|
||||
### Stages
|
||||
The events sent will trigger 4 different states.
|
||||
|
||||
* State 1
|
||||
* n2 and n3 see each other
|
||||
* n4 and n3 are pointing away
|
||||
* State 2
|
||||
* n2 and n3 see each other
|
||||
* n1 and n2 see each other
|
||||
* n4 and n3 see each other
|
||||
* State 3
|
||||
* n2 and n3 see each other
|
||||
* n4 and n3 are pointing at each other but blocked
|
||||
* State 4
|
||||
* n2 and n3 see each other
|
||||
* n4 and n3 see each other
|
103
docs/emane/eel.md
Normal file
103
docs/emane/eel.md
Normal file
|
@ -0,0 +1,103 @@
|
|||
# EMANE Emulation Event Log (EEL) Generator
|
||||
* Table of Contents
|
||||
{:toc}
|
||||
|
||||
## Overview
|
||||
Introduction to using the EMANE event service and eel files to provide events.
|
||||
|
||||
[EMANE Demo 1](https://github.com/adjacentlink/emane-tutorial/wiki/Demonstration-1)
|
||||
for more specifics.
|
||||
|
||||
## Run Demo
|
||||
1. Select `Open...` within the GUI
|
||||
1. Load `emane-demo-eel.xml`
|
||||
1. Click ![Start Button](../static/gui/start.gif)
|
||||
1. After startup completes, double click n1 to bring up the nodes terminal
|
||||
|
||||
## Example Demo
|
||||
This demo will go over defining an EMANE event service and eel file to drive
|
||||
an emane event service.
|
||||
|
||||
### Viewing Events
|
||||
On n1 we will use the EMANE event dump utility to listen to events.
|
||||
```shell
|
||||
root@n1:/tmp/pycore.46777/n1.conf# emaneevent-dump -i ctrl0
|
||||
```
|
||||
|
||||
### Sending Events
|
||||
On the host machine we will create the following files and start the
|
||||
EMANE event service targeting the control network.
|
||||
|
||||
> **WARNING:** make sure to set the `eventservicedevice` to the proper control
|
||||
> network value
|
||||
|
||||
Create `eventservice.xml` with the following contents.
|
||||
```xml
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE eventservice SYSTEM "file:///usr/share/emane/dtd/eventservice.dtd">
|
||||
<eventservice>
|
||||
<param name="eventservicegroup" value="224.1.2.8:45703"/>
|
||||
<param name="eventservicedevice" value="b.9001.f"/>
|
||||
<generator definition="eelgenerator.xml"/>
|
||||
</eventservice>
|
||||
```
|
||||
|
||||
Next we will create the `eelgenerator.xml` file. The EEL Generator is actually
|
||||
a plugin that loads sentence parsing plugins. The sentence parsing plugins know
|
||||
how to convert certain sentences, in this case commeffect, location, velocity,
|
||||
orientation, pathloss and antennaprofile sentences, into their corresponding
|
||||
emane event equivalents.
|
||||
|
||||
* commeffect:eelloadercommeffect:delta
|
||||
* location,velocity,orientation:eelloaderlocation:delta
|
||||
* pathloss:eelloaderpathloss:delta
|
||||
* antennaprofile:eelloaderantennaprofile:delta
|
||||
|
||||
These configuration items tell the EEL Generator which sentences to map to
|
||||
which plugin and whether to issue delta or full updates.
|
||||
|
||||
Create `eelgenerator.xml` with the following contents.
|
||||
```xml
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE eventgenerator SYSTEM "file:///usr/share/emane/dtd/eventgenerator.dtd">
|
||||
<eventgenerator library="eelgenerator">
|
||||
<param name="inputfile" value="scenario.eel" />
|
||||
<paramlist name="loader">
|
||||
<item value="commeffect:eelloadercommeffect:delta"/>
|
||||
<item value="location,velocity,orientation:eelloaderlocation:delta"/>
|
||||
<item value="pathloss:eelloaderpathloss:delta"/>
|
||||
<item value="antennaprofile:eelloaderantennaprofile:delta"/>
|
||||
</paramlist>
|
||||
</eventgenerator>
|
||||
```
|
||||
|
||||
Finally, create `scenario.eel` with the following contents.
|
||||
```shell
|
||||
0.0 nem:1 pathloss nem:2,90.0
|
||||
0.0 nem:2 pathloss nem:1,90.0
|
||||
0.0 nem:1 location gps 40.031075,-74.523518,3.000000
|
||||
0.0 nem:2 location gps 40.031165,-74.523412,3.000000
|
||||
```
|
||||
|
||||
Start the EMANE event service using the files created above.
|
||||
```shell
|
||||
emaneeventservice eventservice.xml -l 3
|
||||
```
|
||||
|
||||
### Sent Events
|
||||
If we go back to look at our original terminal we will see the events logged
|
||||
out to the terminal.
|
||||
|
||||
```shell
|
||||
root@n1:/tmp/pycore.46777/n1.conf# emaneevent-dump -i ctrl0
|
||||
[1601858142.917224] nem: 0 event: 100 len: 66 seq: 1 [Location]
|
||||
UUID: 0af267be-17d3-4103-9f76-6f697e13bcec
|
||||
(1, {'latitude': 40.031075, 'altitude': 3.0, 'longitude': -74.823518})
|
||||
(2, {'latitude': 40.031165, 'altitude': 3.0, 'longitude': -74.523412})
|
||||
[1601858142.917466] nem: 1 event: 101 len: 14 seq: 2 [Pathloss]
|
||||
UUID: 0af267be-17d3-4103-9f76-6f697e13bcec
|
||||
(2, {'forward': 90.0, 'reverse': 90.0})
|
||||
[1601858142.917889] nem: 2 event: 101 len: 14 seq: 3 [Pathloss]
|
||||
UUID: 0af267be-17d3-4103-9f76-6f697e13bcec
|
||||
(1, {'forward': 90.0, 'reverse': 90.0})
|
||||
```
|
160
docs/emane/files.md
Normal file
160
docs/emane/files.md
Normal file
|
@ -0,0 +1,160 @@
|
|||
# EMANE XML Files
|
||||
* Table of Contents
|
||||
{:toc}
|
||||
|
||||
## Overview
|
||||
Introduction to the XML files generated by CORE used to drive EMANE for
|
||||
a given node.
|
||||
|
||||
[EMANE Demo 0](https://github.com/adjacentlink/emane-tutorial/wiki/Demonstration-0)
|
||||
may provide more helpful details.
|
||||
|
||||
## Run Demo
|
||||
1. Select `Open...` within the GUI
|
||||
1. Load `emane-demo-files.xml`
|
||||
1. Click ![Start Button](../static/gui/start.gif)
|
||||
1. After startup completes, double click n1 to bring up the nodes terminal
|
||||
|
||||
## Example Demo
|
||||
We will take a look at the files generated in the example demo provided. In this
|
||||
case we are running the RF Pipe model.
|
||||
|
||||
### Generated Files
|
||||
|
||||
|Name|Description|
|
||||
|---|---|
|
||||
|\<node name>-platform.xml|configuration file for the emulator instances|
|
||||
|\<interface name>-nem.xml|configuration for creating a NEM|
|
||||
|\<interface name>-mac.xml|configuration for defining a NEMs MAC layer|
|
||||
|\<interface name>-phy.xml|configuration for defining a NEMs PHY layer|
|
||||
|\<interface name>-trans-virtual.xml|configuration when a virtual transport is being used|
|
||||
|\<interface name>-trans.xml|configuration when a raw transport is being used|
|
||||
|
||||
### Listing File
|
||||
Below are the files within n1 after starting the demo session.
|
||||
|
||||
```shell
|
||||
root@n1:/tmp/pycore.46777/n1.conf# ls
|
||||
eth0-mac.xml eth0-trans-virtual.xml n1-platform.xml var.log
|
||||
eth0-nem.xml ipforward.sh quaggaboot.sh var.run
|
||||
eth0-phy.xml n1-emane.log usr.local.etc.quagga var.run.quagga
|
||||
```
|
||||
|
||||
### Platform XML
|
||||
The root configuration file used to run EMANE for a node is the platform xml file.
|
||||
In this demo we are looking at `n1-platform.xml`.
|
||||
|
||||
* lists all configuration values set for the platform
|
||||
* The unique nem id given for each interface that EMANE will create for this node
|
||||
* The path to the file(s) used for definition for a given nem
|
||||
|
||||
```shell
|
||||
root@n1:/tmp/pycore.46777/n1.conf# cat n1-platform.xml
|
||||
<?xml version='1.0' encoding='UTF-8'?>
|
||||
<!DOCTYPE platform SYSTEM "file:///usr/share/emane/dtd/platform.dtd">
|
||||
<platform>
|
||||
<param name="antennaprofilemanifesturi" value=""/>
|
||||
<param name="controlportendpoint" value="0.0.0.0:47000"/>
|
||||
<param name="eventservicedevice" value="ctrl0"/>
|
||||
<param name="eventservicegroup" value="224.1.2.8:45703"/>
|
||||
<param name="eventservicettl" value="1"/>
|
||||
<param name="otamanagerchannelenable" value="1"/>
|
||||
<param name="otamanagerdevice" value="ctrl0"/>
|
||||
<param name="otamanagergroup" value="224.1.2.8:45702"/>
|
||||
<param name="otamanagerloopback" value="0"/>
|
||||
<param name="otamanagermtu" value="0"/>
|
||||
<param name="otamanagerpartcheckthreshold" value="2"/>
|
||||
<param name="otamanagerparttimeoutthreshold" value="5"/>
|
||||
<param name="otamanagerttl" value="1"/>
|
||||
<param name="stats.event.maxeventcountrows" value="0"/>
|
||||
<param name="stats.ota.maxeventcountrows" value="0"/>
|
||||
<param name="stats.ota.maxpacketcountrows" value="0"/>
|
||||
<nem id="1" name="tap1.0.f" definition="eth0-nem.xml">
|
||||
<transport definition="eth0-trans-virtual.xml">
|
||||
<param name="device" value="eth0"/>
|
||||
</transport>
|
||||
</nem>
|
||||
</platform>
|
||||
```
|
||||
|
||||
### NEM XML
|
||||
The nem definition will contain reference to the transport, mac, and phy xml
|
||||
definitions being used for a given nem.
|
||||
|
||||
```shell
|
||||
root@n1:/tmp/pycore.46777/n1.conf# cat eth0-nem.xml
|
||||
<?xml version='1.0' encoding='UTF-8'?>
|
||||
<!DOCTYPE nem SYSTEM "file:///usr/share/emane/dtd/nem.dtd">
|
||||
<nem name="emane_rfpipe NEM">
|
||||
<transport definition="eth0-trans-virtual.xml"/>
|
||||
<mac definition="eth0-mac.xml"/>
|
||||
<phy definition="eth0-phy.xml"/>
|
||||
</nem>
|
||||
```
|
||||
|
||||
### MAC XML
|
||||
MAC layer configuration settings would be found in this file. CORE will write
|
||||
out all values, even if the value is a default value.
|
||||
|
||||
```shell
|
||||
root@n1:/tmp/pycore.46777/n1.conf# cat eth0-mac.xml
|
||||
<?xml version='1.0' encoding='UTF-8'?>
|
||||
<!DOCTYPE mac SYSTEM "file:///usr/share/emane/dtd/mac.dtd">
|
||||
<mac name="emane_rfpipe MAC" library="rfpipemaclayer">
|
||||
<param name="datarate" value="1000000"/>
|
||||
<param name="delay" value="0.000000"/>
|
||||
<param name="enablepromiscuousmode" value="0"/>
|
||||
<param name="flowcontrolenable" value="0"/>
|
||||
<param name="flowcontroltokens" value="10"/>
|
||||
<param name="jitter" value="0.000000"/>
|
||||
<param name="neighbormetricdeletetime" value="60.000000"/>
|
||||
<param name="pcrcurveuri" value="/usr/share/emane/xml/models/mac/rfpipe/rfpipepcr.xml"/>
|
||||
<param name="radiometricenable" value="0"/>
|
||||
<param name="radiometricreportinterval" value="1.000000"/>
|
||||
</mac>
|
||||
```
|
||||
|
||||
### PHY XML
|
||||
PHY layer configuration settings would be found in this file. CORE will write
|
||||
out all values, even if the value is a default value.
|
||||
|
||||
```shell
|
||||
root@n1:/tmp/pycore.46777/n1.conf# cat eth0-phy.xml
|
||||
<?xml version='1.0' encoding='UTF-8'?>
|
||||
<!DOCTYPE phy SYSTEM "file:///usr/share/emane/dtd/phy.dtd">
|
||||
<phy name="emane_rfpipe PHY">
|
||||
<param name="bandwidth" value="1000000"/>
|
||||
<param name="fading.model" value="none"/>
|
||||
<param name="fading.nakagami.distance0" value="100.000000"/>
|
||||
<param name="fading.nakagami.distance1" value="250.000000"/>
|
||||
<param name="fading.nakagami.m0" value="0.750000"/>
|
||||
<param name="fading.nakagami.m1" value="1.000000"/>
|
||||
<param name="fading.nakagami.m2" value="200.000000"/>
|
||||
<param name="fixedantennagain" value="0.000000"/>
|
||||
<param name="fixedantennagainenable" value="1"/>
|
||||
<param name="frequency" value="2347000000"/>
|
||||
<param name="frequencyofinterest" value="2347000000"/>
|
||||
<param name="noisebinsize" value="20"/>
|
||||
<param name="noisemaxclampenable" value="0"/>
|
||||
<param name="noisemaxmessagepropagation" value="200000"/>
|
||||
<param name="noisemaxsegmentduration" value="1000000"/>
|
||||
<param name="noisemaxsegmentoffset" value="300000"/>
|
||||
<param name="noisemode" value="none"/>
|
||||
<param name="propagationmodel" value="2ray"/>
|
||||
<param name="subid" value="1"/>
|
||||
<param name="systemnoisefigure" value="4.000000"/>
|
||||
<param name="timesyncthreshold" value="10000"/>
|
||||
<param name="txpower" value="0.000000"/>
|
||||
</phy>
|
||||
```
|
||||
|
||||
### Transport XML
|
||||
```shell
|
||||
root@n1:/tmp/pycore.46777/n1.conf# cat eth0-trans-virtual.xml
|
||||
<?xml version='1.0' encoding='UTF-8'?>
|
||||
<!DOCTYPE transport SYSTEM "file:///usr/share/emane/dtd/transport.dtd">
|
||||
<transport name="Virtual Transport" library="transvirtual">
|
||||
<param name="bitrate" value="0"/>
|
||||
<param name="devicepath" value="/dev/net/tun"/>
|
||||
</transport>
|
||||
```
|
103
docs/emane/gpsd.md
Normal file
103
docs/emane/gpsd.md
Normal file
|
@ -0,0 +1,103 @@
|
|||
# EMANE GPSD Integration
|
||||
* Table of Contents
|
||||
{:toc}
|
||||
|
||||
## Overview
|
||||
Introduction to integrating gpsd in CORE with EMANE.
|
||||
|
||||
[EMANE Demo 0](https://github.com/adjacentlink/emane-tutorial/wiki/Demonstration-0)
|
||||
may provide more helpful details.
|
||||
|
||||
> **WARNING:** requires installation of [gpsd](https://gpsd.gitlab.io/gpsd/index.html)
|
||||
|
||||
## Run Demo
|
||||
1. Select `Open...` within the GUI
|
||||
1. Load `emane-demo-gpsd.xml`
|
||||
1. Click ![Start Button](../static/gui/start.gif)
|
||||
1. After startup completes, double click n1 to bring up the nodes terminal
|
||||
|
||||
## Example Demo
|
||||
This section will cover how to run a gpsd location agent within EMANE, that will
|
||||
write out locations to a pseudo terminal file. That file can be read in by the
|
||||
gpsd server and make EMANE location events available to gpsd clients.
|
||||
|
||||
### EMANE GPSD Event Daemon
|
||||
First create an `eventdaemon.xml` file on n1 with the following contents.
|
||||
```xml
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE eventdaemon SYSTEM "file:///usr/share/emane/dtd/eventdaemon.dtd">
|
||||
<eventdaemon nemid="1">
|
||||
<param name="eventservicegroup" value="224.1.2.8:45703"/>
|
||||
<param name="eventservicedevice" value="ctrl0"/>
|
||||
<agent definition="gpsdlocationagent.xml"/>
|
||||
</eventdaemon>
|
||||
```
|
||||
|
||||
Then create the `gpsdlocationagent.xml` file on n1 with the following contents.
|
||||
```xml
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE eventagent SYSTEM "file:///usr/share/emane/dtd/eventagent.dtd">
|
||||
<eventagent library="gpsdlocationagent">
|
||||
<param name="pseudoterminalfile" value="gps.pty"/>
|
||||
</eventagent>
|
||||
```
|
||||
|
||||
Start the EMANE event agent. This will facilitate feeding location events
|
||||
out to a pseudo terminal file defined above.
|
||||
```shell
|
||||
emaneeventd eventdaemon.xml -r -d -l 3 -f emaneeventd.log
|
||||
```
|
||||
|
||||
Start gpsd, reading in the pseudo terminal file.
|
||||
```shell
|
||||
gpsd -G -n -b $(cat gps.pty)
|
||||
```
|
||||
|
||||
### EMANE EEL Event Daemon
|
||||
|
||||
EEL Events will be played out from the actual host machine over the designated
|
||||
control network interface. Create the following files in the same directory
|
||||
somewhere on your host.
|
||||
|
||||
> **NOTE:** make sure the below eventservicedevice matches the control network
|
||||
> device being used on the host for EMANE
|
||||
|
||||
Create `eventservice.xml` on the host machine with the following contents.
|
||||
```xml
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE eventservice SYSTEM "file:///usr/share/emane/dtd/eventservice.dtd">
|
||||
<eventservice>
|
||||
<param name="eventservicegroup" value="224.1.2.8:45703"/>
|
||||
<param name="eventservicedevice" value="b.9001.1"/>
|
||||
<generator definition="eelgenerator.xml"/>
|
||||
</eventservice>
|
||||
```
|
||||
|
||||
Create `eelgenerator.xml` on the host machine with the following contents.
|
||||
```xml
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE eventgenerator SYSTEM "file:///usr/share/emane/dtd/eventgenerator.dtd">
|
||||
<eventgenerator library="eelgenerator">
|
||||
<param name="inputfile" value="scenario.eel" />
|
||||
<paramlist name="loader">
|
||||
<item value="commeffect:eelloadercommeffect:delta"/>
|
||||
<item value="location,velocity,orientation:eelloaderlocation:delta"/>
|
||||
<item value="pathloss:eelloaderpathloss:delta"/>
|
||||
<item value="antennaprofile:eelloaderantennaprofile:delta"/>
|
||||
</paramlist>
|
||||
</eventgenerator>
|
||||
```
|
||||
|
||||
Create `scenario.eel` file with the following contents.
|
||||
```shell
|
||||
0.0 nem:1 location gps 40.031075,-74.523518,3.000000
|
||||
0.0 nem:2 location gps 40.031165,-74.523412,3.000000
|
||||
```
|
||||
|
||||
Start the EEL event service, which will send the events defined in the file above
|
||||
over the control network to all EMANE nodes. These location events will be received
|
||||
and provided to gpsd. This allow gpsd client to connect to and get gps locations.
|
||||
```shell
|
||||
emaneeventservice eventservice.xml -l 3
|
||||
```
|
||||
|
80
docs/emane/precomputed.md
Normal file
80
docs/emane/precomputed.md
Normal file
|
@ -0,0 +1,80 @@
|
|||
# EMANE Procomputed
|
||||
* Table of Contents
|
||||
{:toc}
|
||||
|
||||
## Overview
|
||||
Introduction to using the precomputed propagation model.
|
||||
|
||||
[EMANE Demo 1](https://github.com/adjacentlink/emane-tutorial/wiki/Demonstration-1)
|
||||
for more specifics.
|
||||
|
||||
## Run Demo
|
||||
1. Select `Open...` within the GUI
|
||||
1. Load `emane-demo-precomputed.xml`
|
||||
1. Click ![Start Button](../static/gui/start.gif)
|
||||
1. After startup completes, double click n1 to bring up the nodes terminal
|
||||
|
||||
## Example Demo
|
||||
This demo is uing the RF Pipe model witht he propagation model set to
|
||||
precomputed.
|
||||
|
||||
### Failed Pings
|
||||
Due to using precomputed and having not sent any pathloss events, the nodes
|
||||
cannot ping eachother yet.
|
||||
|
||||
Open a terminal on n1.
|
||||
```shell
|
||||
root@n1:/tmp/pycore.46777/n1.conf# ping 10.0.0.2
|
||||
connect: Network is unreachable
|
||||
```
|
||||
|
||||
### EMANE Shell
|
||||
You can leverage `emanesh` to investigate why packets are being dropped.
|
||||
```shell
|
||||
root@n1:/tmp/pycore.46777/n1.conf# emanesh localhost get table nems phy BroadcastPacketDropTable0 UnicastPacketDropTable0
|
||||
nem 1 phy BroadcastPacketDropTable0
|
||||
| NEM | Out-of-Band | Rx Sensitivity | Propagation Model | Gain Location | Gain Horizon | Gain Profile | Not FOI | Spectrum Clamp | Fade Location | Fade Algorithm | Fade Select |
|
||||
| 2 | 0 | 0 | 169 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
|
||||
|
||||
nem 1 phy UnicastPacketDropTable0
|
||||
| NEM | Out-of-Band | Rx Sensitivity | Propagation Model | Gain Location | Gain Horizon | Gain Profile | Not FOI | Spectrum Clamp | Fade Location | Fade Algorithm | Fade Select |
|
||||
```
|
||||
|
||||
In the example above we can see that the reason packets are being dropped is due to
|
||||
the propogation model and that is because we have not issued any pathloss events.
|
||||
You can run another command to validate if you have received any pathloss events.
|
||||
```shell
|
||||
root@n1:/tmp/pycore.46777/n1.conf# emanesh localhost get table nems phy PathlossEventInfoTable
|
||||
nem 1 phy PathlossEventInfoTable
|
||||
| NEM | Forward Pathloss | Reverse Pathloss |
|
||||
```
|
||||
|
||||
### Pathloss Events
|
||||
On the host we will send pathloss events from all nems to all other nems.
|
||||
|
||||
> **NOTE:** make sure properly specify the right control network device
|
||||
|
||||
```shell
|
||||
emaneevent-pathloss 1:2 90 -i <controlnet device>
|
||||
```
|
||||
|
||||
Now if we check for pathloss events on n2 we will see what was just sent above.
|
||||
```shell
|
||||
root@n1:/tmp/pycore.46777/n1.conf# emanesh localhost get table nems phy PathlossEventInfoTable
|
||||
nem 1 phy PathlossEventInfoTable
|
||||
| NEM | Forward Pathloss | Reverse Pathloss |
|
||||
| 2 | 90.0 | 90.0
|
||||
```
|
||||
|
||||
You should also now be able to ping n1 from n2.
|
||||
```shell
|
||||
root@n1:/tmp/pycore.46777/n1.conf# ping -c 3 10.0.0.2
|
||||
PING 10.0.0.2 (10.0.0.2) 56(84) bytes of data.
|
||||
64 bytes from 10.0.0.2: icmp_seq=1 ttl=64 time=3.06 ms
|
||||
64 bytes from 10.0.0.2: icmp_seq=2 ttl=64 time=2.12 ms
|
||||
64 bytes from 10.0.0.2: icmp_seq=3 ttl=64 time=1.99 ms
|
||||
|
||||
--- 10.0.0.2 ping statistics ---
|
||||
3 packets transmitted, 3 received, 0% packet loss, time 2001ms
|
||||
rtt min/avg/max/mdev = 1.991/2.393/3.062/0.479 ms
|
||||
```
|
114
docs/install.md
114
docs/install.md
|
@ -9,63 +9,14 @@ or build and install a python wheel.
|
|||
|
||||
> **WARNING:** if Docker is installed, the default iptable rules will block CORE traffic
|
||||
|
||||
### Tools Used
|
||||
The following tools will be leveraged during installation:
|
||||
|
||||
|Tool|Description|
|
||||
|---|---|
|
||||
|[pip](https://pip.pypa.io/en/stable/)|used to install pipx|
|
||||
|[pipx](https://pipxproject.github.io/pipx/)|used to install standalone python tools (invoke, poetry)|
|
||||
|[invoke](http://www.pyinvoke.org/)|used to run provided tasks (install, uninstall, reinstall, etc)|
|
||||
|[poetry](https://python-poetry.org/)|used to install python virtual environment or building a python wheel|
|
||||
|
||||
### Files
|
||||
The following is a list of files that would be installed after running the automated installation.
|
||||
|
||||
> **NOTE:** the default install prefix is /usr/local, but can be changed as noted below
|
||||
|
||||
* executable files
|
||||
* <prefix>/bin/{core-daemon, core-gui, vcmd, vnoded, etc}
|
||||
* tcl/tk gui files
|
||||
* <prefix>/lib/core
|
||||
* <prefix>/share/core/icons
|
||||
* example imn files
|
||||
* <prefix>/share/core/examples
|
||||
* python files
|
||||
* poetry virtual env
|
||||
* `cd <repo>/daemon && poetry env info`
|
||||
* ~/.cache/pypoetry/virtualenvs/
|
||||
* local python install
|
||||
* default install path for python3 installation of a wheel
|
||||
* `python3 -c "import core; print(core.__file__)"`
|
||||
* configuration files
|
||||
* /etc/core/{core.conf, logging.conf}
|
||||
* ospf mdr repository files
|
||||
* <repo>/../ospf-mdr
|
||||
* emane repository files
|
||||
* <repo>/../emane
|
||||
|
||||
### Installed Executables
|
||||
After the installation complete it will have installed the following scripts.
|
||||
|
||||
| Name | Description |
|
||||
|---|---|
|
||||
| core-cleanup | tool to help removed lingering core created containers, bridges, directories |
|
||||
| core-cli | tool to query, open xml files, and send commands using gRPC |
|
||||
| core-daemon | runs the backed core server providing TLV and gRPC APIs |
|
||||
| core-gui | runs the legacy tcl/tk based GUI |
|
||||
| core-imn-to-xml | tool to help automate converting a .imn file to .xml format |
|
||||
| core-manage | tool to add, remove, or check for services, models, and node types |
|
||||
| core-pygui | runs the new python/tk based GUI |
|
||||
| core-python | provides a convenience for running the core python virtual environment |
|
||||
| core-route-monitor | tool to help monitor traffic across nodes and feed that to SDT |
|
||||
| core-service-update | tool to update automate modifying a legacy service to match current naming |
|
||||
| coresendmsg | tool to send TLV API commands from command line |
|
||||
|
||||
### Required Hardware
|
||||
### Requirements
|
||||
Any computer capable of running Linux should be able to run CORE. Since the physical machine will be hosting numerous
|
||||
containers, as a general rule you should select a machine having as much RAM and CPU resources as possible.
|
||||
|
||||
* Linux Kernel v3.3+
|
||||
* iproute2 4.5+ is a requirement for bridge related commands
|
||||
* ebtables not backed by nftables
|
||||
|
||||
### Supported Linux Distributions
|
||||
Plan is to support recent Ubuntu and CentOS LTS releases.
|
||||
|
||||
|
@ -89,11 +40,58 @@ sudo yum install -y kernel-modules-extra
|
|||
sudo modprobe sch_netem
|
||||
```
|
||||
|
||||
### Utility Requirements
|
||||
The following are known dependencies that will result in errors when not met.
|
||||
### Tools Used
|
||||
The following tools will be leveraged during installation:
|
||||
|
||||
* iproute2 4.5+ is a requirement for bridge related commands
|
||||
* ebtables not backed by nftables
|
||||
|Tool|Description|
|
||||
|---|---|
|
||||
|[pip](https://pip.pypa.io/en/stable/)|used to install pipx|
|
||||
|[pipx](https://pipxproject.github.io/pipx/)|used to install standalone python tools (invoke, poetry)|
|
||||
|[invoke](http://www.pyinvoke.org/)|used to run provided tasks (install, uninstall, reinstall, etc)|
|
||||
|[poetry](https://python-poetry.org/)|used to install python virtual environment or building a python wheel|
|
||||
|
||||
### Files
|
||||
The following is a list of files that would be installed after running the automated installation.
|
||||
|
||||
> **NOTE:** the default install prefix is /usr/local, but can be changed as noted below
|
||||
|
||||
* executable files
|
||||
* `<prefix>/bin/{core-daemon, core-gui, vcmd, vnoded, etc}`
|
||||
* tcl/tk gui files
|
||||
* `<prefix>/lib/core`
|
||||
* `<prefix>/share/core/icons`
|
||||
* example imn files
|
||||
* `<prefix>/share/core/examples`
|
||||
* python files
|
||||
* poetry virtual env
|
||||
* `cd <repo>/daemon && poetry env info`
|
||||
* `~/.cache/pypoetry/virtualenvs/`
|
||||
* local python install
|
||||
* default install path for python3 installation of a wheel
|
||||
* `python3 -c "import core; print(core.__file__)"`
|
||||
* configuration files
|
||||
* `/etc/core/{core.conf, logging.conf}`
|
||||
* ospf mdr repository files
|
||||
* `<repo>/../ospf-mdr`
|
||||
* emane repository files
|
||||
* `<repo>/../emane`
|
||||
|
||||
### Installed Executables
|
||||
After the installation complete it will have installed the following scripts.
|
||||
|
||||
| Name | Description |
|
||||
|---|---|
|
||||
| core-cleanup | tool to help removed lingering core created containers, bridges, directories |
|
||||
| core-cli | tool to query, open xml files, and send commands using gRPC |
|
||||
| core-daemon | runs the backed core server providing TLV and gRPC APIs |
|
||||
| core-gui | runs the legacy tcl/tk based GUI |
|
||||
| core-imn-to-xml | tool to help automate converting a .imn file to .xml format |
|
||||
| core-manage | tool to add, remove, or check for services, models, and node types |
|
||||
| core-pygui | runs the new python/tk based GUI |
|
||||
| core-python | provides a convenience for running the core python virtual environment |
|
||||
| core-route-monitor | tool to help monitor traffic across nodes and feed that to SDT |
|
||||
| core-service-update | tool to update automate modifying a legacy service to match current naming |
|
||||
| coresendmsg | tool to send TLV API commands from command line |
|
||||
|
||||
## Upgrading from Older Release
|
||||
Please make sure to uninstall any previous installations of CORE cleanly
|
||||
|
|
111
docs/services.md
111
docs/services.md
|
@ -180,68 +180,77 @@ consider contributing it to the CORE project.
|
|||
Below is the skeleton for a custom service with some documentation. Most
|
||||
people would likely only setup the required class variables **(name/group)**.
|
||||
Then define the **configs** (files they want to generate) and implement the
|
||||
**generate_confifs** function to dynamically create the files wanted. Finally
|
||||
**generate_config** function to dynamically create the files wanted. Finally
|
||||
the **startup** commands would be supplied, which typically tends to be
|
||||
running the shell files generated.
|
||||
|
||||
```python
|
||||
"""
|
||||
Simple example custom service, used to drive shell commands on a node.
|
||||
"""
|
||||
from typing import Tuple
|
||||
|
||||
from core.nodes.base import CoreNode
|
||||
from core.services.coreservices import CoreService, ServiceMode
|
||||
|
||||
|
||||
class MyService(CoreService):
|
||||
class ExampleService(CoreService):
|
||||
"""
|
||||
Custom CORE Service
|
||||
Example Custom CORE Service
|
||||
|
||||
:var str name: name used as a unique ID for this service and is required, no spaces
|
||||
:var str group: allows you to group services within the GUI under a common name
|
||||
:var tuple executables: executables this service depends on to function, if executable is
|
||||
:cvar name: name used as a unique ID for this service and is required, no spaces
|
||||
:cvar group: allows you to group services within the GUI under a common name
|
||||
:cvar executables: executables this service depends on to function, if executable is
|
||||
not on the path, service will not be loaded
|
||||
:var tuple dependencies: services that this service depends on for startup, tuple of service names
|
||||
:var tuple dirs: directories that this service will create within a node
|
||||
:var tuple configs: files that this service will generate, without a full path this file goes in
|
||||
the node's directory e.g. /tmp/pycore.12345/n1.conf/myfile
|
||||
:var tuple startup: commands used to start this service, any non-zero exit code will cause a failure
|
||||
:var tuple validate: commands used to validate that a service was started, any non-zero exit code
|
||||
will cause a failure
|
||||
:var ServiceMode validation_mode: validation mode, used to determine startup success.
|
||||
:cvar dependencies: services that this service depends on for startup, tuple of
|
||||
service names
|
||||
:cvar dirs: directories that this service will create within a node
|
||||
:cvar configs: files that this service will generate, without a full path this file
|
||||
goes in the node's directory e.g. /tmp/pycore.12345/n1.conf/myfile
|
||||
:cvar startup: commands used to start this service, any non-zero exit code will
|
||||
cause a failure
|
||||
:cvar validate: commands used to validate that a service was started, any non-zero
|
||||
exit code will cause a failure
|
||||
:cvar validation_mode: validation mode, used to determine startup success.
|
||||
NON_BLOCKING - runs startup commands, and validates success with validation commands
|
||||
BLOCKING - runs startup commands, and validates success with the startup commands themselves
|
||||
TIMER - runs startup commands, and validates success by waiting for "validation_timer" alone
|
||||
:var int validation_timer: time in seconds for a service to wait for validation, before determining
|
||||
success in TIMER/NON_BLOCKING modes.
|
||||
:var float validation_validation_period: period in seconds to wait before retrying validation,
|
||||
:cvar validation_timer: time in seconds for a service to wait for validation, before
|
||||
determining success in TIMER/NON_BLOCKING modes.
|
||||
:cvar validation_period: period in seconds to wait before retrying validation,
|
||||
only used in NON_BLOCKING mode
|
||||
:var tuple shutdown: shutdown commands to stop this service
|
||||
:cvar shutdown: shutdown commands to stop this service
|
||||
"""
|
||||
|
||||
name = "MyService"
|
||||
group = "Utility"
|
||||
executables = ()
|
||||
dependencies = ()
|
||||
dirs = ()
|
||||
configs = ("myservice1.sh", "myservice2.sh")
|
||||
startup = tuple(f"sh {x}" for x in configs)
|
||||
validate = ()
|
||||
validation_mode = ServiceMode.NON_BLOCKING
|
||||
validation_timer = 5
|
||||
validation_period = 0.5
|
||||
shutdown = ()
|
||||
name: str = "ExampleService"
|
||||
group: str = "Utility"
|
||||
executables: Tuple[str, ...] = ()
|
||||
dependencies: Tuple[str, ...] = ()
|
||||
dirs: Tuple[str, ...] = ()
|
||||
configs: Tuple[str, ...] = ("myservice1.sh", "myservice2.sh")
|
||||
startup: Tuple[str, ...] = tuple(f"sh {x}" for x in configs)
|
||||
validate: Tuple[str, ...] = ()
|
||||
validation_mode: ServiceMode = ServiceMode.NON_BLOCKING
|
||||
validation_timer: int = 5
|
||||
validation_period: float = 0.5
|
||||
shutdown: Tuple[str, ...] = ()
|
||||
|
||||
@classmethod
|
||||
def on_load(cls):
|
||||
def on_load(cls) -> None:
|
||||
"""
|
||||
Provides a way to run some arbitrary logic when the service is loaded, possibly to help facilitate
|
||||
dynamic settings for the environment.
|
||||
Provides a way to run some arbitrary logic when the service is loaded, possibly
|
||||
to help facilitate dynamic settings for the environment.
|
||||
|
||||
:return: nothing
|
||||
"""
|
||||
pass
|
||||
|
||||
@classmethod
|
||||
def get_configs(cls, node):
|
||||
def get_configs(cls, node: CoreNode) -> Tuple[str, ...]:
|
||||
"""
|
||||
Provides a way to dynamically generate the config files from the node a service will run.
|
||||
Defaults to the class definition and can be left out entirely if not needed.
|
||||
Provides a way to dynamically generate the config files from the node a service
|
||||
will run. Defaults to the class definition and can be left out entirely if not
|
||||
needed.
|
||||
|
||||
:param node: core node that the service is being ran on
|
||||
:return: tuple of config files to create
|
||||
|
@ -249,32 +258,31 @@ class MyService(CoreService):
|
|||
return cls.configs
|
||||
|
||||
@classmethod
|
||||
def generate_config(cls, node, filename):
|
||||
def generate_config(cls, node: CoreNode, filename: str) -> str:
|
||||
"""
|
||||
Returns a string representation for a file, given the node the service is starting on the config filename
|
||||
that this information will be used for. This must be defined, if "configs" are defined.
|
||||
Returns a string representation for a file, given the node the service is
|
||||
starting on the config filename that this information will be used for. This
|
||||
must be defined, if "configs" are defined.
|
||||
|
||||
:param node: core node that the service is being ran on
|
||||
:param str filename: configuration file to generate
|
||||
:param filename: configuration file to generate
|
||||
:return: configuration file content
|
||||
:rtype: str
|
||||
"""
|
||||
cfg = "#!/bin/sh\n"
|
||||
|
||||
if filename == cls.configs[0]:
|
||||
cfg += "# auto-generated by MyService (sample.py)\n"
|
||||
for ifc in node.get_ifaces():
|
||||
cfg += f'echo "Node {node.name} has interface {ifc.name}"\n'
|
||||
for iface in node.get_ifaces():
|
||||
cfg += f'echo "Node {node.name} has interface {iface.name}"\n'
|
||||
elif filename == cls.configs[1]:
|
||||
cfg += "echo hello"
|
||||
|
||||
return cfg
|
||||
|
||||
@classmethod
|
||||
def get_startup(cls, node):
|
||||
def get_startup(cls, node: CoreNode) -> Tuple[str, ...]:
|
||||
"""
|
||||
Provides a way to dynamically generate the startup commands from the node a service will run.
|
||||
Defaults to the class definition and can be left out entirely if not needed.
|
||||
Provides a way to dynamically generate the startup commands from the node a
|
||||
service will run. Defaults to the class definition and can be left out entirely
|
||||
if not needed.
|
||||
|
||||
:param node: core node that the service is being ran on
|
||||
:return: tuple of startup commands to run
|
||||
|
@ -282,10 +290,11 @@ class MyService(CoreService):
|
|||
return cls.startup
|
||||
|
||||
@classmethod
|
||||
def get_validate(cls, node):
|
||||
def get_validate(cls, node: CoreNode) -> Tuple[str, ...]:
|
||||
"""
|
||||
Provides a way to dynamically generate the validate commands from the node a service will run.
|
||||
Defaults to the class definition and can be left out entirely if not needed.
|
||||
Provides a way to dynamically generate the validate commands from the node a
|
||||
service will run. Defaults to the class definition and can be left out entirely
|
||||
if not needed.
|
||||
|
||||
:param node: core node that the service is being ran on
|
||||
:return: tuple of commands to validate service startup with
|
||||
|
|
Loading…
Reference in a new issue