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:
parent
071023b1d9
commit
5bc3345d37
17 changed files with 98 additions and 483 deletions
|
@ -28,10 +28,8 @@ from core.api.grpc.configservices_pb2 import (
|
|||
from core.api.grpc.core_pb2 import ExecuteScriptRequest, GetConfigRequest
|
||||
from core.api.grpc.emane_pb2 import (
|
||||
EmaneLinkRequest,
|
||||
GetEmaneConfigRequest,
|
||||
GetEmaneEventChannelRequest,
|
||||
GetEmaneModelConfigRequest,
|
||||
SetEmaneConfigRequest,
|
||||
SetEmaneModelConfigRequest,
|
||||
)
|
||||
from core.api.grpc.mobility_pb2 import (
|
||||
|
@ -253,7 +251,6 @@ class CoreGrpcClient:
|
|||
if asymmetric_links:
|
||||
asymmetric_links = [x.to_proto() for x in asymmetric_links]
|
||||
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 = []
|
||||
mobility_configs = []
|
||||
wlan_configs = []
|
||||
|
@ -313,7 +310,6 @@ class CoreGrpcClient:
|
|||
links=links,
|
||||
location=session.location.to_proto(),
|
||||
hooks=hooks,
|
||||
emane_config=emane_config,
|
||||
emane_model_configs=emane_model_configs,
|
||||
wlan_configs=wlan_configs,
|
||||
mobility_configs=mobility_configs,
|
||||
|
@ -917,31 +913,6 @@ class CoreGrpcClient:
|
|||
response = self.stub.SetWlanConfig(request)
|
||||
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(
|
||||
self, session_id: int, node_id: int, model: str, iface_id: int = -1
|
||||
) -> Dict[str, wrappers.ConfigOption]:
|
||||
|
|
|
@ -630,10 +630,6 @@ def get_node_config_service_configs(session: Session) -> List[ConfigServiceConfi
|
|||
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(
|
||||
session: Session, node_id: int, context: ServicerContext
|
||||
) -> 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
|
||||
)
|
||||
hooks = get_hooks(session)
|
||||
emane_config = get_emane_config(session)
|
||||
emane_model_configs = get_emane_model_configs(session)
|
||||
wlan_configs = get_wlan_configs(session)
|
||||
mobility_configs = get_mobility_configs(session)
|
||||
|
@ -685,7 +680,6 @@ def convert_session(session: Session) -> wrappers.Session:
|
|||
default_services=default_services,
|
||||
location=location,
|
||||
hooks=hooks,
|
||||
emane_config=emane_config,
|
||||
emane_model_configs=emane_model_configs,
|
||||
wlan_configs=wlan_configs,
|
||||
service_configs=service_configs,
|
||||
|
|
|
@ -33,14 +33,10 @@ from core.api.grpc.emane_pb2 import (
|
|||
EmaneLinkResponse,
|
||||
EmanePathlossesRequest,
|
||||
EmanePathlossesResponse,
|
||||
GetEmaneConfigRequest,
|
||||
GetEmaneConfigResponse,
|
||||
GetEmaneEventChannelRequest,
|
||||
GetEmaneEventChannelResponse,
|
||||
GetEmaneModelConfigRequest,
|
||||
GetEmaneModelConfigResponse,
|
||||
SetEmaneConfigRequest,
|
||||
SetEmaneConfigResponse,
|
||||
SetEmaneModelConfigRequest,
|
||||
SetEmaneModelConfigResponse,
|
||||
)
|
||||
|
@ -266,7 +262,6 @@ class CoreGrpcServer(core_pb2_grpc.CoreApiServicer):
|
|||
return core_pb2.StartSessionResponse(result=False, exceptions=exceptions)
|
||||
|
||||
# emane configs
|
||||
session.emane.config.update(request.emane_config)
|
||||
for config in request.emane_model_configs:
|
||||
_id = utils.iface_config_id(config.node_id, config.iface_id)
|
||||
session.emane.set_config(_id, config.model, config.config)
|
||||
|
@ -1045,36 +1040,6 @@ class CoreGrpcServer(core_pb2_grpc.CoreApiServicer):
|
|||
node.updatemodel(config)
|
||||
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(
|
||||
self, request: GetEmaneModelConfigRequest, context: ServicerContext
|
||||
) -> GetEmaneModelConfigResponse:
|
||||
|
|
|
@ -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
|
||||
)
|
||||
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)
|
||||
file: Path = None
|
||||
options: Dict[str, ConfigOption] = field(default_factory=dict)
|
||||
|
@ -836,7 +835,6 @@ class Session:
|
|||
default_services=default_services,
|
||||
location=SessionLocation.from_proto(proto.location),
|
||||
hooks=hooks,
|
||||
emane_config=ConfigOption.from_dict(proto.emane_config),
|
||||
metadata=dict(proto.metadata),
|
||||
file=file_path,
|
||||
options=options,
|
||||
|
@ -889,11 +887,6 @@ class Session:
|
|||
self.links.append(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:
|
||||
for key, value in config.items():
|
||||
option = ConfigOption(name=key, value=value)
|
||||
|
|
|
@ -1046,8 +1046,6 @@ class CoreHandler(socketserver.BaseRequestHandler):
|
|||
self.handle_config_mobility(message_type, config_data)
|
||||
elif config_data.object in self.session.mobility.models:
|
||||
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:
|
||||
replies = self.handle_config_emane_models(message_type, config_data)
|
||||
else:
|
||||
|
@ -1379,36 +1377,6 @@ class CoreHandler(socketserver.BaseRequestHandler):
|
|||
|
||||
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):
|
||||
replies = []
|
||||
node_id = config_data.node
|
||||
|
@ -1851,14 +1819,6 @@ class CoreHandler(socketserver.BaseRequestHandler):
|
|||
)
|
||||
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
|
||||
for node_id, model_configs in self.session.emane.node_configs.items():
|
||||
for model_name, config in model_configs.items():
|
||||
|
|
|
@ -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 os
|
||||
import threading
|
||||
from collections import OrderedDict
|
||||
from dataclasses import dataclass, field
|
||||
from enum import Enum
|
||||
from pathlib import Path
|
||||
from typing import TYPE_CHECKING, Dict, List, Optional, Set, Tuple, Type
|
||||
|
||||
from core import utils
|
||||
from core.config import ConfigGroup, Configuration
|
||||
from core.emane import emanemanifest
|
||||
from core.emane.emanemodel import EmaneModel
|
||||
from core.emane.linkmonitor import EmaneLinkMonitor
|
||||
from core.emane.modelmanager import EmaneModelManager
|
||||
from core.emane.nodes import EmaneNet
|
||||
from core.emulator.data import LinkData
|
||||
from core.emulator.enumerations import (
|
||||
ConfigDataTypes,
|
||||
LinkTypes,
|
||||
MessageFlags,
|
||||
RegisterTlvs,
|
||||
)
|
||||
from core.emulator.enumerations import LinkTypes, MessageFlags, RegisterTlvs
|
||||
from core.errors import CoreCommandError, CoreError
|
||||
from core.nodes.base import CoreNetworkBase, CoreNode, CoreNodeBase, NodeBase
|
||||
from core.nodes.interface import CoreInterface, TunTap
|
||||
|
@ -102,8 +93,6 @@ class EmaneManager:
|
|||
self.eventmonthread: Optional[threading.Thread] = None
|
||||
|
||||
# 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_models: Dict[int, str] = {}
|
||||
|
||||
|
@ -115,7 +104,7 @@ class EmaneManager:
|
|||
self.event_device: Optional[str] = None
|
||||
|
||||
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:
|
||||
nem_id += 1
|
||||
self.nems_to_ifaces[nem_id] = iface
|
||||
|
@ -206,7 +195,6 @@ class EmaneManager:
|
|||
|
||||
def config_reset(self, node_id: int = None) -> None:
|
||||
if node_id is None:
|
||||
self.config = self.emane_config.default_values()
|
||||
self.node_configs.clear()
|
||||
self.node_models.clear()
|
||||
else:
|
||||
|
@ -224,25 +212,20 @@ class EmaneManager:
|
|||
self.service = 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.
|
||||
The multicast group and/or port may be configured.
|
||||
"""
|
||||
self.deleteeventservice()
|
||||
if shutdown:
|
||||
return
|
||||
|
||||
# Get the control network to be used for events
|
||||
group, port = self.config["eventservicegroup"].split(":")
|
||||
self.event_device = self.config["eventservicedevice"]
|
||||
group, port = "224.1.2.8:45703".split(":")
|
||||
self.event_device = DEFAULT_DEV
|
||||
eventnetidx = self.session.get_control_net_index(self.event_device)
|
||||
if eventnetidx < 0:
|
||||
logger.error(
|
||||
"invalid emane event service device provided: %s", self.event_device
|
||||
)
|
||||
return
|
||||
|
||||
# make sure the event control network is in place
|
||||
eventnet = self.session.add_remove_control_net(
|
||||
net_index=eventnetidx, remove=False, conf_required=False
|
||||
|
@ -251,14 +234,13 @@ class EmaneManager:
|
|||
# direct EMANE events towards control net bridge
|
||||
self.event_device = eventnet.brname
|
||||
self.eventchannel = (group, int(port), self.event_device)
|
||||
|
||||
# disabled otachannel for event service
|
||||
# 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:
|
||||
self.service = EventService(eventchannel=self.eventchannel, otachannel=None)
|
||||
except EventServiceException:
|
||||
logger.exception("error instantiating emane EventService")
|
||||
logger.exception("error starting emane event service")
|
||||
|
||||
def add_node(self, emane_net: EmaneNet) -> None:
|
||||
"""
|
||||
|
@ -309,9 +291,8 @@ class EmaneManager:
|
|||
if EventService is None:
|
||||
raise CoreError("EMANE python bindings are not installed")
|
||||
|
||||
# control network bridge required for EMANE 0.9.2
|
||||
# - needs to exist when eventservice binds to it (initeventservice)
|
||||
otadev = self.config["otamanagerdevice"]
|
||||
# control network bridge required for emane
|
||||
otadev = DEFAULT_DEV
|
||||
netidx = self.session.get_control_net_index(otadev)
|
||||
logger.debug("emane ota manager device: index(%s) otadev(%s)", netidx, otadev)
|
||||
if netidx < 0:
|
||||
|
@ -320,25 +301,9 @@ class EmaneManager:
|
|||
otadev,
|
||||
)
|
||||
return EmaneState.NOT_READY
|
||||
|
||||
self.session.add_remove_control_net(
|
||||
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()
|
||||
return EmaneState.SUCCESS
|
||||
|
||||
|
@ -354,8 +319,9 @@ class EmaneManager:
|
|||
status = self.setup()
|
||||
if status != EmaneState.SUCCESS:
|
||||
return status
|
||||
self.starteventmonitor()
|
||||
self.buildeventservicexml()
|
||||
self.initeventservice()
|
||||
if self.service and self.doeventmonitor():
|
||||
self.starteventmonitor()
|
||||
with self._emane_node_lock:
|
||||
logger.info("emane building xmls...")
|
||||
start_data = self.get_start_data()
|
||||
|
@ -392,40 +358,54 @@ class EmaneManager:
|
|||
control_net = self.session.add_remove_control_net(
|
||||
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
|
||||
for iface in data.ifaces:
|
||||
if isinstance(node, CoreNode):
|
||||
self.setup_ota(node, iface)
|
||||
emanexml.build_platform_xml(self, control_net, node, iface)
|
||||
self.start_daemon(node, 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]:
|
||||
return self.nems_to_ifaces.get(nem_id)
|
||||
|
||||
|
@ -445,7 +425,7 @@ class EmaneManager:
|
|||
logger.exception("error writing to emane nem file")
|
||||
|
||||
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:
|
||||
"""
|
||||
|
@ -551,35 +531,6 @@ class EmaneManager:
|
|||
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:
|
||||
"""
|
||||
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.
|
||||
"""
|
||||
logger.info("emane start 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
|
||||
logger.info("starting emane event monitor")
|
||||
self.doeventloop = True
|
||||
self.eventmonthread = threading.Thread(
|
||||
target=self.eventmonitorloop, daemon=True
|
||||
|
@ -673,8 +614,7 @@ class EmaneManager:
|
|||
if self.service is not None:
|
||||
self.service.breakloop()
|
||||
# reset the service, otherwise nextEvent won"t work
|
||||
self.initeventservice(shutdown=True)
|
||||
|
||||
self.deleteeventservice()
|
||||
if self.eventmonthread is not None:
|
||||
self.eventmonthread.join()
|
||||
self.eventmonthread = None
|
||||
|
@ -685,26 +625,17 @@ class EmaneManager:
|
|||
"""
|
||||
if self.service is None:
|
||||
return
|
||||
logger.info(
|
||||
"subscribing to EMANE location events. (%s)",
|
||||
threading.currentThread().getName(),
|
||||
)
|
||||
while self.doeventloop is True:
|
||||
logger.info("subscribing to EMANE location events")
|
||||
while self.doeventloop:
|
||||
_uuid, _seq, events = self.service.nextEvent()
|
||||
|
||||
# this occurs with 0.9.1 event service
|
||||
if not self.doeventloop:
|
||||
break
|
||||
|
||||
for event in events:
|
||||
nem, eid, data = event
|
||||
if eid == LocationEvent.IDENTIFIER:
|
||||
self.handlelocationevent(nem, eid, data)
|
||||
|
||||
logger.info(
|
||||
"unsubscribing from EMANE location events. (%s)",
|
||||
threading.currentThread().getName(),
|
||||
)
|
||||
logger.info("unsubscribing from EMANE location events")
|
||||
|
||||
def handlelocationevent(self, rxnemid: int, eid: int, data: str) -> None:
|
||||
"""
|
||||
|
@ -818,85 +749,3 @@ class EmaneManager:
|
|||
event.append(nem2, forward=rx2)
|
||||
self.service.publish(nem1, 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()]
|
||||
)
|
||||
|
|
|
@ -189,9 +189,10 @@ class EmaneLinkMonitor:
|
|||
self.running: bool = False
|
||||
|
||||
def start(self) -> None:
|
||||
self.loss_threshold = int(self.emane_manager.config["loss_threshold"])
|
||||
self.link_interval = int(self.emane_manager.config["link_interval"])
|
||||
self.link_timeout = int(self.emane_manager.config["link_timeout"])
|
||||
options = self.emane_manager.session.options
|
||||
self.loss_threshold = options.get_config_int("loss_threshold")
|
||||
self.link_interval = options.get_config_int("link_interval")
|
||||
self.link_timeout = options.get_config_int("link_timeout")
|
||||
self.initialize()
|
||||
if not self.clients:
|
||||
logger.info("no valid emane models to monitor links")
|
||||
|
|
|
@ -38,7 +38,7 @@ class LinuxNetClient:
|
|||
:param device: device to add route to
|
||||
: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:
|
||||
"""
|
||||
|
|
|
@ -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(
|
||||
node_id: int,
|
||||
model: "EmaneModelType",
|
||||
|
@ -104,22 +90,22 @@ def create_emane_model_config(
|
|||
add_attribute(emane_element, "node", node_id)
|
||||
add_attribute(emane_element, "iface", iface_id)
|
||||
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")
|
||||
for mac_config in model.mac_config:
|
||||
value = config[mac_config.id]
|
||||
add_configuration(mac_element, mac_config.id, value)
|
||||
|
||||
phy_element = etree.SubElement(emane_element, "phy")
|
||||
for phy_config in model.phy_config:
|
||||
value = config[phy_config.id]
|
||||
add_configuration(phy_element, phy_config.id, value)
|
||||
|
||||
external_element = etree.SubElement(emane_element, "external")
|
||||
for external_config in model.external_config:
|
||||
value = config[external_config.id]
|
||||
add_configuration(external_element, external_config.id, value)
|
||||
|
||||
return emane_element
|
||||
|
||||
|
||||
|
@ -376,8 +362,6 @@ class CoreXmlWriter:
|
|||
self.scenario.append(metadata_elements)
|
||||
|
||||
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")
|
||||
for node_id, model_configs in self.session.emane.node_configs.items():
|
||||
node_id, iface_id = utils.parse_iface_config_id(node_id)
|
||||
|
@ -591,7 +575,6 @@ class CoreXmlReader:
|
|||
self.read_session_origin()
|
||||
self.read_service_configs()
|
||||
self.read_mobility_configs()
|
||||
self.read_emane_global_config()
|
||||
self.read_nodes()
|
||||
self.read_links()
|
||||
self.read_emane_configs()
|
||||
|
@ -729,28 +712,10 @@ class CoreXmlReader:
|
|||
files.add(name)
|
||||
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:
|
||||
emane_configurations = self.scenario.find("emane_configurations")
|
||||
if emane_configurations is None:
|
||||
return
|
||||
|
||||
for emane_configuration in emane_configurations.iterchildren():
|
||||
node_id = get_int(emane_configuration, "node")
|
||||
iface_id = get_int(emane_configuration, "iface")
|
||||
|
@ -768,18 +733,21 @@ class CoreXmlReader:
|
|||
)
|
||||
|
||||
# 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")
|
||||
for config in mac_configuration.iterchildren():
|
||||
name = config.get("name")
|
||||
value = config.get("value")
|
||||
configs[name] = value
|
||||
|
||||
phy_configuration = emane_configuration.find("phy")
|
||||
for config in phy_configuration.iterchildren():
|
||||
name = config.get("name")
|
||||
value = config.get("value")
|
||||
configs[name] = value
|
||||
|
||||
external_configuration = emane_configuration.find("external")
|
||||
for config in external_configuration.iterchildren():
|
||||
name = config.get("name")
|
||||
|
|
|
@ -162,7 +162,7 @@ def build_platform_xml(
|
|||
:param iface: node interface to create platform xml for
|
||||
: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
|
||||
if not isinstance(emane_net, EmaneNet):
|
||||
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
|
||||
transport_configs = {"otamanagerdevice", "eventservicedevice"}
|
||||
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
|
||||
if not isinstance(node, CoreNode) and name in transport_configs:
|
||||
value = control_net.brname
|
||||
else:
|
||||
value = emane_manager.config[name]
|
||||
value = config[configuration.id]
|
||||
if name == "controlportendpoint":
|
||||
port = emane_manager.get_nem_port(iface)
|
||||
value = f"0.0.0.0:{port}"
|
||||
|
|
|
@ -30,8 +30,9 @@ iface1 = iface_helper.create_iface(node2.id, 0)
|
|||
session.add_link(node1=node2, node2=emane, iface1=iface1)
|
||||
|
||||
# setup emane configurations using a dict mapping currently support values as strings
|
||||
session.set_emane({"eventservicettl": "2"})
|
||||
emane.set_emane_model(EmaneIeee80211abgModel.name, {"unicastrate": "3"})
|
||||
emane.set_emane_model(
|
||||
EmaneIeee80211abgModel.name, {"eventservicettl": "2", "unicastrate": "3"}
|
||||
)
|
||||
|
||||
# start session
|
||||
core.start_session(session)
|
||||
|
|
|
@ -95,10 +95,6 @@ service CoreApi {
|
|||
}
|
||||
|
||||
// emane rpc
|
||||
rpc GetEmaneConfig (emane.GetEmaneConfigRequest) returns (emane.GetEmaneConfigResponse) {
|
||||
}
|
||||
rpc SetEmaneConfig (emane.SetEmaneConfigRequest) returns (emane.SetEmaneConfigResponse) {
|
||||
}
|
||||
rpc GetEmaneModelConfig (emane.GetEmaneModelConfigRequest) returns (emane.GetEmaneModelConfigResponse) {
|
||||
}
|
||||
rpc SetEmaneModelConfig (emane.SetEmaneModelConfigRequest) returns (emane.SetEmaneModelConfigResponse) {
|
||||
|
@ -144,19 +140,18 @@ message StartSessionRequest {
|
|||
repeated Link links = 3;
|
||||
repeated Hook hooks = 4;
|
||||
SessionLocation location = 5;
|
||||
map<string, string> emane_config = 6;
|
||||
repeated wlan.WlanConfig wlan_configs = 7;
|
||||
repeated emane.EmaneModelConfig emane_model_configs = 8;
|
||||
repeated mobility.MobilityConfig mobility_configs = 9;
|
||||
repeated services.ServiceConfig service_configs = 10;
|
||||
repeated services.ServiceFileConfig service_file_configs = 11;
|
||||
repeated Link asymmetric_links = 12;
|
||||
repeated configservices.ConfigServiceConfig config_service_configs = 13;
|
||||
map<string, string> options = 14;
|
||||
string user = 15;
|
||||
bool definition = 16;
|
||||
map<string, string> metadata = 17;
|
||||
repeated Server servers = 18;
|
||||
repeated wlan.WlanConfig wlan_configs = 6;
|
||||
repeated emane.EmaneModelConfig emane_model_configs = 7;
|
||||
repeated mobility.MobilityConfig mobility_configs = 8;
|
||||
repeated services.ServiceConfig service_configs = 9;
|
||||
repeated services.ServiceFileConfig service_file_configs = 10;
|
||||
repeated Link asymmetric_links = 11;
|
||||
repeated configservices.ConfigServiceConfig config_service_configs = 12;
|
||||
map<string, string> options = 13;
|
||||
string user = 14;
|
||||
bool definition = 15;
|
||||
map<string, string> metadata = 16;
|
||||
repeated Server servers = 17;
|
||||
}
|
||||
|
||||
message StartSessionResponse {
|
||||
|
|
|
@ -4,23 +4,6 @@ package emane;
|
|||
|
||||
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 {
|
||||
int32 session_id = 1;
|
||||
int32 node_id = 2;
|
||||
|
|
|
@ -61,6 +61,7 @@ eval "$ifcommand" | awk '
|
|||
/tmp\./ {print "removing interface " $1; system("ip link del " $1);}
|
||||
/gt\./ {print "removing interface " $1; system("ip link del " $1);}
|
||||
/b\./ {print "removing bridge " $1; system("ip link set " $1 " down; ip link del " $1);}
|
||||
/ctrl[0-9]+\./ {print "removing bridge " $1; system("ip link set " $1 " down; ip link del " $1);}
|
||||
'
|
||||
|
||||
nft list ruleset | awk '
|
||||
|
|
|
@ -83,11 +83,6 @@ class TestGrpc:
|
|||
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
|
||||
wlan_config_key = "range"
|
||||
wlan_config_value = "333"
|
||||
|
@ -151,7 +146,6 @@ class TestGrpc:
|
|||
location_alt,
|
||||
)
|
||||
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(
|
||||
wlan_node.id, BasicRangeModel.name
|
||||
)
|
||||
|
@ -518,35 +512,6 @@ class TestGrpc:
|
|||
assert config[range_key] == 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):
|
||||
# given
|
||||
client = CoreGrpcClient()
|
||||
|
|
|
@ -941,35 +941,3 @@ class TestGui:
|
|||
|
||||
config = coretlv.session.emane.get_config(wlan.id, EmaneIeee80211abgModel.name)
|
||||
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
|
||||
|
|
|
@ -328,10 +328,11 @@ session.add_link(node1=node1, node2=emane, iface1=iface1)
|
|||
iface1 = iface_helper.create_iface(node2.id, 0)
|
||||
session.add_link(node1=node2, node2=emane, iface1=iface1)
|
||||
|
||||
# setting global emane configuration
|
||||
session.set_emane({"eventservicettl": "2"})
|
||||
# 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
|
||||
core.start_session(session)
|
||||
|
|
Loading…
Reference in a new issue