Merge pull request from coreemu/develop

Develop merge for 8.2.0
This commit is contained in:
bharnden 2022-03-21 21:47:35 -07:00 committed by GitHub
commit 1841fb1110
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
32 changed files with 610 additions and 706 deletions

View file

@ -1,3 +1,17 @@
## 2022-03-21 CORE 8.2.0
* core-gui
* improved failed starts to trigger runtime to allow node investigation
* core-daemon
* improved default service loading to use a full import path
* updated session instantiation to always set to a runtime state
* core-cli
* \#672 - fixed xml loading
* \#578 - restored json flag and added geo output to session overview
* Documentation
* updated emane example and documentation
* improved table markdown
## 2022-02-18 CORE 8.1.0 ## 2022-02-18 CORE 8.1.0
* Installation * Installation

View file

@ -32,7 +32,7 @@ source ~/.bashrc
# Ubuntu # Ubuntu
inv install inv install
# CentOS # CentOS
./install.sh -p /usr inv install -p /usr
``` ```
## Documentation & Support ## Documentation & Support

View file

@ -2,7 +2,7 @@
# Process this file with autoconf to produce a configure script. # Process this file with autoconf to produce a configure script.
# this defines the CORE version number, must be static for AC_INIT # this defines the CORE version number, must be static for AC_INIT
AC_INIT(core, 8.1.0) AC_INIT(core, 8.2.0)
# autoconf and automake initialization # autoconf and automake initialization
AC_CONFIG_SRCDIR([netns/version.h.in]) AC_CONFIG_SRCDIR([netns/version.h.in])

View file

@ -637,6 +637,15 @@ class SessionSummary:
dir=proto.dir, dir=proto.dir,
) )
def to_proto(self) -> core_pb2.SessionSummary:
return core_pb2.SessionSummary(
id=self.id,
state=self.state.value,
nodes=self.nodes,
file=self.file,
dir=self.dir,
)
@dataclass @dataclass
class Hook: class Hook:

View file

@ -43,6 +43,10 @@ class ConfigServiceBootError(Exception):
pass pass
class ConfigServiceTemplateError(Exception):
pass
@dataclass @dataclass
class ShadowDir: class ShadowDir:
path: str path: str
@ -316,7 +320,13 @@ class ConfigService(abc.ABC):
elif self.templates.has_template(template_path): elif self.templates.has_template(template_path):
template = self.templates.get_template(template_path).source template = self.templates.get_template(template_path).source
else: else:
try:
template = self.get_text_template(file) template = self.get_text_template(file)
except Exception as e:
raise ConfigServiceTemplateError(
f"node({self.node.name}) service({self.name}) file({file}) "
f"failure getting template: {e}"
)
template = self.clean_text(template) template = self.clean_text(template)
templates[file] = template templates[file] = template
return templates return templates
@ -340,7 +350,13 @@ class ConfigService(abc.ABC):
elif self.templates.has_template(template_path): elif self.templates.has_template(template_path):
rendered = self.render_template(template_path, data) rendered = self.render_template(template_path, data)
else: else:
try:
text = self.get_text_template(file) text = self.get_text_template(file)
except Exception as e:
raise ConfigServiceTemplateError(
f"node({self.node.name}) service({self.name}) file({file}) "
f"failure getting template: {e}"
)
rendered = self.render_text(text, data) rendered = self.render_text(text, data)
self.node.create_file(file_path, rendered) self.node.create_file(file_path, rendered)
@ -429,20 +445,20 @@ class ConfigService(abc.ABC):
f"{exceptions.text_error_template().render_unicode()}" f"{exceptions.text_error_template().render_unicode()}"
) )
def render_template(self, basename: str, data: Dict[str, Any] = None) -> str: def render_template(self, template_path: str, data: Dict[str, Any] = None) -> str:
""" """
Renders file based template providing all associated data to template. Renders file based template providing all associated data to template.
:param basename: base name for file to render :param template_path: path of file to render
:param data: service specific defined data for template :param data: service specific defined data for template
:return: rendered template :return: rendered template
""" """
try: try:
template = self.templates.get_template(basename) template = self.templates.get_template(template_path)
return self._render(template, data) return self._render(template, data)
except Exception: except Exception:
raise CoreError( raise CoreError(
f"node({self.node.name}) service({self.name}) " f"node({self.node.name}) service({self.name}) file({template_path})"
f"{exceptions.text_error_template().render_template()}" f"{exceptions.text_error_template().render_template()}"
) )

View file

@ -6,7 +6,6 @@ import sys
from pathlib import Path from pathlib import Path
from typing import Dict, List, Type from typing import Dict, List, Type
import core.services
from core import utils from core import utils
from core.configservice.manager import ConfigServiceManager from core.configservice.manager import ConfigServiceManager
from core.emane.modelmanager import EmaneModelManager from core.emane.modelmanager import EmaneModelManager
@ -92,7 +91,7 @@ class CoreEmu:
:return: nothing :return: nothing
""" """
# load default services # load default services
self.service_errors = core.services.load() self.service_errors = ServiceManager.load_locals()
# load custom services # load custom services
service_paths = self.config.get("custom_services_dir") service_paths = self.config.get("custom_services_dir")
logger.debug("custom service paths: %s", service_paths) logger.debug("custom service paths: %s", service_paths)

View file

@ -694,7 +694,11 @@ class Session:
self.run_hook(hook) self.run_hook(hook)
def add_node_file( def add_node_file(
self, node_id: int, src_path: Path, file_path: Path, data: str self,
node_id: int,
src_path: Optional[Path],
file_path: Path,
data: Optional[str],
) -> None: ) -> None:
""" """
Add a file to a node. Add a file to a node.
@ -707,7 +711,7 @@ class Session:
""" """
node = self.get_node(node_id, CoreNode) node = self.get_node(node_id, CoreNode)
if src_path is not None: if src_path is not None:
node.addfile(src_path, file_path) node.copy_file(src_path, file_path)
elif data is not None: elif data is not None:
node.create_file(file_path, data) node.create_file(file_path, data)
@ -1173,34 +1177,30 @@ class Session:
:return: list of service boot errors during startup :return: list of service boot errors during startup
""" """
if self.state == EventTypes.RUNTIME_STATE:
logger.warning("ignoring instantiate, already in runtime state")
return []
# write current nodes out to session directory file # write current nodes out to session directory file
self.write_nodes() self.write_nodes()
# create control net interfaces and network tunnels # create control net interfaces and network tunnels
# which need to exist for emane to sync on location events # which need to exist for emane to sync on location events
# in distributed scenarios # in distributed scenarios
self.add_remove_control_net(0, remove=False) self.add_remove_control_net(0, remove=False)
# initialize distributed tunnels # initialize distributed tunnels
self.distributed.start() self.distributed.start()
# instantiate will be invoked again upon emane configure # instantiate will be invoked again upon emane configure
if self.emane.startup() == EmaneState.NOT_READY: if self.emane.startup() == EmaneState.NOT_READY:
return [] return []
# boot node services and then start mobility # boot node services and then start mobility
exceptions = self.boot_nodes() exceptions = self.boot_nodes()
if not exceptions: if not exceptions:
self.mobility.startup() self.mobility.startup()
# notify listeners that instantiation is complete # notify listeners that instantiation is complete
event = EventData(event_type=EventTypes.INSTANTIATION_COMPLETE) event = EventData(event_type=EventTypes.INSTANTIATION_COMPLETE)
self.broadcast_event(event) self.broadcast_event(event)
# startup event loop
# assume either all nodes have booted already, or there are some self.event_loop.run()
# nodes on slave servers that will be booted and those servers will self.set_state(EventTypes.RUNTIME_STATE, send_event=True)
# send a node status response message
self.check_runtime()
return exceptions return exceptions
def get_node_count(self) -> int: def get_node_count(self) -> int:
@ -1222,28 +1222,6 @@ class Session:
count += 1 count += 1
return count return count
def check_runtime(self) -> None:
"""
Check if we have entered the runtime state, that all nodes have been
started and the emulation is running. Start the event loop once we
have entered runtime (time=0).
:return: nothing
"""
# this is called from instantiate() after receiving an event message
# for the instantiation state
logger.debug(
"session(%s) checking if not in runtime state, current state: %s",
self.id,
self.state.name,
)
if self.state == EventTypes.RUNTIME_STATE:
logger.info("valid runtime state found, returning")
return
# start event loop and set to runtime
self.event_loop.run()
self.set_state(EventTypes.RUNTIME_STATE, send_event=True)
def data_collect(self) -> None: def data_collect(self) -> None:
""" """
Tear down a running session. Stop the event loop and any running Tear down a running session. Stop the event loop and any running

View file

@ -11,7 +11,7 @@ class CoreCommandError(subprocess.CalledProcessError):
def __str__(self) -> str: def __str__(self) -> str:
return ( return (
f"Command({self.cmd}), Status({self.returncode}):\n" f"command({self.cmd}), status({self.returncode}):\n"
f"stdout: {self.output}\nstderr: {self.stderr}" f"stdout: {self.output}\nstderr: {self.stderr}"
) )

View file

@ -304,12 +304,9 @@ class Toolbar(ttk.Frame):
task.start() task.start()
def start_callback(self, result: bool, exceptions: List[str]) -> None: def start_callback(self, result: bool, exceptions: List[str]) -> None:
if result:
self.set_runtime() self.set_runtime()
self.app.core.show_mobility_players() self.app.core.show_mobility_players()
else: if not result and exceptions:
enable_buttons(self.design_frame, enabled=True)
if exceptions:
message = "\n".join(exceptions) message = "\n".join(exceptions)
self.app.show_exception_data( self.app.show_exception_data(
"Start Exception", "Session failed to start", message "Start Exception", "Session failed to start", message

View file

@ -16,8 +16,7 @@ from core.configservice.dependencies import ConfigServiceDependencies
from core.emulator.data import InterfaceData, LinkData from core.emulator.data import InterfaceData, LinkData
from core.emulator.enumerations import LinkTypes, MessageFlags, NodeTypes from core.emulator.enumerations import LinkTypes, MessageFlags, NodeTypes
from core.errors import CoreCommandError, CoreError from core.errors import CoreCommandError, CoreError
from core.executables import MOUNT, TEST, VNODED from core.executables import BASH, MOUNT, TEST, VCMD, VNODED
from core.nodes.client import VnodeClient
from core.nodes.interface import DEFAULT_MTU, CoreInterface, TunTap, Veth from core.nodes.interface import DEFAULT_MTU, CoreInterface, TunTap, Veth
from core.nodes.netclient import LinuxNetClient, get_net_client from core.nodes.netclient import LinuxNetClient, get_net_client
@ -271,18 +270,6 @@ class CoreNodeBase(NodeBase):
""" """
raise NotImplementedError raise NotImplementedError
@abc.abstractmethod
def addfile(self, src_path: Path, file_path: Path) -> None:
"""
Add a file.
:param src_path: source file path
:param file_path: file name to add
:return: nothing
:raises CoreCommandError: when a non-zero exit status occurs
"""
raise NotImplementedError
@abc.abstractmethod @abc.abstractmethod
def cmd(self, args: str, wait: bool = True, shell: bool = False) -> str: def cmd(self, args: str, wait: bool = True, shell: bool = False) -> str:
""" """
@ -516,7 +503,6 @@ class CoreNode(CoreNodeBase):
super().__init__(session, _id, name, server) super().__init__(session, _id, name, server)
self.directory: Optional[Path] = directory self.directory: Optional[Path] = directory
self.ctrlchnlname: Path = self.session.directory / self.name self.ctrlchnlname: Path = self.session.directory / self.name
self.client: Optional[VnodeClient] = None
self.pid: Optional[int] = None self.pid: Optional[int] = None
self.lock: RLock = RLock() self.lock: RLock = RLock()
self._mounts: List[Tuple[Path, Path]] = [] self._mounts: List[Tuple[Path, Path]] = []
@ -558,7 +544,6 @@ class CoreNode(CoreNodeBase):
self.makenodedir() self.makenodedir()
if self.up: if self.up:
raise ValueError("starting a node that is already up") raise ValueError("starting a node that is already up")
# create a new namespace for this node using vnoded # create a new namespace for this node using vnoded
vnoded = ( vnoded = (
f"{VNODED} -v -c {self.ctrlchnlname} -l {self.ctrlchnlname}.log " f"{VNODED} -v -c {self.ctrlchnlname} -l {self.ctrlchnlname}.log "
@ -569,25 +554,17 @@ class CoreNode(CoreNodeBase):
env = self.session.get_environment(state=False) env = self.session.get_environment(state=False)
env["NODE_NUMBER"] = str(self.id) env["NODE_NUMBER"] = str(self.id)
env["NODE_NAME"] = str(self.name) env["NODE_NAME"] = str(self.name)
output = self.host_cmd(vnoded, env=env) output = self.host_cmd(vnoded, env=env)
self.pid = int(output) self.pid = int(output)
logger.debug("node(%s) pid: %s", self.name, self.pid) logger.debug("node(%s) pid: %s", self.name, self.pid)
# create vnode client
self.client = VnodeClient(self.name, self.ctrlchnlname)
# bring up the loopback interface # bring up the loopback interface
logger.debug("bringing up loopback interface") logger.debug("bringing up loopback interface")
self.node_net_client.device_up("lo") self.node_net_client.device_up("lo")
# set hostname for node # set hostname for node
logger.debug("setting hostname: %s", self.name) logger.debug("setting hostname: %s", self.name)
self.node_net_client.set_hostname(self.name) self.node_net_client.set_hostname(self.name)
# mark node as up # mark node as up
self.up = True self.up = True
# create private directories # create private directories
for dir_path in PRIVATE_DIRS: for dir_path in PRIVATE_DIRS:
self.create_dir(dir_path) self.create_dir(dir_path)
@ -621,13 +598,24 @@ class CoreNode(CoreNodeBase):
logger.exception("error removing node directory") logger.exception("error removing node directory")
# clear interface data, close client, and mark self and not up # clear interface data, close client, and mark self and not up
self.ifaces.clear() self.ifaces.clear()
self.client.close()
self.up = False self.up = False
except OSError: except OSError:
logger.exception("error during shutdown") logger.exception("error during shutdown")
finally: finally:
self.rmnodedir() self.rmnodedir()
def _create_cmd(self, args: str, shell: bool = False) -> str:
"""
Create command used to run commands within the context of a node.
:param args: command arguments
:param shell: True to run shell like, False otherwise
:return: node command
"""
if shell:
args = f'{BASH} -c "{args}"'
return f"{VCMD} -c {self.ctrlchnlname} -- {args}"
def cmd(self, args: str, wait: bool = True, shell: bool = False) -> str: def cmd(self, args: str, wait: bool = True, shell: bool = False) -> str:
""" """
Runs a command that is used to configure and setup the network within a Runs a command that is used to configure and setup the network within a
@ -639,10 +627,10 @@ class CoreNode(CoreNodeBase):
:return: combined stdout and stderr :return: combined stdout and stderr
:raises CoreCommandError: when a non-zero exit status occurs :raises CoreCommandError: when a non-zero exit status occurs
""" """
args = self._create_cmd(args, shell)
if self.server is None: if self.server is None:
return self.client.check_cmd(args, wait=wait, shell=shell) return utils.cmd(args, wait=wait, shell=shell)
else: else:
args = self.client.create_cmd(args, shell)
return self.server.remote_cmd(args, wait=wait) return self.server.remote_cmd(args, wait=wait)
def path_exists(self, path: str) -> bool: def path_exists(self, path: str) -> bool:
@ -665,7 +653,7 @@ class CoreNode(CoreNodeBase):
:param sh: shell to execute command in :param sh: shell to execute command in
:return: str :return: str
""" """
terminal = self.client.create_cmd(sh) terminal = self._create_cmd(sh)
if self.server is None: if self.server is None:
return terminal return terminal
else: else:
@ -847,28 +835,9 @@ class CoreNode(CoreNodeBase):
self.ifup(iface_id) self.ifup(iface_id)
return self.get_iface(iface_id) return self.get_iface(iface_id)
def addfile(self, src_path: Path, file_path: Path) -> None:
"""
Add a file.
:param src_path: source file path
:param file_path: file name to add
:return: nothing
:raises CoreCommandError: when a non-zero exit status occurs
"""
logger.info("adding file from %s to %s", src_path, file_path)
directory = file_path.parent
if self.server is None:
self.client.check_cmd(f"mkdir -p {directory}")
self.client.check_cmd(f"mv {src_path} {file_path}")
self.client.check_cmd("sync")
else:
self.host_cmd(f"mkdir -p {directory}")
self.server.remote_put(src_path, file_path)
def _find_parent_path(self, path: Path) -> Optional[Path]: def _find_parent_path(self, path: Path) -> Optional[Path]:
""" """
Check if there is an existing mounted parent directory created for this node. Check if there is a mounted parent directory created for this node.
:param path: existing parent path to use :param path: existing parent path to use
:return: exist parent path if exists, None otherwise :return: exist parent path if exists, None otherwise
@ -1000,7 +969,15 @@ class CoreNetworkBase(NodeBase):
""" """
raise NotImplementedError raise NotImplementedError
@abc.abstractmethod
def custom_iface(self, node: CoreNode, iface_data: InterfaceData) -> CoreInterface: def custom_iface(self, node: CoreNode, iface_data: InterfaceData) -> CoreInterface:
"""
Defines custom logic for creating an interface, if required.
:param node: node to create interface for
:param iface_data: data for creating interface
:return: created interface
"""
raise NotImplementedError raise NotImplementedError
def get_linked_iface(self, net: "CoreNetworkBase") -> Optional[CoreInterface]: def get_linked_iface(self, net: "CoreNetworkBase") -> Optional[CoreInterface]:

View file

@ -1,70 +0,0 @@
"""
client.py: implementation of the VnodeClient class for issuing commands
over a control channel to the vnoded process running in a network namespace.
The control channel can be accessed via calls using the vcmd shell.
"""
from pathlib import Path
from core import utils
from core.executables import BASH, VCMD
class VnodeClient:
"""
Provides client functionality for interacting with a virtual node.
"""
def __init__(self, name: str, ctrlchnlname: Path) -> None:
"""
Create a VnodeClient instance.
:param name: name for client
:param ctrlchnlname: control channel name
"""
self.name: str = name
self.ctrlchnlname: Path = ctrlchnlname
def _verify_connection(self) -> None:
"""
Checks that the vcmd client is properly connected.
:return: nothing
:raises IOError: when not connected
"""
if not self.connected():
raise IOError("vcmd not connected")
def connected(self) -> bool:
"""
Check if node is connected or not.
:return: True if connected, False otherwise
"""
return True
def close(self) -> None:
"""
Close the client connection.
:return: nothing
"""
pass
def create_cmd(self, args: str, shell: bool = False) -> str:
if shell:
args = f'{BASH} -c "{args}"'
return f"{VCMD} -c {self.ctrlchnlname} -- {args}"
def check_cmd(self, args: str, wait: bool = True, shell: bool = False) -> str:
"""
Run command and return exit status and combined stdout and stderr.
:param args: command to run
:param wait: True to wait for command status, False otherwise
:param shell: True to use shell, False otherwise
:return: combined stdout and stderr
:raises core.CoreCommandError: when there is a non-zero exit status
"""
self._verify_connection()
args = self.create_cmd(args, shell)
return utils.cmd(args, wait=wait, shell=shell)

View file

@ -22,7 +22,7 @@ from core.emulator.enumerations import (
) )
from core.errors import CoreCommandError, CoreError from core.errors import CoreCommandError, CoreError
from core.executables import NFTABLES from core.executables import NFTABLES
from core.nodes.base import CoreNetworkBase from core.nodes.base import CoreNetworkBase, CoreNode
from core.nodes.interface import CoreInterface, GreTap, Veth from core.nodes.interface import CoreInterface, GreTap, Veth
from core.nodes.netclient import get_net_client from core.nodes.netclient import get_net_client
@ -436,6 +436,9 @@ class CoreNetwork(CoreNetworkBase):
for ip in ips: for ip in ips:
self.net_client.create_address(self.brname, ip) self.net_client.create_address(self.brname, ip)
def custom_iface(self, node: CoreNode, iface_data: InterfaceData) -> CoreInterface:
raise CoreError(f"{type(self).__name__} does not support, custom interfaces")
class GreTapBridge(CoreNetwork): class GreTapBridge(CoreNetwork):
""" """

View file

@ -187,21 +187,17 @@ class PhysicalNode(CoreNodeBase):
except CoreCommandError: except CoreCommandError:
logger.exception("unmounting failed for %s", target_path) logger.exception("unmounting failed for %s", target_path)
def nodefile(self, file_path: Path, contents: str, mode: int = 0o644) -> None:
host_path = self.host_path(file_path)
directory = host_path.parent
if not directory.is_dir():
directory.mkdir(parents=True, mode=0o755)
with host_path.open("w") as f:
f.write(contents)
host_path.chmod(mode)
logger.info("created nodefile: '%s'; mode: 0%o", host_path, mode)
def cmd(self, args: str, wait: bool = True, shell: bool = False) -> str: def cmd(self, args: str, wait: bool = True, shell: bool = False) -> str:
return self.host_cmd(args, wait=wait) return self.host_cmd(args, wait=wait)
def addfile(self, src_path: str, file_path: str) -> None: def create_dir(self, dir_path: Path) -> None:
raise CoreError("physical node does not support addfile") raise CoreError("physical node does not support creating directories")
def create_file(self, file_path: Path, contents: str, mode: int = 0o644) -> None:
raise CoreError("physical node does not support creating files")
def copy_file(self, src_path: Path, dst_path: Path, mode: int = None) -> None:
raise CoreError("physical node does not support copying files")
class Rj45Node(CoreNodeBase): class Rj45Node(CoreNodeBase):
@ -430,12 +426,6 @@ class Rj45Node(CoreNodeBase):
def termcmdstring(self, sh: str) -> str: def termcmdstring(self, sh: str) -> str:
raise CoreError("rj45 does not support terminal commands") raise CoreError("rj45 does not support terminal commands")
def addfile(self, src_path: str, file_path: str) -> None:
raise CoreError("rj45 does not support addfile")
def nodefile(self, file_path: str, contents: str, mode: int = 0o644) -> None:
raise CoreError("rj45 does not support nodefile")
def cmd(self, args: str, wait: bool = True, shell: bool = False) -> str: def cmd(self, args: str, wait: bool = True, shell: bool = False) -> str:
raise CoreError("rj45 does not support cmds") raise CoreError("rj45 does not support cmds")

View file

@ -1,20 +0,0 @@
"""
Services
Services available to nodes can be put in this directory. Everything listed in
__all__ is automatically loaded by the main core module.
"""
from pathlib import Path
from core.services.coreservices import ServiceManager
_PATH: Path = Path(__file__).resolve().parent
def load():
"""
Loads all services from the modules that reside under core.services.
:return: list of services that failed to load
"""
return ServiceManager.add_services(_PATH)

View file

@ -9,6 +9,7 @@ services.
import enum import enum
import logging import logging
import pkgutil
import time import time
from pathlib import Path from pathlib import Path
from typing import ( from typing import (
@ -23,6 +24,7 @@ from typing import (
Union, Union,
) )
from core import services as core_services
from core import utils from core import utils
from core.emulator.data import FileData from core.emulator.data import FileData
from core.emulator.enumerations import ExceptionLevels, MessageFlags, RegisterTlvs from core.emulator.enumerations import ExceptionLevels, MessageFlags, RegisterTlvs
@ -181,6 +183,8 @@ class ServiceShim:
if value: if value:
if key == "startidx": if key == "startidx":
value = int(value) value = int(value)
elif key == "starttime":
value = float(value)
elif key == "meta": elif key == "meta":
value = str(value) value = str(value)
else: else:
@ -231,25 +235,25 @@ class ServiceManager:
""" """
name = service.name name = service.name
logger.debug("loading service: class(%s) name(%s)", service.__name__, name) logger.debug("loading service: class(%s) name(%s)", service.__name__, name)
# avoid services with no name
if name is None:
logger.debug("not loading class(%s) with no name", service.__name__)
return
# avoid duplicate services # avoid duplicate services
if name in cls.services: if name in cls.services:
raise ValueError("duplicate service being added: %s" % name) raise ValueError(f"duplicate service being added: {name}")
# validate dependent executables are present # validate dependent executables are present
for executable in service.executables: for executable in service.executables:
try: try:
utils.which(executable, required=True) utils.which(executable, required=True)
except CoreError as e: except CoreError as e:
raise CoreError(f"service({name}): {e}") raise CoreError(f"service({name}): {e}")
# validate service on load succeeds # validate service on load succeeds
try: try:
service.on_load() service.on_load()
except Exception as e: except Exception as e:
logger.exception("error during service(%s) on load", service.name) logger.exception("error during service(%s) on load", service.name)
raise ValueError(e) raise ValueError(e)
# make service available # make service available
cls.services[name] = service cls.services[name] = service
@ -286,6 +290,21 @@ class ServiceManager:
logger.debug("not loading service(%s): %s", service.name, e) logger.debug("not loading service(%s): %s", service.name, e)
return service_errors return service_errors
@classmethod
def load_locals(cls) -> List[str]:
errors = []
for module_info in pkgutil.walk_packages(
core_services.__path__, f"{core_services.__name__}."
):
services = utils.load_module(module_info.name, CoreService)
for service in services:
try:
cls.add(service)
except CoreError as e:
errors.append(service.name)
logger.debug("not loading service(%s): %s", service.name, e)
return errors
class CoreServices: class CoreServices:
""" """

View file

@ -227,6 +227,7 @@ def cmd(
execute is not found execute is not found
""" """
logger.debug("command cwd(%s) wait(%s): %s", cwd, wait, args) logger.debug("command cwd(%s) wait(%s): %s", cwd, wait, args)
input_args = args
if shell is False: if shell is False:
args = shlex.split(args) args = shlex.split(args)
try: try:
@ -238,13 +239,13 @@ def cmd(
stderr = stderr.decode("utf-8").strip() stderr = stderr.decode("utf-8").strip()
status = p.wait() status = p.wait()
if status != 0: if status != 0:
raise CoreCommandError(status, args, stdout, stderr) raise CoreCommandError(status, input_args, stdout, stderr)
return stdout return stdout
else: else:
return "" return ""
except OSError as e: except OSError as e:
logger.error("cmd error: %s", e.strerror) logger.error("cmd error: %s", e.strerror)
raise CoreCommandError(1, args, "", e.strerror) raise CoreCommandError(1, input_args, "", e.strerror)
def file_munge(pathname: str, header: str, text: str) -> None: def file_munge(pathname: str, header: str, text: str) -> None:

View file

@ -1,6 +1,6 @@
[tool.poetry] [tool.poetry]
name = "core" name = "core"
version = "8.1.0" version = "8.2.0"
description = "CORE Common Open Research Emulator" description = "CORE Common Open Research Emulator"
authors = ["Boeing Research and Technology"] authors = ["Boeing Research and Technology"]
license = "BSD-2-Clause" license = "BSD-2-Clause"

View file

@ -1,22 +1,24 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
import json
import sys import sys
from argparse import ( from argparse import (
ArgumentDefaultsHelpFormatter, ArgumentDefaultsHelpFormatter,
ArgumentParser, ArgumentParser,
ArgumentTypeError, ArgumentTypeError,
Namespace, Namespace,
_SubParsersAction,
) )
from functools import wraps from functools import wraps
from pathlib import Path from pathlib import Path
from typing import Optional, Tuple from typing import Any, Dict, Optional, Tuple
import grpc import grpc
import netaddr import netaddr
from google.protobuf.json_format import MessageToDict
from netaddr import EUI, AddrFormatError, IPNetwork from netaddr import EUI, AddrFormatError, IPNetwork
from core.api.grpc.client import CoreGrpcClient from core.api.grpc.client import CoreGrpcClient
from core.api.grpc.wrappers import ( from core.api.grpc.wrappers import (
ConfigOption,
Geo, Geo,
Interface, Interface,
Link, Link,
@ -29,6 +31,15 @@ from core.api.grpc.wrappers import (
NODE_TYPES = [x for x in NodeType if x != NodeType.PEER_TO_PEER] NODE_TYPES = [x for x in NodeType if x != NodeType.PEER_TO_PEER]
def protobuf_to_json(message: Any) -> Dict[str, Any]:
return MessageToDict(message, including_default_value_fields=True, preserving_proto_field_name=True)
def print_json(data: Any) -> None:
data = json.dumps(data, indent=2)
print(data)
def coreclient(func): def coreclient(func):
@wraps(func) @wraps(func)
def wrapper(*args, **kwargs): def wrapper(*args, **kwargs):
@ -94,11 +105,11 @@ def geo_type(value: str) -> Tuple[float, float, float]:
return lon, lat, alt return lon, lat, alt
def file_type(value: str) -> str: def file_type(value: str) -> Path:
path = Path(value) path = Path(value)
if not path.is_file(): if not path.is_file():
raise ArgumentTypeError(f"invalid file: {value}") raise ArgumentTypeError(f"invalid file: {value}")
return str(path.absolute()) return path
def get_current_session(core: CoreGrpcClient, session_id: Optional[int]) -> int: def get_current_session(core: CoreGrpcClient, session_id: Optional[int]) -> int:
@ -140,6 +151,9 @@ def print_iface(iface: Interface) -> None:
def get_wlan_config(core: CoreGrpcClient, args: Namespace) -> None: def get_wlan_config(core: CoreGrpcClient, args: Namespace) -> None:
session_id = get_current_session(core, args.session) session_id = get_current_session(core, args.session)
config = core.get_wlan_config(session_id, args.node) config = core.get_wlan_config(session_id, args.node)
if args.json:
print_json(ConfigOption.to_dict(config))
else:
size = 0 size = 0
for option in config.values(): for option in config.values():
size = max(size, len(option.name)) size = max(size, len(option.name))
@ -163,18 +177,28 @@ def set_wlan_config(core: CoreGrpcClient, args: Namespace) -> None:
if args.range: if args.range:
config["range"] = str(args.range) config["range"] = str(args.range)
result = core.set_wlan_config(session_id, args.node, config) result = core.set_wlan_config(session_id, args.node, config)
if args.json:
print_json(dict(result=result))
else:
print(f"set wlan config: {result}") print(f"set wlan config: {result}")
@coreclient @coreclient
def open_xml(core: CoreGrpcClient, args: Namespace) -> None: def open_xml(core: CoreGrpcClient, args: Namespace) -> None:
result, session_id = core.open_xml(args.file, args.start) result, session_id = core.open_xml(args.file, args.start)
if args.json:
print_json(dict(result=result, session_id=session_id))
else:
print(f"opened xml: {result},{session_id}") print(f"opened xml: {result},{session_id}")
@coreclient @coreclient
def query_sessions(core: CoreGrpcClient, args: Namespace) -> None: def query_sessions(core: CoreGrpcClient, args: Namespace) -> None:
sessions = core.get_sessions() sessions = core.get_sessions()
if args.json:
sessions = [protobuf_to_json(x.to_proto()) for x in sessions]
print_json(sessions)
else:
print("Session ID | Session State | Nodes") print("Session ID | Session State | Nodes")
for session in sessions: for session in sessions:
print(f"{session.id:<10} | {session.state.name:<13} | {session.nodes}") print(f"{session.id:<10} | {session.state.name:<13} | {session.nodes}")
@ -183,10 +207,16 @@ def query_sessions(core: CoreGrpcClient, args: Namespace) -> None:
@coreclient @coreclient
def query_session(core: CoreGrpcClient, args: Namespace) -> None: def query_session(core: CoreGrpcClient, args: Namespace) -> None:
session = core.get_session(args.id) session = core.get_session(args.id)
if args.json:
session = protobuf_to_json(session.to_proto())
print_json(session)
else:
print("Nodes") print("Nodes")
print("Node ID | Node Name | Node Type") print("ID | Name | Type | XY | Geo")
for node in session.nodes.values(): for node in session.nodes.values():
print(f"{node.id:<7} | {node.name:<9} | {node.type.name}") xy_pos = f"{int(node.position.x)},{int(node.position.y)}"
geo_pos = f"{node.geo.lon:.7f},{node.geo.lat:.7f},{node.geo.alt:f}"
print(f"{node.id:<7} | {node.name[:7]:<7} | {node.type.name[:7]:<7} | {xy_pos:<9} | {geo_pos}")
print("\nLinks") print("\nLinks")
for link in session.links: for link in session.links:
n1 = session.nodes[link.node1_id].name n1 = session.nodes[link.node1_id].name
@ -210,12 +240,15 @@ def query_session(core: CoreGrpcClient, args: Namespace) -> None:
def query_node(core: CoreGrpcClient, args: Namespace) -> None: def query_node(core: CoreGrpcClient, args: Namespace) -> None:
session = core.get_session(args.id) session = core.get_session(args.id)
node, ifaces, _ = core.get_node(args.id, args.node) node, ifaces, _ = core.get_node(args.id, args.node)
print("ID | Name | Type | XY") if args.json:
node = protobuf_to_json(node.to_proto())
ifaces = [protobuf_to_json(x.to_proto()) for x in ifaces]
print_json(dict(node=node, ifaces=ifaces))
else:
print("ID | Name | Type | XY | Geo")
xy_pos = f"{int(node.position.x)},{int(node.position.y)}" xy_pos = f"{int(node.position.x)},{int(node.position.y)}"
print(f"{node.id:<4} | {node.name[:7]:<7} | {node.type.name[:7]:<7} | {xy_pos}") geo_pos = f"{node.geo.lon:.7f},{node.geo.lat:.7f},{node.geo.alt:f}"
if node.geo: print(f"{node.id:<7} | {node.name[:7]:<7} | {node.type.name[:7]:<7} | {xy_pos:<9} | {geo_pos}")
print("Geo")
print(f"{node.geo.lon:.7f},{node.geo.lat:.7f},{node.geo.alt:f}")
if ifaces: if ifaces:
print("Interfaces") print("Interfaces")
print("Connected To | ", end="") print("Connected To | ", end="")
@ -236,6 +269,9 @@ def query_node(core: CoreGrpcClient, args: Namespace) -> None:
@coreclient @coreclient
def delete_session(core: CoreGrpcClient, args: Namespace) -> None: def delete_session(core: CoreGrpcClient, args: Namespace) -> None:
result = core.delete_session(args.id) result = core.delete_session(args.id)
if args.json:
print_json(dict(result=result))
else:
print(f"delete session({args.id}): {result}") print(f"delete session({args.id}): {result}")
@ -263,6 +299,9 @@ def add_node(core: CoreGrpcClient, args: Namespace) -> None:
geo=geo, geo=geo,
) )
node_id = core.add_node(session_id, node) node_id = core.add_node(session_id, node)
if args.json:
print_json(dict(node_id=node_id))
else:
print(f"created node: {node_id}") print(f"created node: {node_id}")
@ -270,6 +309,9 @@ def add_node(core: CoreGrpcClient, args: Namespace) -> None:
def edit_node(core: CoreGrpcClient, args: Namespace) -> None: def edit_node(core: CoreGrpcClient, args: Namespace) -> None:
session_id = get_current_session(core, args.session) session_id = get_current_session(core, args.session)
result = core.edit_node(session_id, args.id, args.icon) result = core.edit_node(session_id, args.id, args.icon)
if args.json:
print_json(dict(result=result))
else:
print(f"edit node: {result}") print(f"edit node: {result}")
@ -285,6 +327,9 @@ def move_node(core: CoreGrpcClient, args: Namespace) -> None:
lon, lat, alt = args.geo lon, lat, alt = args.geo
geo = Geo(lon=lon, lat=lat, alt=alt) geo = Geo(lon=lon, lat=lat, alt=alt)
result = core.move_node(session_id, args.id, pos, geo) result = core.move_node(session_id, args.id, pos, geo)
if args.json:
print_json(dict(result=result))
else:
print(f"move node: {result}") print(f"move node: {result}")
@ -292,6 +337,9 @@ def move_node(core: CoreGrpcClient, args: Namespace) -> None:
def delete_node(core: CoreGrpcClient, args: Namespace) -> None: def delete_node(core: CoreGrpcClient, args: Namespace) -> None:
session_id = get_current_session(core, args.session) session_id = get_current_session(core, args.session)
result = core.delete_node(session_id, args.id) result = core.delete_node(session_id, args.id)
if args.json:
print_json(dict(result=result))
else:
print(f"deleted node: {result}") print(f"deleted node: {result}")
@ -313,7 +361,12 @@ def add_link(core: CoreGrpcClient, args: Namespace) -> None:
unidirectional=args.uni, unidirectional=args.uni,
) )
link = Link(args.node1, args.node2, iface1=iface1, iface2=iface2, options=options) link = Link(args.node1, args.node2, iface1=iface1, iface2=iface2, options=options)
result, _, _ = core.add_link(session_id, link) result, iface1, iface2 = core.add_link(session_id, link)
if args.json:
iface1 = protobuf_to_json(iface1.to_proto())
iface2 = protobuf_to_json(iface2.to_proto())
print_json(dict(result=result, iface1=iface1, iface2=iface2))
else:
print(f"add link: {result}") print(f"add link: {result}")
@ -332,6 +385,9 @@ def edit_link(core: CoreGrpcClient, args: Namespace) -> None:
iface2 = Interface(args.iface2) iface2 = Interface(args.iface2)
link = Link(args.node1, args.node2, iface1=iface1, iface2=iface2, options=options) link = Link(args.node1, args.node2, iface1=iface1, iface2=iface2, options=options)
result = core.edit_link(session_id, link) result = core.edit_link(session_id, link)
if args.json:
print_json(dict(result=result))
else:
print(f"edit link: {result}") print(f"edit link: {result}")
@ -342,10 +398,13 @@ def delete_link(core: CoreGrpcClient, args: Namespace) -> None:
iface2 = Interface(args.iface2) iface2 = Interface(args.iface2)
link = Link(args.node1, args.node2, iface1=iface1, iface2=iface2) link = Link(args.node1, args.node2, iface1=iface1, iface2=iface2)
result = core.delete_link(session_id, link) result = core.delete_link(session_id, link)
if args.json:
print_json(dict(result=result))
else:
print(f"delete link: {result}") print(f"delete link: {result}")
def setup_sessions_parser(parent: _SubParsersAction) -> None: def setup_sessions_parser(parent) -> None:
parser = parent.add_parser("session", help="session interactions") parser = parent.add_parser("session", help="session interactions")
parser.formatter_class = ArgumentDefaultsHelpFormatter parser.formatter_class = ArgumentDefaultsHelpFormatter
parser.add_argument("-i", "--id", type=int, help="session id to use", required=True) parser.add_argument("-i", "--id", type=int, help="session id to use", required=True)
@ -358,7 +417,7 @@ def setup_sessions_parser(parent: _SubParsersAction) -> None:
delete_parser.set_defaults(func=delete_session) delete_parser.set_defaults(func=delete_session)
def setup_node_parser(parent: _SubParsersAction) -> None: def setup_node_parser(parent) -> None:
parser = parent.add_parser("node", help="node interactions") parser = parent.add_parser("node", help="node interactions")
parser.formatter_class = ArgumentDefaultsHelpFormatter parser.formatter_class = ArgumentDefaultsHelpFormatter
parser.add_argument("-s", "--session", type=int, help="session to interact with") parser.add_argument("-s", "--session", type=int, help="session to interact with")
@ -402,7 +461,7 @@ def setup_node_parser(parent: _SubParsersAction) -> None:
delete_parser.set_defaults(func=delete_node) delete_parser.set_defaults(func=delete_node)
def setup_link_parser(parent: _SubParsersAction) -> None: def setup_link_parser(parent) -> None:
parser = parent.add_parser("link", help="link interactions") parser = parent.add_parser("link", help="link interactions")
parser.formatter_class = ArgumentDefaultsHelpFormatter parser.formatter_class = ArgumentDefaultsHelpFormatter
parser.add_argument("-s", "--session", type=int, help="session to interact with") parser.add_argument("-s", "--session", type=int, help="session to interact with")
@ -455,7 +514,7 @@ def setup_link_parser(parent: _SubParsersAction) -> None:
delete_parser.set_defaults(func=delete_link) delete_parser.set_defaults(func=delete_link)
def setup_query_parser(parent: _SubParsersAction) -> None: def setup_query_parser(parent) -> None:
parser = parent.add_parser("query", help="query interactions") parser = parent.add_parser("query", help="query interactions")
subparsers = parser.add_subparsers(help="query commands") subparsers = parser.add_subparsers(help="query commands")
subparsers.required = True subparsers.required = True
@ -477,7 +536,7 @@ def setup_query_parser(parent: _SubParsersAction) -> None:
node_parser.set_defaults(func=query_node) node_parser.set_defaults(func=query_node)
def setup_xml_parser(parent: _SubParsersAction) -> None: def setup_xml_parser(parent) -> None:
parser = parent.add_parser("xml", help="open session xml") parser = parent.add_parser("xml", help="open session xml")
parser.formatter_class = ArgumentDefaultsHelpFormatter parser.formatter_class = ArgumentDefaultsHelpFormatter
parser.add_argument("-f", "--file", type=file_type, help="xml file to open", required=True) parser.add_argument("-f", "--file", type=file_type, help="xml file to open", required=True)
@ -485,7 +544,7 @@ def setup_xml_parser(parent: _SubParsersAction) -> None:
parser.set_defaults(func=open_xml) parser.set_defaults(func=open_xml)
def setup_wlan_parser(parent: _SubParsersAction) -> None: def setup_wlan_parser(parent) -> None:
parser = parent.add_parser("wlan", help="wlan specific interactions") parser = parent.add_parser("wlan", help="wlan specific interactions")
parser.formatter_class = ArgumentDefaultsHelpFormatter parser.formatter_class = ArgumentDefaultsHelpFormatter
parser.add_argument("-s", "--session", type=int, help="session to interact with") parser.add_argument("-s", "--session", type=int, help="session to interact with")
@ -511,6 +570,9 @@ def setup_wlan_parser(parent: _SubParsersAction) -> None:
def main() -> None: def main() -> None:
parser = ArgumentParser(formatter_class=ArgumentDefaultsHelpFormatter) parser = ArgumentParser(formatter_class=ArgumentDefaultsHelpFormatter)
parser.add_argument(
"-js", "--json", action="store_true", help="print responses to terminal as json"
)
subparsers = parser.add_subparsers(help="supported commands") subparsers = parser.add_subparsers(help="supported commands")
subparsers.required = True subparsers.required = True
subparsers.dest = "command" subparsers.dest = "command"

View file

@ -63,39 +63,6 @@ class TestCore:
status = ping(node1, node2, ip_prefixes) status = ping(node1, node2, ip_prefixes)
assert not status assert not status
def test_vnode_client(self, request, session: Session, ip_prefixes: IpPrefixes):
"""
Test vnode client methods.
:param request: pytest request
:param session: session for test
:param ip_prefixes: generates ip addresses for nodes
"""
# create ptp
ptp_node = session.add_node(PtpNet)
# create nodes
node1 = session.add_node(CoreNode)
node2 = session.add_node(CoreNode)
# link nodes to ptp net
for node in [node1, node2]:
iface_data = ip_prefixes.create_iface(node)
session.add_link(node.id, ptp_node.id, iface1_data=iface_data)
# get node client for testing
client = node1.client
# instantiate session
session.instantiate()
# check we are connected
assert client.connected()
# validate command
if not request.config.getoption("mock"):
assert client.check_cmd("echo hello") == "hello"
def test_iface(self, session: Session, ip_prefixes: IpPrefixes): def test_iface(self, session: Session, ip_prefixes: IpPrefixes):
""" """
Test interface methods. Test interface methods.

View file

@ -28,23 +28,23 @@ will allow quickly dragging and dropping that node type during creation.
## Available Services ## Available Services
| Service Group | Services | | Service Group | Services |
|---|---| |----------------------------------|-----------------------------------------------------------------------|
|[BIRD](services/bird.md)|BGP, OSPF, RADV, RIP, Static| | [BIRD](services/bird.md) | BGP, OSPF, RADV, RIP, Static |
|[EMANE](services/emane.md)|Transport Service| | [EMANE](services/emane.md) | Transport Service |
|[FRR](services/frr.md)|BABEL, BGP, OSPFv2, OSPFv3, PIMD, RIP, RIPNG, Zebra| | [FRR](services/frr.md) | BABEL, BGP, OSPFv2, OSPFv3, PIMD, RIP, RIPNG, Zebra |
|[NRL](services/nrl.md)|arouted, MGEN Sink, MGEN Actor, NHDP, OLSR, OLSRORG, OLSRv2, SMF| | [NRL](services/nrl.md) | arouted, MGEN Sink, MGEN Actor, NHDP, OLSR, OLSRORG, OLSRv2, SMF |
|[Quagga](services/quagga.md)|BABEL, BGP, OSPFv2, OSPFv3, OSPFv3 MDR, RIP, RIPNG, XPIMD, Zebra| | [Quagga](services/quagga.md) | BABEL, BGP, OSPFv2, OSPFv3, OSPFv3 MDR, RIP, RIPNG, XPIMD, Zebra |
|[SDN](services/sdn.md)|OVS, RYU| | [SDN](services/sdn.md) | OVS, RYU |
|[Security](services/security.md)|Firewall, IPsec, NAT, VPN Client, VPN Server| | [Security](services/security.md) | Firewall, IPsec, NAT, VPN Client, VPN Server |
|[Utility](services/utility.md)|ATD, Routing Utils, DHCP, FTP, IP Forward, PCAP, RADVD, SSF, UCARP| | [Utility](services/utility.md) | ATD, Routing Utils, DHCP, FTP, IP Forward, PCAP, RADVD, SSF, UCARP |
|[XORP](services/xorp.md)|BGP, OLSR, OSPFv2, OSPFv3, PIMSM4, PIMSM6, RIP, RIPNG, Router Manager| | [XORP](services/xorp.md) | BGP, OLSR, OSPFv2, OSPFv3, PIMSM4, PIMSM6, RIP, RIPNG, Router Manager |
## Node Types and Default Services ## Node Types and Default Services
Here are the default node types and their services: Here are the default node types and their services:
| Node Type | Services | | Node Type | Services |
|---|---| |-----------|--------------------------------------------------------------------------------------------------------------------------------------------|
| *router* | zebra, OSFPv2, OSPFv3, and IPForward services for IGP link-state routing. | | *router* | zebra, OSFPv2, OSPFv3, and IPForward services for IGP link-state routing. |
| *PC* | DefaultRoute service for having a default route when connected directly to a router. | | *PC* | DefaultRoute service for having a default route when connected directly to a router. |
| *mdr* | zebra, OSPFv3MDR, and IPForward services for wireless-optimized MANET Designated Router routing. | | *mdr* | zebra, OSPFv3MDR, and IPForward services for wireless-optimized MANET Designated Router routing. |

View file

@ -10,12 +10,12 @@ historical reasons. Current development focuses on the Python modules and
daemon. Here is a brief description of the source directories. daemon. Here is a brief description of the source directories.
| Directory | Description | | Directory | Description |
|---|---| |-----------|--------------------------------------------------------------------------------------|
|daemon|Python CORE daemon code that handles receiving API calls and creating containers| | daemon | Python CORE daemon/gui code that handles receiving API calls and creating containers |
|docs|Markdown Documentation currently hosted on GitHub| | docs | Markdown Documentation currently hosted on GitHub |
|gui|Tcl/Tk GUI| | gui | Tcl/Tk GUI |
|man|Template files for creating man pages for various CORE command line utilities| | man | Template files for creating man pages for various CORE command line utilities |
|netns|C program for creating CORE containers| | netns | C program for creating CORE containers |
## Getting started ## Getting started

View file

@ -33,7 +33,7 @@ user space socket to the TAP device for sending and receiving data from CORE.
An EMANE instance sends and receives OTA (Over-The-Air) traffic to and from An EMANE instance sends and receives OTA (Over-The-Air) traffic to and from
other EMANE instances via a control port (e.g. *ctrl0*, *ctrl1*). It also other EMANE instances via a control port (e.g. *ctrl0*, *ctrl1*). It also
sends and receives Events to and from the Event Service using the same or a sends and receives Events to and from the Event Service using the same or a
different control port. EMANE models are configured through CORE's WLAN different control port. EMANE models are configured through the GUI's
configuration dialog. A corresponding EmaneModel Python class is sub-classed configuration dialog. A corresponding EmaneModel Python class is sub-classed
for each supported EMANE model, to provide configuration items and their for each supported EMANE model, to provide configuration items and their
mapping to XML files. This way new models can be easily supported. When mapping to XML files. This way new models can be easily supported. When
@ -62,13 +62,13 @@ Every topic below assumes CORE, EMANE, and OSPF MDR have been installed.
> **WARNING:** demo files will be found within the new `core-pygui` > **WARNING:** demo files will be found within the new `core-pygui`
|Topic|Model|Description| | Topic | Model | Description |
|---|---|---| |--------------------------------------|---------|-----------------------------------------------------------|
|[XML Files](emane/files.md)|RF Pipe|Overview of generated XML files used to drive EMANE| | [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| | [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| | [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| | [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| | [Antenna Profiles](emane/antenna.md) | RF Pipe | Overview of using antenna profiles in EMANE |
## EMANE Configuration ## EMANE Configuration
@ -201,27 +201,21 @@ EMANE. Using the primary control channel prevents your emulation session from
sending multicast traffic on your local network and interfering with other sending multicast traffic on your local network and interfering with other
EMANE users. EMANE users.
EMANE is configured through a WLAN node, because it is all about emulating EMANE is configured through an EMANE node. Once a node is linked to an EMANE
wireless radio networks. Once a node is linked to a WLAN cloud configured cloud, the radio interface on that node may also be configured
with an EMANE model, the radio interface on that node may also be configured
separately (apart from the cloud.) separately (apart from the cloud.)
Double-click on a WLAN node to invoke the WLAN configuration dialog. Click Right click on an EMANE node and select EMANE Config to open the configuration dialog.
the *EMANE* tab; when EMANE has been properly installed, EMANE wireless modules The EMANE models should be listed here for selection. (You may need to restart the
should be listed in the *EMANE Models* list. (You may need to restart the
CORE daemon if it was running prior to installing the EMANE Python bindings.) CORE daemon if it was running prior to installing the EMANE Python bindings.)
Click on a model name to enable it.
When an EMANE model is selected in the *EMANE Models* list, clicking on the When an EMANE model is selected, you can click on the models option button
*model options* button causes the GUI to query the CORE daemon for causing the GUI to query the CORE daemon for configuration items.
configuration items. Each model will have different parameters, refer to the Each model will have different parameters, refer to the
EMANE documentation for an explanation of each item. The defaults values are EMANE documentation for an explanation of each item. The defaults values are
presented in the dialog. Clicking *Apply* and *Apply* again will store the presented in the dialog. Clicking *Apply* and *Apply* again will store the
EMANE model selections. EMANE model selections.
The *EMANE options* button allows specifying some global parameters for
EMANE, some of which are necessary for distributed operation.
The RF-PIPE and IEEE 802.11abg models use a Universal PHY that supports The RF-PIPE and IEEE 802.11abg models use a Universal PHY that supports
geographic location information for determining pathloss between nodes. A geographic location information for determining pathloss between nodes. A
default latitude and longitude location is provided by CORE and this default latitude and longitude location is provided by CORE and this
@ -241,21 +235,12 @@ used to achieve geo-location accuracy in this situation.
Clicking the green *Start* button launches the emulation and causes TAP devices Clicking the green *Start* button launches the emulation and causes TAP devices
to be created in the virtual nodes that are linked to the EMANE WLAN. These to be created in the virtual nodes that are linked to the EMANE WLAN. These
devices appear with interface names such as eth0, eth1, etc. The EMANE processes devices appear with interface names such as eth0, eth1, etc. The EMANE processes
should now be running in each namespace. For a four node scenario: should now be running in each namespace.
```shell To view the configuration generated by CORE, look in the */tmp/pycore.nnnnn/* session
ps -aef | grep emane directory to find the generated EMANE xml files. One easy way to view
root 1063 969 0 11:46 ? 00:00:00 emane -d --logl 3 -r -f /tmp/pycore.59992/emane4.log /tmp/pycore.59992/platform4.xml this information is by double-clicking one of the virtual nodes and listing the files
root 1117 959 0 11:46 ? 00:00:00 emane -d --logl 3 -r -f /tmp/pycore.59992/emane2.log /tmp/pycore.59992/platform2.xml in the shell.
root 1179 942 0 11:46 ? 00:00:00 emane -d --logl 3 -r -f /tmp/pycore.59992/emane1.log /tmp/pycore.59992/platform1.xml
root 1239 979 0 11:46 ? 00:00:00 emane -d --logl 3 -r -f /tmp/pycore.59992/emane5.log /tmp/pycore.59992/platform5.xml
```
The example above shows the EMANE processes started by CORE. To view the
configuration generated by CORE, look in the */tmp/pycore.nnnnn/* session
directory for a *platform.xml* file and other XML files. One easy way to view
this information is by double-clicking one of the virtual nodes, and typing
*cd ..* in the shell to go up to the session directory.
![](static/single-pc-emane.png) ![](static/single-pc-emane.png)
@ -278,55 +263,40 @@ within a node.
**IMPORTANT: If an auxiliary control network is used, an interface on the host **IMPORTANT: If an auxiliary control network is used, an interface on the host
has to be assigned to that network.** has to be assigned to that network.**
Each machine that will act as an emulation server needs to have CORE and EMANE Each machine that will act as an emulation server needs to have CORE distributed
installed. and EMANE installed. As well as be setup to work for CORE distributed mode.
The IP addresses of the available servers are configured from the CORE emulation The IP addresses of the available servers are configured from the CORE
servers dialog box (choose *Session* then *Emulation servers...*). This list of servers dialog box. The dialog shows available
servers is stored in a *~/.core/servers.conf* file. The dialog shows available
servers, some or all of which may be assigned to nodes on the canvas. servers, some or all of which may be assigned to nodes on the canvas.
Nodes need to be assigned to emulation servers. Select several nodes, Nodes need to be assigned to servers and can be done so using the node
right-click them, and choose *Assign to* and the name of the desired server. configuration dialog. When a node is not assigned to any emulation server,
When a node is not assigned to any emulation server, it will be emulated it will be emulated locally.
locally. The local machine that the GUI connects with is considered the
"master" machine, which in turn connects to the other emulation server
"slaves". Public key SSH should be configured from the master to the slaves.
Under the *EMANE* tab of the EMANE WLAN, click on the *EMANE options* button. Using the EMANE node configuration dialog. You can change the EMANE model
This brings up the emane configuration dialog. The *enable OTA Manager channel* being used, along with changing any configuration setting from their defaults.
should be set to *on*. The *OTA Manager device* and *Event Service device*
should be set to a control network device. For example, if you have a primary
and auxiliary control network (i.e. controlnet and controlnet1), and you want
the OTA traffic to have its dedicated network, set the OTA Manager device to
*ctrl1* and the Event Service device to *ctrl0*. The EMANE models can be
configured. Click *Apply* to save these settings.
![](static/distributed-emane-configuration.png) ![](static/distributed-emane-configuration.png)
> **NOTE:** Here is a quick checklist for distributed emulation with EMANE. > **NOTE:** Here is a quick checklist for distributed emulation with EMANE.
1. Follow the steps outlined for normal CORE. 1. Follow the steps outlined for normal CORE.
2. Under the *EMANE* tab of the EMANE WLAN, click on *EMANE options*. 2. Assign nodes to desired servers
3. Turn on the *OTA Manager channel* and set the *OTA Manager device*. 3. Synchronize your machine's clocks prior to starting the emulation,
Also set the *Event Service device*.
4. Select groups of nodes, right-click them, and assign them to servers
using the *Assign to* menu.
5. Synchronize your machine's clocks prior to starting the emulation,
using *ntp* or *ptp*. Some EMANE models are sensitive to timing. using *ntp* or *ptp*. Some EMANE models are sensitive to timing.
6. Press the *Start* button to launch the distributed emulation. 4. Press the *Start* button to launch the distributed emulation.
Now when the Start button is used to instantiate the emulation, the local CORE Now when the Start button is used to instantiate the emulation, the local CORE
Python daemon will connect to other emulation servers that have been assigned daemon will connect to other emulation servers that have been assigned
to nodes. Each server will have its own session directory where the to nodes. Each server will have its own session directory where the
*platform.xml* file and other EMANE XML files are generated. The NEM IDs are *platform.xml* file and other EMANE XML files are generated. The NEM IDs are
automatically coordinated across servers so there is no overlap. Each server automatically coordinated across servers so there is no overlap.
also gets its own Platform ID.
An Ethernet device is used for disseminating multicast EMANE events, as An Ethernet device is used for disseminating multicast EMANE events, as
specified in the *configure emane* dialog. EMANE's Event Service can be run specified in the *configure emane* dialog. EMANE's Event Service can be run
with mobility or pathloss scripts as described in :ref:`Single_PC_with_EMANE`. with mobility or pathloss scripts.
If CORE is not subscribed to location events, it will generate them as nodes If CORE is not subscribed to location events, it will generate them as nodes
are moved on the canvas. are moved on the canvas.

View file

@ -94,7 +94,7 @@ item, starting from the top. Most of the tools are grouped into related
sub-menus, which appear when you click on their group icon. sub-menus, which appear when you click on their group icon.
| Icon | Name | Description | | Icon | Name | Description |
|---|---|---| |----------------------------|----------------|----------------------------------------------------------------------------------------|
| ![](static/gui/select.gif) | Selection Tool | Tool for selecting, moving, configuring nodes. | | ![](static/gui/select.gif) | Selection Tool | Tool for selecting, moving, configuring nodes. |
| ![](static/gui/start.gif) | Start Button | Starts Execute mode, instantiates the emulation. | | ![](static/gui/start.gif) | Start Button | Starts Execute mode, instantiates the emulation. |
| ![](static/gui/link.gif) | Link | Allows network links to be drawn between two nodes by clicking and dragging the mouse. | | ![](static/gui/link.gif) | Link | Allows network links to be drawn between two nodes by clicking and dragging the mouse. |
@ -104,7 +104,7 @@ sub-menus, which appear when you click on their group icon.
These nodes will create a new node container and run associated services. These nodes will create a new node container and run associated services.
| Icon | Name | Description | | Icon | Name | Description |
|---|---|---| |-----------------------------------------|---------|------------------------------------------------------------------------------|
| ![](static/gui/router.gif) | Router | Runs Quagga OSPFv2 and OSPFv3 routing to forward packets. | | ![](static/gui/router.gif) | Router | Runs Quagga OSPFv2 and OSPFv3 routing to forward packets. |
| ![](static/gui/host.gif) | Host | Emulated server machine having a default route, runs SSH server. | | ![](static/gui/host.gif) | Host | Emulated server machine having a default route, runs SSH server. |
| ![](static/gui/pc.gif) | PC | Basic emulated machine having a default route, runs no processes by default. | | ![](static/gui/pc.gif) | PC | Basic emulated machine having a default route, runs no processes by default. |
@ -118,7 +118,7 @@ These nodes are mostly used to create a Linux bridge that serves the
purpose described below. purpose described below.
| Icon | Name | Description | | Icon | Name | Description |
|---|---|---| |-------------------------------|--------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| ![](static/gui/hub.gif) | Hub | Ethernet hub forwards incoming packets to every connected node. | | ![](static/gui/hub.gif) | Hub | Ethernet hub forwards incoming packets to every connected node. |
| ![](static/gui/lanswitch.gif) | Switch | Ethernet switch intelligently forwards incoming packets to attached hosts using an Ethernet address hash table. | | ![](static/gui/lanswitch.gif) | Switch | Ethernet switch intelligently forwards incoming packets to attached hosts using an Ethernet address hash table. |
| ![](static/gui/wlan.gif) | Wireless LAN | When routers are connected to this WLAN node, they join a wireless network and an antenna is drawn instead of a connecting line; the WLAN node typically controls connectivity between attached wireless nodes based on the distance between them. | | ![](static/gui/wlan.gif) | Wireless LAN | When routers are connected to this WLAN node, they join a wireless network and an antenna is drawn instead of a connecting line; the WLAN node typically controls connectivity between attached wireless nodes based on the distance between them. |
@ -128,7 +128,7 @@ purpose described below.
### Annotation Tools ### Annotation Tools
| Icon | Name | Description | | Icon | Name | Description |
|---|---|---| |-------------------------------|-----------|---------------------------------------------------------------------|
| ![](static/gui/marker.gif) | Marker | For drawing marks on the canvas. | | ![](static/gui/marker.gif) | Marker | For drawing marks on the canvas. |
| ![](static/gui/oval.gif) | Oval | For drawing circles on the canvas that appear in the background. | | ![](static/gui/oval.gif) | Oval | For drawing circles on the canvas that appear in the background. |
| ![](static/gui/rectangle.gif) | Rectangle | For drawing rectangles on the canvas that appear in the background. | | ![](static/gui/rectangle.gif) | Rectangle | For drawing rectangles on the canvas that appear in the background. |
@ -141,7 +141,7 @@ toolbar on the left of the CORE window is replaced with the Execution toolbar
Below are the items on this toolbar, starting from the top. Below are the items on this toolbar, starting from the top.
| Icon | Name | Description | | Icon | Name | Description |
|---|---|---| |-----------------------------|-----------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| ![](static/gui/select.gif) | Selection Tool | In Execute mode, the Selection Tool can be used for moving nodes around the canvas, and double-clicking on a node will open a shell window for that node; right-clicking on a node invokes a pop-up menu of run-time options for that node. | | ![](static/gui/select.gif) | Selection Tool | In Execute mode, the Selection Tool can be used for moving nodes around the canvas, and double-clicking on a node will open a shell window for that node; right-clicking on a node invokes a pop-up menu of run-time options for that node. |
| ![](static/gui/stop.gif) | Stop Button | Stops Execute mode, terminates the emulation, returns CORE to edit mode. | | ![](static/gui/stop.gif) | Stop Button | Stops Execute mode, terminates the emulation, returns CORE to edit mode. |
| ![](static/gui/observe.gif) | Observer Widgets Tool | Clicking on this magnifying glass icon invokes a menu for easily selecting an Observer Widget. The icon has a darker gray background when an Observer Widget is active, during which time moving the mouse over a node will pop up an information display for that node. | | ![](static/gui/observe.gif) | Observer Widgets Tool | Clicking on this magnifying glass icon invokes a menu for easily selecting an Observer Widget. The icon has a darker gray background when an Observer Widget is active, during which time moving the mouse over a node will pop up an information display for that node. |
@ -161,7 +161,7 @@ The File menu contains options for manipulating the **.imn** Configuration
Files. Generally, these menu items should not be used in Execute mode. Files. Generally, these menu items should not be used in Execute mode.
| Option | Description | | Option | Description |
|---|---| |------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| New | This starts a new file with an empty canvas. | | New | This starts a new file with an empty canvas. |
| Open | Invokes the File Open dialog box for selecting a new **.imn** or XML file to open. You can change the default path used for this dialog in the Preferences Dialog. | | Open | Invokes the File Open dialog box for selecting a new **.imn** or XML file to open. You can change the default path used for this dialog in the Preferences Dialog. |
| Save | Saves the current topology. If you have not yet specified a file name, the Save As dialog box is invoked. | | Save | Saves the current topology. If you have not yet specified a file name, the Save As dialog box is invoked. |
@ -179,10 +179,10 @@ Files. Generally, these menu items should not be used in Execute mode.
### Edit Menu ### Edit Menu
| Option | Description | | Option | Description |
|---|---| |------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| Undo | Attempts to undo the last edit in edit mode. | | Undo | Attempts to undo the last edit in edit mode. |
| Redo | Attempts to redo an edit that has been undone. | | Redo | Attempts to redo an edit that has been undone. |
| Cut, Copy, Paste | Used to cut, copy, and paste a selection. When nodes are pasted, their node numbers are automatically incremented, and existing links are preserved with new IP addresses assigned. Services and their customizations are copied to the new node, but care should be taken as node IP addresses have changed with possibly old addresses remaining in any custom service configurations. Annotations may also be copied and pasted. | Cut, Copy, Paste | Used to cut, copy, and paste a selection. When nodes are pasted, their node numbers are automatically incremented, and existing links are preserved with new IP addresses assigned. Services and their customizations are copied to the new node, but care should be taken as node IP addresses have changed with possibly old addresses remaining in any custom service configurations. Annotations may also be copied and pasted. |
| Select All | Selects all items on the canvas. Selected items can be moved as a group. | | Select All | Selects all items on the canvas. Selected items can be moved as a group. |
| Select Adjacent | Select all nodes that are linked to the already selected node(s). For wireless nodes this simply selects the WLAN node(s) that the wireless node belongs to. You can use this by clicking on a node and pressing CTRL+N to select the adjacent nodes. | | Select Adjacent | Select all nodes that are linked to the already selected node(s). For wireless nodes this simply selects the WLAN node(s) that the wireless node belongs to. You can use this by clicking on a node and pressing CTRL+N to select the adjacent nodes. |
| Find... | Invokes the *Find* dialog box. The Find dialog can be used to search for nodes by name or number. Results are listed in a table that includes the node or link location and details such as IP addresses or link parameters. Clicking on a result will focus the canvas on that node or link, switching canvases if necessary. | | Find... | Invokes the *Find* dialog box. The Find dialog can be used to search for nodes by name or number. Results are listed in a table that includes the node or link location and details such as IP addresses or link parameters. Clicking on a result will focus the canvas on that node or link, switching canvases if necessary. |
@ -195,11 +195,11 @@ The canvas menu provides commands for adding, removing, changing, and switching
to different editing canvases. to different editing canvases.
| Option | Description | | Option | Description |
|---|---| |-----------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| New | Creates a new empty canvas at the right of all existing canvases. | | New | Creates a new empty canvas at the right of all existing canvases. |
| Manage... | Invokes the *Manage Canvases* dialog box, where canvases may be renamed and reordered, and you can easily switch to one of the canvases by selecting it. | | Manage... | Invokes the *Manage Canvases* dialog box, where canvases may be renamed and reordered, and you can easily switch to one of the canvases by selecting it. |
| Delete | Deletes the current canvas and all items that it contains. | | Delete | Deletes the current canvas and all items that it contains. |
| Size/scale... | Invokes a Canvas Size and Scale dialog that allows configuring the canvas size, scale, and geographic reference point. The size controls allow changing the width and height of the current canvas, in pixels or meters. The scale allows specifying how many meters are equivalent to 100 pixels. The reference point controls specify the latitude, longitude, and altitude reference point used to convert between geographic and Cartesian coordinate systems. By clicking the *Save as default* option, all new canvases will be created with these properties. The default canvas size can also be changed in the Preferences dialog box. | Size/scale... | Invokes a Canvas Size and Scale dialog that allows configuring the canvas size, scale, and geographic reference point. The size controls allow changing the width and height of the current canvas, in pixels or meters. The scale allows specifying how many meters are equivalent to 100 pixels. The reference point controls specify the latitude, longitude, and altitude reference point used to convert between geographic and Cartesian coordinate systems. By clicking the *Save as default* option, all new canvases will be created with these properties. The default canvas size can also be changed in the Preferences dialog box. |
| Wallpaper... | Used for setting the canvas background image. | | Wallpaper... | Used for setting the canvas background image. |
| Previous, Next, First, Last | Used for switching the active canvas to the first, last, or adjacent canvas. | | Previous, Next, First, Last | Used for switching the active canvas to the first, last, or adjacent canvas. |
@ -209,7 +209,7 @@ The View menu features items for controlling what is displayed on the drawing
canvas. canvas.
| Option | Description | | Option | Description |
|---|---| |-------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| Show | Opens a submenu of items that can be displayed or hidden, such as interface names, addresses, and labels. Use these options to help declutter the display. These options are generally saved in the topology files, so scenarios have a more consistent look when copied from one computer to another. | | Show | Opens a submenu of items that can be displayed or hidden, such as interface names, addresses, and labels. Use these options to help declutter the display. These options are generally saved in the topology files, so scenarios have a more consistent look when copied from one computer to another. |
| Show hidden nodes | Reveal nodes that have been hidden. Nodes are hidden by selecting one or more nodes, right-clicking one and choosing *hide*. | | Show hidden nodes | Reveal nodes that have been hidden. Nodes are hidden by selecting one or more nodes, right-clicking one and choosing *hide*. |
| Locked | Toggles locked view; when the view is locked, nodes cannot be moved around on the canvas with the mouse. This could be useful when sharing the topology with someone and you do not expect them to change things. | | Locked | Toggles locked view; when the view is locked, nodes cannot be moved around on the canvas with the mouse. This could be useful when sharing the topology with someone and you do not expect them to change things. |
@ -222,7 +222,7 @@ canvas.
The tools menu lists different utility functions. The tools menu lists different utility functions.
| Option | Description | | Option | Description |
|---|---| |------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| Autorearrange all | Automatically arranges all nodes on the canvas. Nodes having a greater number of links are moved to the center. This mode can continue to run while placing nodes. To turn off this autorearrange mode, click on a blank area of the canvas with the select tool, or choose this menu option again. | | Autorearrange all | Automatically arranges all nodes on the canvas. Nodes having a greater number of links are moved to the center. This mode can continue to run while placing nodes. To turn off this autorearrange mode, click on a blank area of the canvas with the select tool, or choose this menu option again. |
| Autorearrange selected | Automatically arranges the selected nodes on the canvas. | | Autorearrange selected | Automatically arranges the selected nodes on the canvas. |
| Align to grid | Moves nodes into a grid formation, starting with the smallest-numbered node in the upper-left corner of the canvas, arranging nodes in vertical columns. | | Align to grid | Moves nodes into a grid formation, starting with the smallest-numbered node in the upper-left corner of the canvas, arranging nodes in vertical columns. |
@ -238,7 +238,7 @@ The tools menu lists different utility functions.
#### Topology Generator #### Topology Generator
| Pattern | Description | | Pattern | Description |
|---|---| |----------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------|
| Random | Nodes are randomly placed about the canvas, but are not linked together. This can be used in conjunction with a WLAN node to quickly create a wireless network. | | Random | Nodes are randomly placed about the canvas, but are not linked together. This can be used in conjunction with a WLAN node to quickly create a wireless network. |
| Grid | Nodes are placed in horizontal rows starting in the upper-left corner, evenly spaced to the right; nodes are not linked to each other. | | Grid | Nodes are placed in horizontal rows starting in the upper-left corner, evenly spaced to the right; nodes are not linked to each other. |
| Connected Grid | Nodes are placed in an N x M (width and height) rectangular grid, and each node is linked to the node above, below, left and right of itself. | | Connected Grid | Nodes are placed in an N x M (width and height) rectangular grid, and each node is linked to the node above, below, left and right of itself. |
@ -307,7 +307,7 @@ in addition to global options such as node types, comments, hooks, servers,
and options. and options.
| Option | Description | | Option | Description |
|---|---| |----------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| Start or Stop | This starts or stops the emulation, performing the same function as the green Start or red Stop button. | | Start or Stop | This starts or stops the emulation, performing the same function as the green Start or red Stop button. |
| Change sessions... | Invokes the CORE Sessions dialog box containing a list of active CORE sessions in the daemon. Basic session information such as name, node count, start time, and a thumbnail are displayed. This dialog allows connecting to different sessions, shutting them down, or starting a new session. | | Change sessions... | Invokes the CORE Sessions dialog box containing a list of active CORE sessions in the daemon. Basic session information such as name, node count, start time, and a thumbnail are displayed. This dialog allows connecting to different sessions, shutting them down, or starting a new session. |
| Node types... | Invokes the CORE Node Types dialog, performing the same function as the Edit button on the Network-Layer Nodes toolbar. | | Node types... | Invokes the CORE Node Types dialog, performing the same function as the Edit button on the Network-Layer Nodes toolbar. |
@ -321,18 +321,18 @@ and options.
#### Session States #### Session States
| State | Description | | State | Description |
|---|---| |---------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| definition | Used by the GUI to tell the backend to clear any state. | | definition | Used by the GUI to tell the backend to clear any state. |
| configuration | When the user presses the *Start* button, node, link, and other configuration data is sent to the backend. This state is also reached when the user customizes a service. | | configuration | When the user presses the *Start* button, node, link, and other configuration data is sent to the backend. This state is also reached when the user customizes a service. |
| instantiation | After configuration data has been sent, just before the nodes are created. | | instantiation | After configuration data has been sent, just before the nodes are created. |
| runtime | All nodes and networks have been built and are running. (This is the same state at which the previously-named *global experiment script* was run.) | runtime | All nodes and networks have been built and are running. (This is the same state at which the previously-named *global experiment script* was run.) |
| datacollect | The user has pressed the *Stop* button, but before services have been stopped and nodes have been shut down. This is a good time to collect log files and other data from the nodes. | | datacollect | The user has pressed the *Stop* button, but before services have been stopped and nodes have been shut down. This is a good time to collect log files and other data from the nodes. |
| shutdown | All nodes and networks have been shut down and destroyed. | | shutdown | All nodes and networks have been shut down and destroyed. |
### Help Menu ### Help Menu
| Option | Description | | Option | Description |
|---|---| |--------------------------|---------------------------------------------------------------|
| CORE Github (www) | Link to the CORE GitHub page. | | CORE Github (www) | Link to the CORE GitHub page. |
| CORE Documentation (www) | Lnk to the CORE Documentation page. | | CORE Documentation (www) | Lnk to the CORE Documentation page. |
| About | Invokes the About dialog box for viewing version information. | | About | Invokes the About dialog box for viewing version information. |
@ -542,10 +542,10 @@ complexity and CPU usage. The availability of certain plug-ins varies depending
on platform. See the table below for a brief overview of wireless model types. on platform. See the table below for a brief overview of wireless model types.
|Model|Type|Supported Platform(s)|Fidelity|Description| | Model | Type | Supported Platform(s) | Fidelity | Description |
|-----|----|---------------------|--------|-----------| |-------|---------|-----------------------|----------|-------------------------------------------------------------------------------|
|Basic|on/off|Linux|Low|Ethernet bridging with nftables| | Basic | on/off | Linux | Low | Ethernet bridging with nftables |
|EMANE|Plug-in|Linux|High|TAP device connected to EMANE emulator with pluggable MAC and PHY radio types| | EMANE | Plug-in | Linux | High | TAP device connected to EMANE emulator with pluggable MAC and PHY radio types |
To quickly build a wireless network, you can first place several router nodes To quickly build a wireless network, you can first place several router nodes
onto the canvas. If you have the onto the canvas. If you have the
@ -582,7 +582,7 @@ See the [EMANE](emane.md) chapter for details on using EMANE.
CORE has a few ways to script mobility. CORE has a few ways to script mobility.
| Option | Description | | Option | Description |
|---|---| |--------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| ns-2 script | The script specifies either absolute positions or waypoints with a velocity. Locations are given with Cartesian coordinates. | | ns-2 script | The script specifies either absolute positions or waypoints with a velocity. Locations are given with Cartesian coordinates. |
| CORE API | An external entity can move nodes by sending CORE API Node messages with updated X,Y coordinates; the **coresendmsg** utility allows a shell script to generate these messages. | | CORE API | An external entity can move nodes by sending CORE API Node messages with updated X,Y coordinates; the **coresendmsg** utility allows a shell script to generate these messages. |
| EMANE events | See [EMANE](emane.md) for details on using EMANE scripts to move nodes around. Location information is typically given as latitude, longitude, and altitude. | | EMANE events | See [EMANE](emane.md) for details on using EMANE scripts to move nodes around. Location information is typically given as latitude, longitude, and altitude. |

View file

@ -18,22 +18,22 @@ networking scenarios, security studies, and increasing the size of physical test
## Topics ## Topics
| Topic | Description| | Topic | Description |
|-------|------------| |--------------------------------------|-------------------------------------------------------------------|
|[Installation](install.md)|How to install CORE and its requirements| | [Installation](install.md) | How to install CORE and its requirements |
|[Architecture](architecture.md)|Overview of the architecture| | [Architecture](architecture.md) | Overview of the architecture |
|[Node Types](nodetypes.md)|Overview of node types supported within CORE| | [Node Types](nodetypes.md) | Overview of node types supported within CORE |
|[GUI](gui.md)|How to use the GUI| | [Python GUI](pygui.md) | How to use the default python based GUI |
|[(BETA) Python GUI](pygui.md)|How to use the BETA python based GUI| | [Legacy GUI (deprecated)](gui.md) | How to use the deprecated Tcl based GUI |
|[Python API](python.md)|Covers how to control core directly using python| | [Python API](python.md) | Covers how to control core directly using python |
|[gRPC API](grpc.md)|Covers how control core using gRPC| | [gRPC API](grpc.md) | Covers how control core using gRPC |
|[Distributed](distributed.md)|Details for running CORE across multiple servers| | [Distributed](distributed.md) | Details for running CORE across multiple servers |
|[Control Network](ctrlnet.md)|How to use control networks to communicate with nodes from host| | [Control Network](ctrlnet.md) | How to use control networks to communicate with nodes from host |
|[Config Services](configservices.md)|Overview of provided config services and creating custom ones| | [Config Services](configservices.md) | Overview of provided config services and creating custom ones |
|[Services](services.md)|Overview of provided services and creating custom ones| | [Services](services.md) | Overview of provided services and creating custom ones |
|[EMANE](emane.md)|Overview of EMANE integration and integrating custom EMANE models| | [EMANE](emane.md) | Overview of EMANE integration and integrating custom EMANE models |
|[Performance](performance.md)|Notes on performance when using CORE| | [Performance](performance.md) | Notes on performance when using CORE |
|[Developers Guide](devguide.md)|Overview on how to contribute to CORE| | [Developers Guide](devguide.md) | Overview on how to contribute to CORE |
## Credits ## Credits

View file

@ -37,12 +37,12 @@ sudo modprobe sch_netem
### Tools Used ### Tools Used
The following tools will be leveraged during installation: The following tools will be leveraged during installation:
|Tool|Description| | Tool | Description |
|---|---| |---------------------------------------------|-----------------------------------------------------------------------|
|[pip](https://pip.pypa.io/en/stable/)|used to install pipx| | [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)| | [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)| | [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| | [poetry](https://python-poetry.org/) | used to install python virtual environment or building a python wheel |
### Files ### Files
The following is a list of files that would be installed after running the automated installation. The following is a list of files that would be installed after running the automated installation.
@ -74,7 +74,7 @@ The following is a list of files that would be installed after running the autom
After the installation complete it will have installed the following scripts. After the installation complete it will have installed the following scripts.
| Name | Description | | Name | Description |
|---|---| |---------------------|------------------------------------------------------------------------------|
| core-cleanup | tool to help removed lingering core created containers, bridges, directories | | 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-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-daemon | runs the backed core server providing TLV and gRPC APIs |
@ -127,7 +127,7 @@ source ~/.bashrc
# Ubuntu # Ubuntu
inv install inv install
# CentOS # CentOS
./install.sh -p /usr inv install -p /usr
``` ```
First you can use `setup.sh` as a convenience to install tooling for running invoke tasks: First you can use `setup.sh` as a convenience to install tooling for running invoke tasks:

View file

@ -9,7 +9,7 @@ The top question about the performance of CORE is often *how many nodes can it
handle?* The answer depends on several factors: handle?* The answer depends on several factors:
| Factor | Performance Impact | | Factor | Performance Impact |
|---|---| |--------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------|
| Hardware | the number and speed of processors in the computer, the available processor cache, RAM memory, and front-side bus speed may greatly affect overall performance. | | Hardware | the number and speed of processors in the computer, the available processor cache, RAM memory, and front-side bus speed may greatly affect overall performance. |
| Operating system version | distribution of Linux and the specific kernel versions used will affect overall performance. | | Operating system version | distribution of Linux and the specific kernel versions used will affect overall performance. |
| Active processes | all nodes share the same CPU resources, so if one or more nodes is performing a CPU-intensive task, overall performance will suffer. | | Active processes | all nodes share the same CPU resources, so if one or more nodes is performing a CPU-intensive task, overall performance will suffer. |

View file

@ -115,7 +115,7 @@ item, starting from the top. Most of the tools are grouped into related
sub-menus, which appear when you click on their group icon. sub-menus, which appear when you click on their group icon.
| Icon | Name | Description | | Icon | Name | Description |
|---|---|---| |------------------------------|----------------|----------------------------------------------------------------------------------------|
| ![](static/pygui/select.png) | Selection Tool | Tool for selecting, moving, configuring nodes. | | ![](static/pygui/select.png) | Selection Tool | Tool for selecting, moving, configuring nodes. |
| ![](static/pygui/start.png) | Start Button | Starts Execute mode, instantiates the emulation. | | ![](static/pygui/start.png) | Start Button | Starts Execute mode, instantiates the emulation. |
| ![](static/pygui/link.png) | Link | Allows network links to be drawn between two nodes by clicking and dragging the mouse. | | ![](static/pygui/link.png) | Link | Allows network links to be drawn between two nodes by clicking and dragging the mouse. |
@ -125,7 +125,7 @@ sub-menus, which appear when you click on their group icon.
These nodes will create a new node container and run associated services. These nodes will create a new node container and run associated services.
| Icon | Name | Description | | Icon | Name | Description |
|---|---|---| |------------------------------|---------|------------------------------------------------------------------------------|
| ![](static/pygui/router.png) | Router | Runs Quagga OSPFv2 and OSPFv3 routing to forward packets. | | ![](static/pygui/router.png) | Router | Runs Quagga OSPFv2 and OSPFv3 routing to forward packets. |
| ![](static/pygui/host.png) | Host | Emulated server machine having a default route, runs SSH server. | | ![](static/pygui/host.png) | Host | Emulated server machine having a default route, runs SSH server. |
| ![](static/pygui/pc.png) | PC | Basic emulated machine having a default route, runs no processes by default. | | ![](static/pygui/pc.png) | PC | Basic emulated machine having a default route, runs no processes by default. |
@ -138,7 +138,7 @@ These nodes are mostly used to create a Linux bridge that serves the
purpose described below. purpose described below.
| Icon | Name | Description | | Icon | Name | Description |
|---|---|---| |---------------------------------|--------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| ![](static/pygui/hub.png) | Hub | Ethernet hub forwards incoming packets to every connected node. | | ![](static/pygui/hub.png) | Hub | Ethernet hub forwards incoming packets to every connected node. |
| ![](static/pygui/lanswitch.png) | Switch | Ethernet switch intelligently forwards incoming packets to attached hosts using an Ethernet address hash table. | | ![](static/pygui/lanswitch.png) | Switch | Ethernet switch intelligently forwards incoming packets to attached hosts using an Ethernet address hash table. |
| ![](static/pygui/wlan.png) | Wireless LAN | When routers are connected to this WLAN node, they join a wireless network and an antenna is drawn instead of a connecting line; the WLAN node typically controls connectivity between attached wireless nodes based on the distance between them. | | ![](static/pygui/wlan.png) | Wireless LAN | When routers are connected to this WLAN node, they join a wireless network and an antenna is drawn instead of a connecting line; the WLAN node typically controls connectivity between attached wireless nodes based on the distance between them. |
@ -148,7 +148,7 @@ purpose described below.
### Annotation Tools ### Annotation Tools
| Icon | Name | Description | | Icon | Name | Description |
|---|---|---| |---------------------------------|-----------|---------------------------------------------------------------------|
| ![](static/pygui/marker.png) | Marker | For drawing marks on the canvas. | | ![](static/pygui/marker.png) | Marker | For drawing marks on the canvas. |
| ![](static/pygui/oval.png) | Oval | For drawing circles on the canvas that appear in the background. | | ![](static/pygui/oval.png) | Oval | For drawing circles on the canvas that appear in the background. |
| ![](static/pygui/rectangle.png) | Rectangle | For drawing rectangles on the canvas that appear in the background. | | ![](static/pygui/rectangle.png) | Rectangle | For drawing rectangles on the canvas that appear in the background. |
@ -161,7 +161,7 @@ toolbar on the left of the CORE window is replaced with the Execution toolbar
Below are the items on this toolbar, starting from the top. Below are the items on this toolbar, starting from the top.
| Icon | Name | Description | | Icon | Name | Description |
|---|---|---| |------------------------------|----------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| ![](static/pygui/stop.png) | Stop Button | Stops Execute mode, terminates the emulation, returns CORE to edit mode. | | ![](static/pygui/stop.png) | Stop Button | Stops Execute mode, terminates the emulation, returns CORE to edit mode. |
| ![](static/pygui/select.png) | Selection Tool | In Execute mode, the Selection Tool can be used for moving nodes around the canvas, and double-clicking on a node will open a shell window for that node; right-clicking on a node invokes a pop-up menu of run-time options for that node. | | ![](static/pygui/select.png) | Selection Tool | In Execute mode, the Selection Tool can be used for moving nodes around the canvas, and double-clicking on a node will open a shell window for that node; right-clicking on a node invokes a pop-up menu of run-time options for that node. |
| ![](static/pygui/marker.png) | Marker | For drawing freehand lines on the canvas, useful during demonstrations; markings are not saved. | | ![](static/pygui/marker.png) | Marker | For drawing freehand lines on the canvas, useful during demonstrations; markings are not saved. |
@ -179,7 +179,7 @@ The File menu contains options for manipulating the **.imn** Configuration
Files. Generally, these menu items should not be used in Execute mode. Files. Generally, these menu items should not be used in Execute mode.
| Option | Description | | Option | Description |
|---|---| |-----------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| New Session | This starts a new session with an empty canvas. | | New Session | This starts a new session with an empty canvas. |
| Save | Saves the current topology. If you have not yet specified a file name, the Save As dialog box is invoked. | | Save | Saves the current topology. If you have not yet specified a file name, the Save As dialog box is invoked. |
| Save As | Invokes the Save As dialog box for selecting a new **.xml** file for saving the current configuration in the XML file. | | Save As | Invokes the Save As dialog box for selecting a new **.xml** file for saving the current configuration in the XML file. |
@ -191,20 +191,20 @@ Files. Generally, these menu items should not be used in Execute mode.
### Edit Menu ### Edit Menu
| Option | Description | | Option | Description |
|---|---| |--------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| Preferences | Invokes the Preferences dialog box. | | Preferences | Invokes the Preferences dialog box. |
| Custom Nodes | Custom node creation dialog box. | | Custom Nodes | Custom node creation dialog box. |
| Undo | (Disabled) Attempts to undo the last edit in edit mode. | | Undo | (Disabled) Attempts to undo the last edit in edit mode. |
| Redo | (Disabled) Attempts to redo an edit that has been undone. | | Redo | (Disabled) Attempts to redo an edit that has been undone. |
| Cut, Copy, Paste, Delete | Used to cut, copy, paste, and delete a selection. When nodes are pasted, their node numbers are automatically incremented, and existing links are preserved with new IP addresses assigned. Services and their customizations are copied to the new node, but care should be taken as node IP addresses have changed with possibly old addresses remaining in any custom service configurations. Annotations may also be copied and pasted. | Cut, Copy, Paste, Delete | Used to cut, copy, paste, and delete a selection. When nodes are pasted, their node numbers are automatically incremented, and existing links are preserved with new IP addresses assigned. Services and their customizations are copied to the new node, but care should be taken as node IP addresses have changed with possibly old addresses remaining in any custom service configurations. Annotations may also be copied and pasted. |
### Canvas Menu ### Canvas Menu
The canvas menu provides commands related to the editing canvas. The canvas menu provides commands related to the editing canvas.
| Option | Description | | Option | Description |
|---|---| |------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| Size/scale | Invokes a Canvas Size and Scale dialog that allows configuring the canvas size, scale, and geographic reference point. The size controls allow changing the width and height of the current canvas, in pixels or meters. The scale allows specifying how many meters are equivalent to 100 pixels. The reference point controls specify the latitude, longitude, and altitude reference point used to convert between geographic and Cartesian coordinate systems. By clicking the *Save as default* option, all new canvases will be created with these properties. The default canvas size can also be changed in the Preferences dialog box. | Size/scale | Invokes a Canvas Size and Scale dialog that allows configuring the canvas size, scale, and geographic reference point. The size controls allow changing the width and height of the current canvas, in pixels or meters. The scale allows specifying how many meters are equivalent to 100 pixels. The reference point controls specify the latitude, longitude, and altitude reference point used to convert between geographic and Cartesian coordinate systems. By clicking the *Save as default* option, all new canvases will be created with these properties. The default canvas size can also be changed in the Preferences dialog box. |
| Wallpaper | Used for setting the canvas background image. | | Wallpaper | Used for setting the canvas background image. |
### View Menu ### View Menu
@ -212,7 +212,7 @@ The canvas menu provides commands related to the editing canvas.
The View menu features items for toggling on and off their display on the canvas. The View menu features items for toggling on and off their display on the canvas.
| Option | Description | | Option | Description |
|---|---| |-----------------|-----------------------------------|
| Interface Names | Display interface names on links. | | Interface Names | Display interface names on links. |
| IPv4 Addresses | Display IPv4 addresses on links. | | IPv4 Addresses | Display IPv4 addresses on links. |
| IPv6 Addresses | Display IPv6 addresses on links. | | IPv6 Addresses | Display IPv6 addresses on links. |
@ -226,7 +226,7 @@ The View menu features items for toggling on and off their display on the canvas
The tools menu lists different utility functions. The tools menu lists different utility functions.
| Option | Description | | Option | Description |
|---|---| |---------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| Find | Display find dialog used for highlighting a node on the canvas. | | Find | Display find dialog used for highlighting a node on the canvas. |
| Auto Grid | Automatically layout nodes in a grid. | | Auto Grid | Automatically layout nodes in a grid. |
| IP addresses | Invokes the IP Addresses dialog box for configuring which IPv4/IPv6 prefixes are used when automatically addressing new interfaces. | | IP addresses | Invokes the IP Addresses dialog box for configuring which IPv4/IPv6 prefixes are used when automatically addressing new interfaces. |
@ -289,7 +289,7 @@ in addition to global options such as node types, comments, hooks, servers,
and options. and options.
| Option | Description | | Option | Description |
|---|---| |----------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| Sessions | Invokes the CORE Sessions dialog box containing a list of active CORE sessions in the daemon. Basic session information such as name, node count, start time, and a thumbnail are displayed. This dialog allows connecting to different sessions, shutting them down, or starting a new session. | | Sessions | Invokes the CORE Sessions dialog box containing a list of active CORE sessions in the daemon. Basic session information such as name, node count, start time, and a thumbnail are displayed. This dialog allows connecting to different sessions, shutting them down, or starting a new session. |
| Servers | Invokes the CORE emulation servers dialog for configuring. | | Servers | Invokes the CORE emulation servers dialog for configuring. |
| Options | Presents per-session options, such as the IPv4 prefix to be used, if any, for a control network the ability to preserve the session directory; and an on/off switch for SDT3D support. | | Options | Presents per-session options, such as the IPv4 prefix to be used, if any, for a control network the ability to preserve the session directory; and an on/off switch for SDT3D support. |
@ -298,18 +298,18 @@ and options.
#### Session States #### Session States
| State | Description | | State | Description |
|---|---| |---------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| Definition | Used by the GUI to tell the backend to clear any state. | | Definition | Used by the GUI to tell the backend to clear any state. |
| Configuration | When the user presses the *Start* button, node, link, and other configuration data is sent to the backend. This state is also reached when the user customizes a service. | | Configuration | When the user presses the *Start* button, node, link, and other configuration data is sent to the backend. This state is also reached when the user customizes a service. |
| Instantiation | After configuration data has been sent, just before the nodes are created. | | Instantiation | After configuration data has been sent, just before the nodes are created. |
| Runtime | All nodes and networks have been built and are running. (This is the same state at which the previously-named *global experiment script* was run.) | Runtime | All nodes and networks have been built and are running. (This is the same state at which the previously-named *global experiment script* was run.) |
| Datacollect | The user has pressed the *Stop* button, but before services have been stopped and nodes have been shut down. This is a good time to collect log files and other data from the nodes. | | Datacollect | The user has pressed the *Stop* button, but before services have been stopped and nodes have been shut down. This is a good time to collect log files and other data from the nodes. |
| Shutdown | All nodes and networks have been shut down and destroyed. | | Shutdown | All nodes and networks have been shut down and destroyed. |
### Help Menu ### Help Menu
| Option | Description | | Option | Description |
|---|---| |--------------------------|---------------------------------------------------------------|
| CORE Github (www) | Link to the CORE GitHub page. | | CORE Github (www) | Link to the CORE GitHub page. |
| CORE Documentation (www) | Lnk to the CORE Documentation page. | | CORE Documentation (www) | Lnk to the CORE Documentation page. |
| About | Invokes the About dialog box for viewing version information. | | About | Invokes the About dialog box for viewing version information. |
@ -519,10 +519,10 @@ complexity and CPU usage. The availability of certain plug-ins varies depending
on platform. See the table below for a brief overview of wireless model types. on platform. See the table below for a brief overview of wireless model types.
|Model|Type|Supported Platform(s)|Fidelity|Description| | Model | Type | Supported Platform(s) | Fidelity | Description |
|-----|----|---------------------|--------|-----------| |-------|---------|-----------------------|----------|-------------------------------------------------------------------------------|
|Basic|on/off|Linux|Low|Ethernet bridging with nftables| | Basic | on/off | Linux | Low | Ethernet bridging with nftables |
|EMANE|Plug-in|Linux|High|TAP device connected to EMANE emulator with pluggable MAC and PHY radio types| | EMANE | Plug-in | Linux | High | TAP device connected to EMANE emulator with pluggable MAC and PHY radio types |
To quickly build a wireless network, you can first place several router nodes To quickly build a wireless network, you can first place several router nodes
onto the canvas. If you have the onto the canvas. If you have the
@ -557,7 +557,7 @@ See the [EMANE](emane.md) chapter for details on using EMANE.
CORE has a few ways to script mobility. CORE has a few ways to script mobility.
| Option | Description | | Option | Description |
|---|---| |--------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| ns-2 script | The script specifies either absolute positions or waypoints with a velocity. Locations are given with Cartesian coordinates. | | ns-2 script | The script specifies either absolute positions or waypoints with a velocity. Locations are given with Cartesian coordinates. |
| CORE API | An external entity can move nodes by sending CORE API Node messages with updated X,Y coordinates; the **coresendmsg** utility allows a shell script to generate these messages. | | CORE API | An external entity can move nodes by sending CORE API Node messages with updated X,Y coordinates; the **coresendmsg** utility allows a shell script to generate these messages. |
| EMANE events | See [EMANE](emane.md) for details on using EMANE scripts to move nodes around. Location information is typically given as latitude, longitude, and altitude. | | EMANE events | See [EMANE](emane.md) for details on using EMANE scripts to move nodes around. Location information is typically given as latitude, longitude, and altitude. |

View file

@ -341,19 +341,11 @@ EMANE Model Configuration:
```python ```python
from core import utils from core import utils
# emane network specific config # standardized way to retrieve an appropriate config id
session.emane.set_model_config(emane.id, EmaneIeee80211abgModel.name, { # iface id can be omitted, to allow a general configuration for a model, per node
"unicastrate": "3",
})
# node specific config
session.emane.set_model_config(node.id, EmaneIeee80211abgModel.name, {
"unicastrate": "3",
})
# node interface specific config
config_id = utils.iface_config_id(node.id, iface_id) config_id = utils.iface_config_id(node.id, iface_id)
session.emane.set_model_config(config_id, EmaneIeee80211abgModel.name, { # set emane configuration for the config id
session.emane.set_config(config_id, EmaneIeee80211abgModel.name, {
"unicastrate": "3", "unicastrate": "3",
}) })
``` ```

View file

@ -22,23 +22,23 @@ shutdown commands, and meta-data associated with a node.
## Available Services ## Available Services
| Service Group | Services | | Service Group | Services |
|---|---| |----------------------------------|-----------------------------------------------------------------------|
|[BIRD](services/bird.md)|BGP, OSPF, RADV, RIP, Static| | [BIRD](services/bird.md) | BGP, OSPF, RADV, RIP, Static |
|[EMANE](services/emane.md)|Transport Service| | [EMANE](services/emane.md) | Transport Service |
|[FRR](services/frr.md)|BABEL, BGP, OSPFv2, OSPFv3, PIMD, RIP, RIPNG, Zebra| | [FRR](services/frr.md) | BABEL, BGP, OSPFv2, OSPFv3, PIMD, RIP, RIPNG, Zebra |
|[NRL](services/nrl.md)|arouted, MGEN Sink, MGEN Actor, NHDP, OLSR, OLSRORG, OLSRv2, SMF| | [NRL](services/nrl.md) | arouted, MGEN Sink, MGEN Actor, NHDP, OLSR, OLSRORG, OLSRv2, SMF |
|[Quagga](services/quagga.md)|BABEL, BGP, OSPFv2, OSPFv3, OSPFv3 MDR, RIP, RIPNG, XPIMD, Zebra| | [Quagga](services/quagga.md) | BABEL, BGP, OSPFv2, OSPFv3, OSPFv3 MDR, RIP, RIPNG, XPIMD, Zebra |
|[SDN](services/sdn.md)|OVS, RYU| | [SDN](services/sdn.md) | OVS, RYU |
|[Security](services/security.md)|Firewall, IPsec, NAT, VPN Client, VPN Server| | [Security](services/security.md) | Firewall, IPsec, NAT, VPN Client, VPN Server |
|[Utility](services/utility.md)|ATD, Routing Utils, DHCP, FTP, IP Forward, PCAP, RADVD, SSF, UCARP| | [Utility](services/utility.md) | ATD, Routing Utils, DHCP, FTP, IP Forward, PCAP, RADVD, SSF, UCARP |
|[XORP](services/xorp.md)|BGP, OLSR, OSPFv2, OSPFv3, PIMSM4, PIMSM6, RIP, RIPNG, Router Manager| | [XORP](services/xorp.md) | BGP, OLSR, OSPFv2, OSPFv3, PIMSM4, PIMSM6, RIP, RIPNG, Router Manager |
## Node Types and Default Services ## Node Types and Default Services
Here are the default node types and their services: Here are the default node types and their services:
| Node Type | Services | | Node Type | Services |
|---|---| |-----------|--------------------------------------------------------------------------------------------------------------------------------------------|
| *router* | zebra, OSFPv2, OSPFv3, and IPForward services for IGP link-state routing. | | *router* | zebra, OSFPv2, OSPFv3, and IPForward services for IGP link-state routing. |
| *host* | DefaultRoute and SSH services, representing an SSH server having a default route when connected directly to a router. | | *host* | DefaultRoute and SSH services, representing an SSH server having a default route when connected directly to a router. |
| *PC* | DefaultRoute service for having a default route when connected directly to a router. | | *PC* | DefaultRoute service for having a default route when connected directly to a router. |

Binary file not shown.

Before

(image error) Size: 160 KiB

After

(image error) Size: 140 KiB

Binary file not shown.

Before

(image error) Size: 131 KiB

After

(image error) Size: 105 KiB

View file

@ -210,7 +210,7 @@ def install_poetry(c: Context, dev: bool, local: bool, hide: bool) -> None:
def install_ospf_mdr(c: Context, os_info: OsInfo, hide: bool) -> None: def install_ospf_mdr(c: Context, os_info: OsInfo, hide: bool) -> None:
if c.run("which zebra", warn=True, hide=hide): if c.run("sudo which zebra", warn=True, hide=hide):
print("\nquagga already installed, skipping ospf mdr") print("\nquagga already installed, skipping ospf mdr")
return return
if os_info.like == OsLike.DEBIAN: if os_info.like == OsLike.DEBIAN: