adjustments to remove global emane configuration, platform configurations can now be configured per nem, retrieve emane specific core settings from session options

This commit is contained in:
Blake Harnden 2021-05-19 20:44:00 -07:00
parent 071023b1d9
commit 5bc3345d37
17 changed files with 98 additions and 483 deletions

View file

@ -28,10 +28,8 @@ from core.api.grpc.configservices_pb2 import (
from core.api.grpc.core_pb2 import ExecuteScriptRequest, GetConfigRequest from core.api.grpc.core_pb2 import ExecuteScriptRequest, GetConfigRequest
from core.api.grpc.emane_pb2 import ( from core.api.grpc.emane_pb2 import (
EmaneLinkRequest, EmaneLinkRequest,
GetEmaneConfigRequest,
GetEmaneEventChannelRequest, GetEmaneEventChannelRequest,
GetEmaneModelConfigRequest, GetEmaneModelConfigRequest,
SetEmaneConfigRequest,
SetEmaneModelConfigRequest, SetEmaneModelConfigRequest,
) )
from core.api.grpc.mobility_pb2 import ( from core.api.grpc.mobility_pb2 import (
@ -253,7 +251,6 @@ class CoreGrpcClient:
if asymmetric_links: if asymmetric_links:
asymmetric_links = [x.to_proto() for x in asymmetric_links] asymmetric_links = [x.to_proto() for x in asymmetric_links]
hooks = [x.to_proto() for x in session.hooks.values()] hooks = [x.to_proto() for x in session.hooks.values()]
emane_config = {k: v.value for k, v in session.emane_config.items()}
emane_model_configs = [] emane_model_configs = []
mobility_configs = [] mobility_configs = []
wlan_configs = [] wlan_configs = []
@ -313,7 +310,6 @@ class CoreGrpcClient:
links=links, links=links,
location=session.location.to_proto(), location=session.location.to_proto(),
hooks=hooks, hooks=hooks,
emane_config=emane_config,
emane_model_configs=emane_model_configs, emane_model_configs=emane_model_configs,
wlan_configs=wlan_configs, wlan_configs=wlan_configs,
mobility_configs=mobility_configs, mobility_configs=mobility_configs,
@ -917,31 +913,6 @@ class CoreGrpcClient:
response = self.stub.SetWlanConfig(request) response = self.stub.SetWlanConfig(request)
return response.result return response.result
def get_emane_config(self, session_id: int) -> Dict[str, wrappers.ConfigOption]:
"""
Get session emane configuration.
:param session_id: session id
:return: response with a list of configuration groups
:raises grpc.RpcError: when session doesn't exist
"""
request = GetEmaneConfigRequest(session_id=session_id)
response = self.stub.GetEmaneConfig(request)
return wrappers.ConfigOption.from_dict(response.config)
def set_emane_config(self, session_id: int, config: Dict[str, str]) -> bool:
"""
Set session emane configuration.
:param session_id: session id
:param config: emane configuration
:return: True for success, False otherwise
:raises grpc.RpcError: when session doesn't exist
"""
request = SetEmaneConfigRequest(session_id=session_id, config=config)
response = self.stub.SetEmaneConfig(request)
return response.result
def get_emane_model_config( def get_emane_model_config(
self, session_id: int, node_id: int, model: str, iface_id: int = -1 self, session_id: int, node_id: int, model: str, iface_id: int = -1
) -> Dict[str, wrappers.ConfigOption]: ) -> Dict[str, wrappers.ConfigOption]:

View file

@ -630,10 +630,6 @@ def get_node_config_service_configs(session: Session) -> List[ConfigServiceConfi
return configs return configs
def get_emane_config(session: Session) -> Dict[str, common_pb2.ConfigOption]:
return get_config_options(session.emane.config, session.emane.emane_config)
def get_mobility_node( def get_mobility_node(
session: Session, node_id: int, context: ServicerContext session: Session, node_id: int, context: ServicerContext
) -> Union[WlanNode, EmaneNet]: ) -> Union[WlanNode, EmaneNet]:
@ -663,7 +659,6 @@ def convert_session(session: Session) -> wrappers.Session:
x=x, y=y, z=z, lat=lat, lon=lon, alt=alt, scale=session.location.refscale x=x, y=y, z=z, lat=lat, lon=lon, alt=alt, scale=session.location.refscale
) )
hooks = get_hooks(session) hooks = get_hooks(session)
emane_config = get_emane_config(session)
emane_model_configs = get_emane_model_configs(session) emane_model_configs = get_emane_model_configs(session)
wlan_configs = get_wlan_configs(session) wlan_configs = get_wlan_configs(session)
mobility_configs = get_mobility_configs(session) mobility_configs = get_mobility_configs(session)
@ -685,7 +680,6 @@ def convert_session(session: Session) -> wrappers.Session:
default_services=default_services, default_services=default_services,
location=location, location=location,
hooks=hooks, hooks=hooks,
emane_config=emane_config,
emane_model_configs=emane_model_configs, emane_model_configs=emane_model_configs,
wlan_configs=wlan_configs, wlan_configs=wlan_configs,
service_configs=service_configs, service_configs=service_configs,

View file

@ -33,14 +33,10 @@ from core.api.grpc.emane_pb2 import (
EmaneLinkResponse, EmaneLinkResponse,
EmanePathlossesRequest, EmanePathlossesRequest,
EmanePathlossesResponse, EmanePathlossesResponse,
GetEmaneConfigRequest,
GetEmaneConfigResponse,
GetEmaneEventChannelRequest, GetEmaneEventChannelRequest,
GetEmaneEventChannelResponse, GetEmaneEventChannelResponse,
GetEmaneModelConfigRequest, GetEmaneModelConfigRequest,
GetEmaneModelConfigResponse, GetEmaneModelConfigResponse,
SetEmaneConfigRequest,
SetEmaneConfigResponse,
SetEmaneModelConfigRequest, SetEmaneModelConfigRequest,
SetEmaneModelConfigResponse, SetEmaneModelConfigResponse,
) )
@ -266,7 +262,6 @@ class CoreGrpcServer(core_pb2_grpc.CoreApiServicer):
return core_pb2.StartSessionResponse(result=False, exceptions=exceptions) return core_pb2.StartSessionResponse(result=False, exceptions=exceptions)
# emane configs # emane configs
session.emane.config.update(request.emane_config)
for config in request.emane_model_configs: for config in request.emane_model_configs:
_id = utils.iface_config_id(config.node_id, config.iface_id) _id = utils.iface_config_id(config.node_id, config.iface_id)
session.emane.set_config(_id, config.model, config.config) session.emane.set_config(_id, config.model, config.config)
@ -1045,36 +1040,6 @@ class CoreGrpcServer(core_pb2_grpc.CoreApiServicer):
node.updatemodel(config) node.updatemodel(config)
return SetWlanConfigResponse(result=True) return SetWlanConfigResponse(result=True)
def GetEmaneConfig(
self, request: GetEmaneConfigRequest, context: ServicerContext
) -> GetEmaneConfigResponse:
"""
Retrieve EMANE configuration of a session
:param request: get-EMANE-configuration request
:param context: context object
:return: get-EMANE-configuration response
"""
logger.debug("get emane config: %s", request)
session = self.get_session(request.session_id, context)
config = grpcutils.get_emane_config(session)
return GetEmaneConfigResponse(config=config)
def SetEmaneConfig(
self, request: SetEmaneConfigRequest, context: ServicerContext
) -> SetEmaneConfigResponse:
"""
Set EMANE configuration of a session
:param request: set-EMANE-configuration request
:param context: context object
:return: set-EMANE-configuration response
"""
logger.debug("set emane config: %s", request)
session = self.get_session(request.session_id, context)
session.emane.config.update(request.config)
return SetEmaneConfigResponse(result=True)
def GetEmaneModelConfig( def GetEmaneModelConfig(
self, request: GetEmaneModelConfigRequest, context: ServicerContext self, request: GetEmaneModelConfigRequest, context: ServicerContext
) -> GetEmaneModelConfigResponse: ) -> GetEmaneModelConfigResponse:

View file

@ -783,7 +783,6 @@ class Session:
x=0.0, y=0.0, z=0.0, lat=47.57917, lon=-122.13232, alt=2.0, scale=150.0 x=0.0, y=0.0, z=0.0, lat=47.57917, lon=-122.13232, alt=2.0, scale=150.0
) )
hooks: Dict[str, Hook] = field(default_factory=dict) hooks: Dict[str, Hook] = field(default_factory=dict)
emane_config: Dict[str, ConfigOption] = field(default_factory=dict)
metadata: Dict[str, str] = field(default_factory=dict) metadata: Dict[str, str] = field(default_factory=dict)
file: Path = None file: Path = None
options: Dict[str, ConfigOption] = field(default_factory=dict) options: Dict[str, ConfigOption] = field(default_factory=dict)
@ -836,7 +835,6 @@ class Session:
default_services=default_services, default_services=default_services,
location=SessionLocation.from_proto(proto.location), location=SessionLocation.from_proto(proto.location),
hooks=hooks, hooks=hooks,
emane_config=ConfigOption.from_dict(proto.emane_config),
metadata=dict(proto.metadata), metadata=dict(proto.metadata),
file=file_path, file=file_path,
options=options, options=options,
@ -889,11 +887,6 @@ class Session:
self.links.append(link) self.links.append(link)
return link return link
def set_emane(self, config: Dict[str, str]) -> None:
for key, value in config.items():
option = ConfigOption(name=key, value=value)
self.emane_config[key] = option
def set_options(self, config: Dict[str, str]) -> None: def set_options(self, config: Dict[str, str]) -> None:
for key, value in config.items(): for key, value in config.items():
option = ConfigOption(name=key, value=value) option = ConfigOption(name=key, value=value)

View file

@ -1046,8 +1046,6 @@ class CoreHandler(socketserver.BaseRequestHandler):
self.handle_config_mobility(message_type, config_data) self.handle_config_mobility(message_type, config_data)
elif config_data.object in self.session.mobility.models: elif config_data.object in self.session.mobility.models:
replies = self.handle_config_mobility_models(message_type, config_data) replies = self.handle_config_mobility_models(message_type, config_data)
elif config_data.object == self.session.emane.name:
replies = self.handle_config_emane(message_type, config_data)
elif config_data.object in EmaneModelManager.models: elif config_data.object in EmaneModelManager.models:
replies = self.handle_config_emane_models(message_type, config_data) replies = self.handle_config_emane_models(message_type, config_data)
else: else:
@ -1379,36 +1377,6 @@ class CoreHandler(socketserver.BaseRequestHandler):
return replies return replies
def handle_config_emane(self, message_type, config_data):
replies = []
node_id = config_data.node
object_name = config_data.object
iface_id = config_data.iface_id
values_str = config_data.data_values
node_id = utils.iface_config_id(node_id, iface_id)
logger.debug(
"received configure message for %s nodenum: %s", object_name, node_id
)
if message_type == ConfigFlags.REQUEST:
logger.info("replying to configure request for %s model", object_name)
typeflags = ConfigFlags.NONE.value
config = self.session.emane.config
config_response = ConfigShim.config_data(
0, node_id, typeflags, self.session.emane.emane_config, config
)
replies.append(config_response)
elif message_type != ConfigFlags.RESET:
if not object_name:
logger.info("no configuration object for node %s", node_id)
return []
if values_str:
config = ConfigShim.str_to_dict(values_str)
self.session.emane.config = config
return replies
def handle_config_emane_models(self, message_type, config_data): def handle_config_emane_models(self, message_type, config_data):
replies = [] replies = []
node_id = config_data.node node_id = config_data.node
@ -1851,14 +1819,6 @@ class CoreHandler(socketserver.BaseRequestHandler):
) )
self.session.broadcast_config(config_data) self.session.broadcast_config(config_data)
# send global emane config
config = self.session.emane.config
logger.debug("global emane config: values(%s)", config)
config_data = ConfigShim.config_data(
0, None, ConfigFlags.UPDATE.value, self.session.emane.emane_config, config
)
self.session.broadcast_config(config_data)
# send emane model configs # send emane model configs
for node_id, model_configs in self.session.emane.node_configs.items(): for node_id, model_configs in self.session.emane.node_configs.items():
for model_name, config in model_configs.items(): for model_name, config in model_configs.items():

View file

@ -1,30 +1,21 @@
""" """
emane.py: definition of an Emane class for implementing configuration control of an EMANE emulation. Implements configuration and control of an EMANE emulation.
""" """
import logging import logging
import os import os
import threading import threading
from collections import OrderedDict
from dataclasses import dataclass, field from dataclasses import dataclass, field
from enum import Enum from enum import Enum
from pathlib import Path
from typing import TYPE_CHECKING, Dict, List, Optional, Set, Tuple, Type from typing import TYPE_CHECKING, Dict, List, Optional, Set, Tuple, Type
from core import utils from core import utils
from core.config import ConfigGroup, Configuration
from core.emane import emanemanifest
from core.emane.emanemodel import EmaneModel from core.emane.emanemodel import EmaneModel
from core.emane.linkmonitor import EmaneLinkMonitor from core.emane.linkmonitor import EmaneLinkMonitor
from core.emane.modelmanager import EmaneModelManager from core.emane.modelmanager import EmaneModelManager
from core.emane.nodes import EmaneNet from core.emane.nodes import EmaneNet
from core.emulator.data import LinkData from core.emulator.data import LinkData
from core.emulator.enumerations import ( from core.emulator.enumerations import LinkTypes, MessageFlags, RegisterTlvs
ConfigDataTypes,
LinkTypes,
MessageFlags,
RegisterTlvs,
)
from core.errors import CoreCommandError, CoreError from core.errors import CoreCommandError, CoreError
from core.nodes.base import CoreNetworkBase, CoreNode, CoreNodeBase, NodeBase from core.nodes.base import CoreNetworkBase, CoreNode, CoreNodeBase, NodeBase
from core.nodes.interface import CoreInterface, TunTap from core.nodes.interface import CoreInterface, TunTap
@ -102,8 +93,6 @@ class EmaneManager:
self.eventmonthread: Optional[threading.Thread] = None self.eventmonthread: Optional[threading.Thread] = None
# model for global EMANE configuration options # model for global EMANE configuration options
self.emane_config: EmaneGlobalModel = EmaneGlobalModel(session)
self.config: Dict[str, str] = self.emane_config.default_values()
self.node_configs: Dict[int, Dict[str, Dict[str, str]]] = {} self.node_configs: Dict[int, Dict[str, Dict[str, str]]] = {}
self.node_models: Dict[int, str] = {} self.node_models: Dict[int, str] = {}
@ -115,7 +104,7 @@ class EmaneManager:
self.event_device: Optional[str] = None self.event_device: Optional[str] = None
def next_nem_id(self, iface: CoreInterface) -> int: def next_nem_id(self, iface: CoreInterface) -> int:
nem_id = int(self.config["nem_id_start"]) nem_id = self.session.options.get_config_int("nem_id_start")
while nem_id in self.nems_to_ifaces: while nem_id in self.nems_to_ifaces:
nem_id += 1 nem_id += 1
self.nems_to_ifaces[nem_id] = iface self.nems_to_ifaces[nem_id] = iface
@ -206,7 +195,6 @@ class EmaneManager:
def config_reset(self, node_id: int = None) -> None: def config_reset(self, node_id: int = None) -> None:
if node_id is None: if node_id is None:
self.config = self.emane_config.default_values()
self.node_configs.clear() self.node_configs.clear()
self.node_models.clear() self.node_models.clear()
else: else:
@ -224,25 +212,20 @@ class EmaneManager:
self.service = None self.service = None
self.event_device = None self.event_device = None
def initeventservice(self, filename: str = None, shutdown: bool = False) -> None: def initeventservice(self) -> None:
""" """
Re-initialize the EMANE Event service. Re-initialize the EMANE Event service.
The multicast group and/or port may be configured. The multicast group and/or port may be configured.
""" """
self.deleteeventservice()
if shutdown:
return
# Get the control network to be used for events # Get the control network to be used for events
group, port = self.config["eventservicegroup"].split(":") group, port = "224.1.2.8:45703".split(":")
self.event_device = self.config["eventservicedevice"] self.event_device = DEFAULT_DEV
eventnetidx = self.session.get_control_net_index(self.event_device) eventnetidx = self.session.get_control_net_index(self.event_device)
if eventnetidx < 0: if eventnetidx < 0:
logger.error( logger.error(
"invalid emane event service device provided: %s", self.event_device "invalid emane event service device provided: %s", self.event_device
) )
return return
# make sure the event control network is in place # make sure the event control network is in place
eventnet = self.session.add_remove_control_net( eventnet = self.session.add_remove_control_net(
net_index=eventnetidx, remove=False, conf_required=False net_index=eventnetidx, remove=False, conf_required=False
@ -251,14 +234,13 @@ class EmaneManager:
# direct EMANE events towards control net bridge # direct EMANE events towards control net bridge
self.event_device = eventnet.brname self.event_device = eventnet.brname
self.eventchannel = (group, int(port), self.event_device) self.eventchannel = (group, int(port), self.event_device)
# disabled otachannel for event service # disabled otachannel for event service
# only needed for e.g. antennaprofile events xmit by models # only needed for e.g. antennaprofile events xmit by models
logger.info("using %s for event service traffic", self.event_device) logger.info("using %s for emane event service", self.event_device)
try: try:
self.service = EventService(eventchannel=self.eventchannel, otachannel=None) self.service = EventService(eventchannel=self.eventchannel, otachannel=None)
except EventServiceException: except EventServiceException:
logger.exception("error instantiating emane EventService") logger.exception("error starting emane event service")
def add_node(self, emane_net: EmaneNet) -> None: def add_node(self, emane_net: EmaneNet) -> None:
""" """
@ -309,9 +291,8 @@ class EmaneManager:
if EventService is None: if EventService is None:
raise CoreError("EMANE python bindings are not installed") raise CoreError("EMANE python bindings are not installed")
# control network bridge required for EMANE 0.9.2 # control network bridge required for emane
# - needs to exist when eventservice binds to it (initeventservice) otadev = DEFAULT_DEV
otadev = self.config["otamanagerdevice"]
netidx = self.session.get_control_net_index(otadev) netidx = self.session.get_control_net_index(otadev)
logger.debug("emane ota manager device: index(%s) otadev(%s)", netidx, otadev) logger.debug("emane ota manager device: index(%s) otadev(%s)", netidx, otadev)
if netidx < 0: if netidx < 0:
@ -320,25 +301,9 @@ class EmaneManager:
otadev, otadev,
) )
return EmaneState.NOT_READY return EmaneState.NOT_READY
self.session.add_remove_control_net( self.session.add_remove_control_net(
net_index=netidx, remove=False, conf_required=False net_index=netidx, remove=False, conf_required=False
) )
eventdev = self.config["eventservicedevice"]
logger.debug("emane event service device: eventdev(%s)", eventdev)
if eventdev != otadev:
netidx = self.session.get_control_net_index(eventdev)
logger.debug("emane event service device index: %s", netidx)
if netidx < 0:
logger.error(
"emane cannot start due to invalid event service device: %s",
eventdev,
)
return EmaneState.NOT_READY
self.session.add_remove_control_net(
net_index=netidx, remove=False, conf_required=False
)
self.check_node_models() self.check_node_models()
return EmaneState.SUCCESS return EmaneState.SUCCESS
@ -354,8 +319,9 @@ class EmaneManager:
status = self.setup() status = self.setup()
if status != EmaneState.SUCCESS: if status != EmaneState.SUCCESS:
return status return status
self.starteventmonitor() self.initeventservice()
self.buildeventservicexml() if self.service and self.doeventmonitor():
self.starteventmonitor()
with self._emane_node_lock: with self._emane_node_lock:
logger.info("emane building xmls...") logger.info("emane building xmls...")
start_data = self.get_start_data() start_data = self.get_start_data()
@ -392,40 +358,54 @@ class EmaneManager:
control_net = self.session.add_remove_control_net( control_net = self.session.add_remove_control_net(
0, remove=False, conf_required=False 0, remove=False, conf_required=False
) )
if isinstance(node, CoreNode):
# setup ota device
otagroup, _otaport = self.config["otamanagergroup"].split(":")
otadev = self.config["otamanagerdevice"]
otanetidx = self.session.get_control_net_index(otadev)
eventgroup, _eventport = self.config["eventservicegroup"].split(":")
eventdev = self.config["eventservicedevice"]
eventservicenetidx = self.session.get_control_net_index(eventdev)
# control network not yet started here
self.session.add_remove_control_iface(
node, 0, remove=False, conf_required=False
)
if otanetidx > 0:
logger.info("adding ota device ctrl%d", otanetidx)
self.session.add_remove_control_iface(
node, otanetidx, remove=False, conf_required=False
)
if eventservicenetidx >= 0:
logger.info("adding event service device ctrl%d", eventservicenetidx)
self.session.add_remove_control_iface(
node, eventservicenetidx, remove=False, conf_required=False
)
# multicast route is needed for OTA data
logger.info("OTA GROUP(%s) OTA DEV(%s)", otagroup, otadev)
node.node_net_client.create_route(otagroup, otadev)
# multicast route is also needed for event data if on control network
if eventservicenetidx >= 0 and eventgroup != otagroup:
node.node_net_client.create_route(eventgroup, eventdev)
# builds xmls and start emane daemons # builds xmls and start emane daemons
for iface in data.ifaces: for iface in data.ifaces:
if isinstance(node, CoreNode):
self.setup_ota(node, iface)
emanexml.build_platform_xml(self, control_net, node, iface) emanexml.build_platform_xml(self, control_net, node, iface)
self.start_daemon(node, iface) self.start_daemon(node, iface)
self.install_iface(iface) self.install_iface(iface)
def setup_ota(self, node: CoreNode, iface: CoreInterface) -> None:
if not isinstance(iface.net, EmaneNet):
raise CoreError(
f"emane interface not connected to emane net: {iface.net.name}"
)
config = self.get_iface_config(iface.net, iface)
# setup ota device
otagroup, _otaport = config["otamanagergroup"].split(":")
otadev = config["otamanagerdevice"]
otanetidx = self.session.get_control_net_index(otadev)
eventgroup, _eventport = config["eventservicegroup"].split(":")
eventdev = config["eventservicedevice"]
eventservicenetidx = self.session.get_control_net_index(eventdev)
# control network not yet started here
self.session.add_remove_control_iface(
node, 0, remove=False, conf_required=False
)
if otanetidx > 0:
logger.info("adding ota device ctrl%d", otanetidx)
self.session.add_remove_control_iface(
node, otanetidx, remove=False, conf_required=False
)
if eventservicenetidx >= 0:
logger.info("adding event service device ctrl%d", eventservicenetidx)
self.session.add_remove_control_iface(
node, eventservicenetidx, remove=False, conf_required=False
)
# multicast route is needed for OTA data
logger.info(
"node(%s) interface(%s) ota group(%s) dev(%s)",
node.name,
iface.name,
otagroup,
otadev,
)
node.node_net_client.create_route(otagroup, otadev)
# multicast route is also needed for event data if on control network
if eventservicenetidx >= 0 and eventgroup != otagroup:
node.node_net_client.create_route(eventgroup, eventdev)
def get_iface(self, nem_id: int) -> Optional[CoreInterface]: def get_iface(self, nem_id: int) -> Optional[CoreInterface]:
return self.nems_to_ifaces.get(nem_id) return self.nems_to_ifaces.get(nem_id)
@ -445,7 +425,7 @@ class EmaneManager:
logger.exception("error writing to emane nem file") logger.exception("error writing to emane nem file")
def links_enabled(self) -> bool: def links_enabled(self) -> bool:
return self.config["link_enabled"] == "1" return self.session.options.get_config_int("link_enabled") == 1
def poststartup(self) -> None: def poststartup(self) -> None:
""" """
@ -551,35 +531,6 @@ class EmaneManager:
color=color, color=color,
) )
def buildeventservicexml(self) -> None:
"""
Build the libemaneeventservice.xml file if event service options
were changed in the global config.
"""
need_xml = False
default_values = self.emane_config.default_values()
for name in ["eventservicegroup", "eventservicedevice"]:
a = default_values[name]
b = self.config[name]
if a != b:
need_xml = True
if not need_xml:
# reset to using default config
self.initeventservice()
return
try:
group, port = self.config["eventservicegroup"].split(":")
except ValueError:
logger.exception("invalid eventservicegroup in EMANE config")
return
dev = self.config["eventservicedevice"]
emanexml.create_event_service_xml(group, port, dev, self.session.directory)
self.session.distributed.execute(
lambda x: emanexml.create_event_service_xml(
group, port, dev, self.session.directory, x
)
)
def start_daemon(self, node: CoreNodeBase, iface: CoreInterface) -> None: def start_daemon(self, node: CoreNodeBase, iface: CoreInterface) -> None:
""" """
Start one EMANE daemon per node having a radio. Start one EMANE daemon per node having a radio.
@ -648,17 +599,7 @@ class EmaneManager:
""" """
Start monitoring EMANE location events if configured to do so. Start monitoring EMANE location events if configured to do so.
""" """
logger.info("emane start event monitor") logger.info("starting emane event monitor")
if not self.doeventmonitor():
return
if self.service is None:
logger.error(
"Warning: EMANE events will not be generated "
"because the emaneeventservice\n binding was "
"unable to load "
"(install the python-emaneeventservice bindings)"
)
return
self.doeventloop = True self.doeventloop = True
self.eventmonthread = threading.Thread( self.eventmonthread = threading.Thread(
target=self.eventmonitorloop, daemon=True target=self.eventmonitorloop, daemon=True
@ -673,8 +614,7 @@ class EmaneManager:
if self.service is not None: if self.service is not None:
self.service.breakloop() self.service.breakloop()
# reset the service, otherwise nextEvent won"t work # reset the service, otherwise nextEvent won"t work
self.initeventservice(shutdown=True) self.deleteeventservice()
if self.eventmonthread is not None: if self.eventmonthread is not None:
self.eventmonthread.join() self.eventmonthread.join()
self.eventmonthread = None self.eventmonthread = None
@ -685,26 +625,17 @@ class EmaneManager:
""" """
if self.service is None: if self.service is None:
return return
logger.info( logger.info("subscribing to EMANE location events")
"subscribing to EMANE location events. (%s)", while self.doeventloop:
threading.currentThread().getName(),
)
while self.doeventloop is True:
_uuid, _seq, events = self.service.nextEvent() _uuid, _seq, events = self.service.nextEvent()
# this occurs with 0.9.1 event service # this occurs with 0.9.1 event service
if not self.doeventloop: if not self.doeventloop:
break break
for event in events: for event in events:
nem, eid, data = event nem, eid, data = event
if eid == LocationEvent.IDENTIFIER: if eid == LocationEvent.IDENTIFIER:
self.handlelocationevent(nem, eid, data) self.handlelocationevent(nem, eid, data)
logger.info("unsubscribing from EMANE location events")
logger.info(
"unsubscribing from EMANE location events. (%s)",
threading.currentThread().getName(),
)
def handlelocationevent(self, rxnemid: int, eid: int, data: str) -> None: def handlelocationevent(self, rxnemid: int, eid: int, data: str) -> None:
""" """
@ -818,85 +749,3 @@ class EmaneManager:
event.append(nem2, forward=rx2) event.append(nem2, forward=rx2)
self.service.publish(nem1, event) self.service.publish(nem1, event)
self.service.publish(nem2, event) self.service.publish(nem2, event)
class EmaneGlobalModel:
"""
Global EMANE configuration options.
"""
name: str = "emane"
bitmap: Optional[str] = None
def __init__(self, session: "Session") -> None:
self.session: "Session" = session
self.core_config: List[Configuration] = [
Configuration(
id="platform_id_start",
type=ConfigDataTypes.INT32,
default="1",
label="Starting Platform ID",
),
Configuration(
id="nem_id_start",
type=ConfigDataTypes.INT32,
default="1",
label="Starting NEM ID",
),
Configuration(
id="link_enabled",
type=ConfigDataTypes.BOOL,
default="1",
label="Enable Links?",
),
Configuration(
id="loss_threshold",
type=ConfigDataTypes.INT32,
default="30",
label="Link Loss Threshold (%)",
),
Configuration(
id="link_interval",
type=ConfigDataTypes.INT32,
default="1",
label="Link Check Interval (sec)",
),
Configuration(
id="link_timeout",
type=ConfigDataTypes.INT32,
default="4",
label="Link Timeout (sec)",
),
]
self.emulator_config = None
self.parse_config()
def parse_config(self) -> None:
emane_prefix = self.session.options.get_config(
"emane_prefix", default=DEFAULT_EMANE_PREFIX
)
emane_prefix = Path(emane_prefix)
emulator_xml = emane_prefix / "share/emane/manifest/nemmanager.xml"
emulator_defaults = {
"eventservicedevice": DEFAULT_DEV,
"eventservicegroup": "224.1.2.8:45703",
"otamanagerdevice": DEFAULT_DEV,
"otamanagergroup": "224.1.2.8:45702",
}
self.emulator_config = emanemanifest.parse(emulator_xml, emulator_defaults)
def configurations(self) -> List[Configuration]:
return self.emulator_config + self.core_config
def config_groups(self) -> List[ConfigGroup]:
emulator_len = len(self.emulator_config)
config_len = len(self.configurations())
return [
ConfigGroup("Platform Attributes", 1, emulator_len),
ConfigGroup("CORE Configuration", emulator_len + 1, config_len),
]
def default_values(self) -> Dict[str, str]:
return OrderedDict(
[(config.id, config.default) for config in self.configurations()]
)

View file

@ -189,9 +189,10 @@ class EmaneLinkMonitor:
self.running: bool = False self.running: bool = False
def start(self) -> None: def start(self) -> None:
self.loss_threshold = int(self.emane_manager.config["loss_threshold"]) options = self.emane_manager.session.options
self.link_interval = int(self.emane_manager.config["link_interval"]) self.loss_threshold = options.get_config_int("loss_threshold")
self.link_timeout = int(self.emane_manager.config["link_timeout"]) self.link_interval = options.get_config_int("link_interval")
self.link_timeout = options.get_config_int("link_timeout")
self.initialize() self.initialize()
if not self.clients: if not self.clients:
logger.info("no valid emane models to monitor links") logger.info("no valid emane models to monitor links")

View file

@ -38,7 +38,7 @@ class LinuxNetClient:
:param device: device to add route to :param device: device to add route to
:return: nothing :return: nothing
""" """
self.run(f"{IP} route add {route} dev {device}") self.run(f"{IP} route replace {route} dev {device}")
def device_up(self, device: str) -> None: def device_up(self, device: str) -> None:
""" """

View file

@ -80,20 +80,6 @@ def create_iface_data(iface_element: etree.Element) -> InterfaceData:
) )
def create_emane_config(session: "Session") -> etree.Element:
emane_configuration = etree.Element("emane_global_configuration")
config = session.emane.config
emulator_element = etree.SubElement(emane_configuration, "emulator")
for emulator_config in session.emane.emane_config.emulator_config:
value = config[emulator_config.id]
add_configuration(emulator_element, emulator_config.id, value)
core_element = etree.SubElement(emane_configuration, "core")
for core_config in session.emane.emane_config.core_config:
value = config[core_config.id]
add_configuration(core_element, core_config.id, value)
return emane_configuration
def create_emane_model_config( def create_emane_model_config(
node_id: int, node_id: int,
model: "EmaneModelType", model: "EmaneModelType",
@ -104,22 +90,22 @@ def create_emane_model_config(
add_attribute(emane_element, "node", node_id) add_attribute(emane_element, "node", node_id)
add_attribute(emane_element, "iface", iface_id) add_attribute(emane_element, "iface", iface_id)
add_attribute(emane_element, "model", model.name) add_attribute(emane_element, "model", model.name)
platform_element = etree.SubElement(emane_element, "platform")
for platform_config in model.platform_config:
value = config[platform_config.id]
add_configuration(platform_element, platform_config.id, value)
mac_element = etree.SubElement(emane_element, "mac") mac_element = etree.SubElement(emane_element, "mac")
for mac_config in model.mac_config: for mac_config in model.mac_config:
value = config[mac_config.id] value = config[mac_config.id]
add_configuration(mac_element, mac_config.id, value) add_configuration(mac_element, mac_config.id, value)
phy_element = etree.SubElement(emane_element, "phy") phy_element = etree.SubElement(emane_element, "phy")
for phy_config in model.phy_config: for phy_config in model.phy_config:
value = config[phy_config.id] value = config[phy_config.id]
add_configuration(phy_element, phy_config.id, value) add_configuration(phy_element, phy_config.id, value)
external_element = etree.SubElement(emane_element, "external") external_element = etree.SubElement(emane_element, "external")
for external_config in model.external_config: for external_config in model.external_config:
value = config[external_config.id] value = config[external_config.id]
add_configuration(external_element, external_config.id, value) add_configuration(external_element, external_config.id, value)
return emane_element return emane_element
@ -376,8 +362,6 @@ class CoreXmlWriter:
self.scenario.append(metadata_elements) self.scenario.append(metadata_elements)
def write_emane_configs(self) -> None: def write_emane_configs(self) -> None:
emane_global_configuration = create_emane_config(self.session)
self.scenario.append(emane_global_configuration)
emane_configurations = etree.Element("emane_configurations") emane_configurations = etree.Element("emane_configurations")
for node_id, model_configs in self.session.emane.node_configs.items(): for node_id, model_configs in self.session.emane.node_configs.items():
node_id, iface_id = utils.parse_iface_config_id(node_id) node_id, iface_id = utils.parse_iface_config_id(node_id)
@ -591,7 +575,6 @@ class CoreXmlReader:
self.read_session_origin() self.read_session_origin()
self.read_service_configs() self.read_service_configs()
self.read_mobility_configs() self.read_mobility_configs()
self.read_emane_global_config()
self.read_nodes() self.read_nodes()
self.read_links() self.read_links()
self.read_emane_configs() self.read_emane_configs()
@ -729,28 +712,10 @@ class CoreXmlReader:
files.add(name) files.add(name)
service.configs = tuple(files) service.configs = tuple(files)
def read_emane_global_config(self) -> None:
emane_global_configuration = self.scenario.find("emane_global_configuration")
if emane_global_configuration is None:
return
emulator_configuration = emane_global_configuration.find("emulator")
configs = {}
for config in emulator_configuration.iterchildren():
name = config.get("name")
value = config.get("value")
configs[name] = value
core_configuration = emane_global_configuration.find("core")
for config in core_configuration.iterchildren():
name = config.get("name")
value = config.get("value")
configs[name] = value
self.session.emane.config = configs
def read_emane_configs(self) -> None: def read_emane_configs(self) -> None:
emane_configurations = self.scenario.find("emane_configurations") emane_configurations = self.scenario.find("emane_configurations")
if emane_configurations is None: if emane_configurations is None:
return return
for emane_configuration in emane_configurations.iterchildren(): for emane_configuration in emane_configurations.iterchildren():
node_id = get_int(emane_configuration, "node") node_id = get_int(emane_configuration, "node")
iface_id = get_int(emane_configuration, "iface") iface_id = get_int(emane_configuration, "iface")
@ -768,18 +733,21 @@ class CoreXmlReader:
) )
# read and set emane model configuration # read and set emane model configuration
platform_configuration = emane_configuration.find("platform")
for config in platform_configuration.iterchildren():
name = config.get("name")
value = config.get("value")
configs[name] = value
mac_configuration = emane_configuration.find("mac") mac_configuration = emane_configuration.find("mac")
for config in mac_configuration.iterchildren(): for config in mac_configuration.iterchildren():
name = config.get("name") name = config.get("name")
value = config.get("value") value = config.get("value")
configs[name] = value configs[name] = value
phy_configuration = emane_configuration.find("phy") phy_configuration = emane_configuration.find("phy")
for config in phy_configuration.iterchildren(): for config in phy_configuration.iterchildren():
name = config.get("name") name = config.get("name")
value = config.get("value") value = config.get("value")
configs[name] = value configs[name] = value
external_configuration = emane_configuration.find("external") external_configuration = emane_configuration.find("external")
for config in external_configuration.iterchildren(): for config in external_configuration.iterchildren():
name = config.get("name") name = config.get("name")

View file

@ -162,7 +162,7 @@ def build_platform_xml(
:param iface: node interface to create platform xml for :param iface: node interface to create platform xml for
:return: the next nem id that can be used for creating platform xml files :return: the next nem id that can be used for creating platform xml files
""" """
# create nem xml entries for all interfaces # create model based xml files
emane_net = iface.net emane_net = iface.net
if not isinstance(emane_net, EmaneNet): if not isinstance(emane_net, EmaneNet):
raise CoreError(f"emane interface not connected to emane net: {emane_net.name}") raise CoreError(f"emane interface not connected to emane net: {emane_net.name}")
@ -173,12 +173,12 @@ def build_platform_xml(
# create top level platform element # create top level platform element
transport_configs = {"otamanagerdevice", "eventservicedevice"} transport_configs = {"otamanagerdevice", "eventservicedevice"}
platform_element = etree.Element("platform") platform_element = etree.Element("platform")
for configuration in emane_manager.emane_config.emulator_config: for configuration in emane_net.model.platform_config:
name = configuration.id name = configuration.id
if not isinstance(node, CoreNode) and name in transport_configs: if not isinstance(node, CoreNode) and name in transport_configs:
value = control_net.brname value = control_net.brname
else: else:
value = emane_manager.config[name] value = config[configuration.id]
if name == "controlportendpoint": if name == "controlportendpoint":
port = emane_manager.get_nem_port(iface) port = emane_manager.get_nem_port(iface)
value = f"0.0.0.0:{port}" value = f"0.0.0.0:{port}"

View file

@ -30,8 +30,9 @@ iface1 = iface_helper.create_iface(node2.id, 0)
session.add_link(node1=node2, node2=emane, iface1=iface1) session.add_link(node1=node2, node2=emane, iface1=iface1)
# setup emane configurations using a dict mapping currently support values as strings # setup emane configurations using a dict mapping currently support values as strings
session.set_emane({"eventservicettl": "2"}) emane.set_emane_model(
emane.set_emane_model(EmaneIeee80211abgModel.name, {"unicastrate": "3"}) EmaneIeee80211abgModel.name, {"eventservicettl": "2", "unicastrate": "3"}
)
# start session # start session
core.start_session(session) core.start_session(session)

View file

@ -95,10 +95,6 @@ service CoreApi {
} }
// emane rpc // emane rpc
rpc GetEmaneConfig (emane.GetEmaneConfigRequest) returns (emane.GetEmaneConfigResponse) {
}
rpc SetEmaneConfig (emane.SetEmaneConfigRequest) returns (emane.SetEmaneConfigResponse) {
}
rpc GetEmaneModelConfig (emane.GetEmaneModelConfigRequest) returns (emane.GetEmaneModelConfigResponse) { rpc GetEmaneModelConfig (emane.GetEmaneModelConfigRequest) returns (emane.GetEmaneModelConfigResponse) {
} }
rpc SetEmaneModelConfig (emane.SetEmaneModelConfigRequest) returns (emane.SetEmaneModelConfigResponse) { rpc SetEmaneModelConfig (emane.SetEmaneModelConfigRequest) returns (emane.SetEmaneModelConfigResponse) {
@ -144,19 +140,18 @@ message StartSessionRequest {
repeated Link links = 3; repeated Link links = 3;
repeated Hook hooks = 4; repeated Hook hooks = 4;
SessionLocation location = 5; SessionLocation location = 5;
map<string, string> emane_config = 6; repeated wlan.WlanConfig wlan_configs = 6;
repeated wlan.WlanConfig wlan_configs = 7; repeated emane.EmaneModelConfig emane_model_configs = 7;
repeated emane.EmaneModelConfig emane_model_configs = 8; repeated mobility.MobilityConfig mobility_configs = 8;
repeated mobility.MobilityConfig mobility_configs = 9; repeated services.ServiceConfig service_configs = 9;
repeated services.ServiceConfig service_configs = 10; repeated services.ServiceFileConfig service_file_configs = 10;
repeated services.ServiceFileConfig service_file_configs = 11; repeated Link asymmetric_links = 11;
repeated Link asymmetric_links = 12; repeated configservices.ConfigServiceConfig config_service_configs = 12;
repeated configservices.ConfigServiceConfig config_service_configs = 13; map<string, string> options = 13;
map<string, string> options = 14; string user = 14;
string user = 15; bool definition = 15;
bool definition = 16; map<string, string> metadata = 16;
map<string, string> metadata = 17; repeated Server servers = 17;
repeated Server servers = 18;
} }
message StartSessionResponse { message StartSessionResponse {

View file

@ -4,23 +4,6 @@ package emane;
import "core/api/grpc/common.proto"; import "core/api/grpc/common.proto";
message GetEmaneConfigRequest {
int32 session_id = 1;
}
message GetEmaneConfigResponse {
map<string, common.ConfigOption> config = 1;
}
message SetEmaneConfigRequest {
int32 session_id = 1;
map<string, string> config = 2;
}
message SetEmaneConfigResponse {
bool result = 1;
}
message GetEmaneModelConfigRequest { message GetEmaneModelConfigRequest {
int32 session_id = 1; int32 session_id = 1;
int32 node_id = 2; int32 node_id = 2;

View file

@ -61,6 +61,7 @@ eval "$ifcommand" | awk '
/tmp\./ {print "removing interface " $1; system("ip link del " $1);} /tmp\./ {print "removing interface " $1; system("ip link del " $1);}
/gt\./ {print "removing interface " $1; system("ip link del " $1);} /gt\./ {print "removing interface " $1; system("ip link del " $1);}
/b\./ {print "removing bridge " $1; system("ip link set " $1 " down; ip link del " $1);} /b\./ {print "removing bridge " $1; system("ip link set " $1 " down; ip link del " $1);}
/ctrl[0-9]+\./ {print "removing bridge " $1; system("ip link set " $1 " down; ip link del " $1);}
' '
nft list ruleset | awk ' nft list ruleset | awk '

View file

@ -83,11 +83,6 @@ class TestGrpc:
scale=location_scale, scale=location_scale,
) )
# setup global emane config
emane_config_key = "platform_id_start"
emane_config_value = "2"
session.set_emane({emane_config_key: emane_config_value})
# setup wlan config # setup wlan config
wlan_config_key = "range" wlan_config_key = "range"
wlan_config_value = "333" wlan_config_value = "333"
@ -151,7 +146,6 @@ class TestGrpc:
location_alt, location_alt,
) )
assert real_session.location.refscale == location_scale assert real_session.location.refscale == location_scale
assert real_session.emane.config[emane_config_key] == emane_config_value
set_wlan_config = real_session.mobility.get_model_config( set_wlan_config = real_session.mobility.get_model_config(
wlan_node.id, BasicRangeModel.name wlan_node.id, BasicRangeModel.name
) )
@ -518,35 +512,6 @@ class TestGrpc:
assert config[range_key] == range_value assert config[range_key] == range_value
assert wlan.model.range == int(range_value) assert wlan.model.range == int(range_value)
def test_get_emane_config(self, grpc_server: CoreGrpcServer):
# given
client = CoreGrpcClient()
session = grpc_server.coreemu.create_session()
# then
with client.context_connect():
config = client.get_emane_config(session.id)
# then
assert len(config) > 0
def test_set_emane_config(self, grpc_server: CoreGrpcServer):
# given
client = CoreGrpcClient()
session = grpc_server.coreemu.create_session()
config_key = "platform_id_start"
config_value = "2"
# then
with client.context_connect():
result = client.set_emane_config(session.id, {config_key: config_value})
# then
assert result is True
config = session.emane.config
assert len(config) > 1
assert config[config_key] == config_value
def test_set_emane_model_config(self, grpc_server: CoreGrpcServer): def test_set_emane_model_config(self, grpc_server: CoreGrpcServer):
# given # given
client = CoreGrpcClient() client = CoreGrpcClient()

View file

@ -941,35 +941,3 @@ class TestGui:
config = coretlv.session.emane.get_config(wlan.id, EmaneIeee80211abgModel.name) config = coretlv.session.emane.get_config(wlan.id, EmaneIeee80211abgModel.name)
assert config[config_key] == config_value assert config[config_key] == config_value
def test_config_emane_request(self, coretlv: CoreHandler):
message = coreapi.CoreConfMessage.create(
0,
[
(ConfigTlvs.OBJECT, "emane"),
(ConfigTlvs.TYPE, ConfigFlags.REQUEST.value),
],
)
coretlv.handle_broadcast_config = mock.MagicMock()
coretlv.handle_message(message)
coretlv.handle_broadcast_config.assert_called_once()
def test_config_emane_update(self, coretlv: CoreHandler):
config_key = "eventservicedevice"
config_value = "eth4"
values = {config_key: config_value}
message = coreapi.CoreConfMessage.create(
0,
[
(ConfigTlvs.OBJECT, "emane"),
(ConfigTlvs.TYPE, ConfigFlags.UPDATE.value),
(ConfigTlvs.VALUES, dict_to_str(values)),
],
)
coretlv.handle_message(message)
config = coretlv.session.emane.config
assert config[config_key] == config_value

View file

@ -328,10 +328,11 @@ session.add_link(node1=node1, node2=emane, iface1=iface1)
iface1 = iface_helper.create_iface(node2.id, 0) iface1 = iface_helper.create_iface(node2.id, 0)
session.add_link(node1=node2, node2=emane, iface1=iface1) session.add_link(node1=node2, node2=emane, iface1=iface1)
# setting global emane configuration
session.set_emane({"eventservicettl": "2"})
# setting emane specific emane model configuration # setting emane specific emane model configuration
emane.set_emane_model(EmaneIeee80211abgModel.name, {"unicastrate": "3"}) emane.set_emane_model(EmaneIeee80211abgModel.name, {
"eventservicettl": "2",
"unicastrate": "3",
})
# start session # start session
core.start_session(session) core.start_session(session)