daemon: refactoring to load emane models one time during startup, updates to account for this change

This commit is contained in:
Blake Harnden 2021-05-07 10:40:18 -07:00
parent 1ddb7b7b24
commit 50e3aadc6b
32 changed files with 271 additions and 332 deletions

View file

@ -31,7 +31,6 @@ from core.api.grpc.emane_pb2 import (
GetEmaneConfigRequest,
GetEmaneEventChannelRequest,
GetEmaneModelConfigRequest,
GetEmaneModelsRequest,
SetEmaneConfigRequest,
SetEmaneModelConfigRequest,
)
@ -943,18 +942,6 @@ class CoreGrpcClient:
response = self.stub.SetEmaneConfig(request)
return response.result
def get_emane_models(self, session_id: int) -> List[str]:
"""
Get session emane models.
:param session_id: session id
:return: list of emane models
:raises grpc.RpcError: when session doesn't exist
"""
request = GetEmaneModelsRequest(session_id=session_id)
response = self.stub.GetEmaneModels(request)
return list(response.models)
def get_emane_model_config(
self, session_id: int, node_id: int, model: str, iface_id: int = -1
) -> Dict[str, wrappers.ConfigOption]:

View file

@ -532,14 +532,11 @@ def get_nem_id(
def get_emane_model_configs(session: Session) -> List[GetEmaneModelConfig]:
configs = []
for _id in session.emane.node_configurations:
if _id == -1:
continue
model_configs = session.emane.node_configurations[_id]
for _id, model_configs in session.emane.node_configs.items():
for model_name in model_configs:
model = session.emane.models[model_name]
current_config = session.emane.get_model_config(_id, model_name)
config = get_config_options(current_config, model)
model_class = session.emane.get_model(model_name)
current_config = session.emane.get_config(_id, model_name)
config = get_config_options(current_config, model_class)
node_id, iface_id = utils.parse_iface_config_id(_id)
iface_id = iface_id if iface_id is not None else -1
model_config = GetEmaneModelConfig(
@ -591,15 +588,6 @@ def get_hooks(session: Session) -> List[core_pb2.Hook]:
return hooks
def get_emane_models(session: Session) -> List[str]:
emane_models = []
for model in session.emane.models.keys():
if len(model.split("_")) != 2:
continue
emane_models.append(model)
return emane_models
def get_default_services(session: Session) -> List[ServiceDefaults]:
default_services = []
for name, services in session.services.default_services.items():
@ -643,8 +631,7 @@ def get_node_config_service_configs(session: Session) -> List[ConfigServiceConfi
def get_emane_config(session: Session) -> Dict[str, common_pb2.ConfigOption]:
current_config = session.emane.get_configs()
return get_config_options(current_config, session.emane.emane_config)
return get_config_options(session.emane.config, session.emane.emane_config)
def get_mobility_node(
@ -676,7 +663,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_models = get_emane_models(session)
emane_config = get_emane_config(session)
emane_model_configs = get_emane_model_configs(session)
wlan_configs = get_wlan_configs(session)
@ -699,7 +685,6 @@ def convert_session(session: Session) -> wrappers.Session:
default_services=default_services,
location=location,
hooks=hooks,
emane_models=emane_models,
emane_config=emane_config,
emane_model_configs=emane_model_configs,
wlan_configs=wlan_configs,

View file

@ -39,8 +39,6 @@ from core.api.grpc.emane_pb2 import (
GetEmaneEventChannelResponse,
GetEmaneModelConfigRequest,
GetEmaneModelConfigResponse,
GetEmaneModelsRequest,
GetEmaneModelsResponse,
SetEmaneConfigRequest,
SetEmaneConfigResponse,
SetEmaneModelConfigRequest,
@ -79,6 +77,7 @@ from core.api.grpc.wlan_pb2 import (
WlanLinkRequest,
WlanLinkResponse,
)
from core.emane.modelmanager import EmaneModelManager
from core.emulator.coreemu import CoreEmu
from core.emulator.data import InterfaceData, LinkData, LinkOptions, NodeOptions
from core.emulator.enumerations import (
@ -211,8 +210,11 @@ class CoreGrpcServer(core_pb2_grpc.CoreApiServicer):
validation_period=service.validation_period,
)
config_services.append(service_proto)
emane_models = [x.name for x in EmaneModelManager.models.values()]
return core_pb2.GetConfigResponse(
services=services, config_services=config_services
services=services,
config_services=config_services,
emane_models=emane_models,
)
def StartSession(
@ -264,11 +266,10 @@ class CoreGrpcServer(core_pb2_grpc.CoreApiServicer):
return core_pb2.StartSessionResponse(result=False, exceptions=exceptions)
# emane configs
config = session.emane.get_configs()
config.update(request.emane_config)
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_model_config(_id, config.model, config.config)
session.emane.set_config(_id, config.model, config.config)
# wlan configs
for config in request.wlan_configs:
@ -426,57 +427,6 @@ class CoreGrpcServer(core_pb2_grpc.CoreApiServicer):
"""
logger.debug("get session: %s", request)
session = self.get_session(request.session_id, context)
# links = []
# nodes = []
# for _id in session.nodes:
# node = session.nodes[_id]
# if not isinstance(node, (PtpNet, CtrlNet)):
# node_proto = grpcutils.get_node_proto(session, node)
# nodes.append(node_proto)
# node_links = get_links(node)
# links.extend(node_links)
# default_services = grpcutils.get_default_services(session)
# x, y, z = session.location.refxyz
# lat, lon, alt = session.location.refgeo
# location = core_pb2.SessionLocation(
# x=x, y=y, z=z, lat=lat, lon=lon, alt=alt, scale=session.location.refscale
# )
# hooks = grpcutils.get_hooks(session)
# emane_models = grpcutils.get_emane_models(session)
# emane_config = grpcutils.get_emane_config(session)
# emane_model_configs = grpcutils.get_emane_model_configs(session)
# wlan_configs = grpcutils.get_wlan_configs(session)
# mobility_configs = grpcutils.get_mobility_configs(session)
# service_configs = grpcutils.get_node_service_configs(session)
# config_service_configs = grpcutils.get_node_config_service_configs(session)
# session_file = str(session.file_path) if session.file_path else None
# options = get_config_options(session.options.get_configs(), session.options)
# servers = [
# core_pb2.Server(name=x.name, host=x.host)
# for x in session.distributed.servers.values()
# ]
# session_proto = core_pb2.Session(
# id=session.id,
# state=session.state.value,
# nodes=nodes,
# links=links,
# dir=str(session.directory),
# user=session.user,
# default_services=default_services,
# location=location,
# hooks=hooks,
# emane_models=emane_models,
# emane_config=emane_config,
# emane_model_configs=emane_model_configs,
# wlan_configs=wlan_configs,
# service_configs=service_configs,
# config_service_configs=config_service_configs,
# mobility_configs=mobility_configs,
# metadata=session.metadata,
# file=session_file,
# options=options,
# servers=servers,
# )
session_proto = grpcutils.convert_session(session)
return core_pb2.GetSessionResponse(session=session_proto)
@ -1122,25 +1072,9 @@ class CoreGrpcServer(core_pb2_grpc.CoreApiServicer):
"""
logger.debug("set emane config: %s", request)
session = self.get_session(request.session_id, context)
config = session.emane.get_configs()
config.update(request.config)
session.emane.config.update(request.config)
return SetEmaneConfigResponse(result=True)
def GetEmaneModels(
self, request: GetEmaneModelsRequest, context: ServicerContext
) -> GetEmaneModelsResponse:
"""
Retrieve all the EMANE models in the session
:param request: get-emane-model request
:param context: context object
:return: get-EMANE-models response that has all the models
"""
logger.debug("get emane models: %s", request)
session = self.get_session(request.session_id, context)
models = grpcutils.get_emane_models(session)
return GetEmaneModelsResponse(models=models)
def GetEmaneModelConfig(
self, request: GetEmaneModelConfigRequest, context: ServicerContext
) -> GetEmaneModelConfigResponse:
@ -1154,11 +1088,9 @@ class CoreGrpcServer(core_pb2_grpc.CoreApiServicer):
"""
logger.debug("get emane model config: %s", request)
session = self.get_session(request.session_id, context)
model = session.emane.models.get(request.model)
if not model:
raise CoreError(f"invalid emane model: {request.model}")
model = session.emane.get_model(request.model)
_id = utils.iface_config_id(request.node_id, request.iface_id)
current_config = session.emane.get_model_config(_id, request.model)
current_config = session.emane.get_config(_id, request.model)
config = get_config_options(current_config, model)
return GetEmaneModelConfigResponse(config=config)
@ -1177,7 +1109,7 @@ class CoreGrpcServer(core_pb2_grpc.CoreApiServicer):
session = self.get_session(request.session_id, context)
model_config = request.emane_model_config
_id = utils.iface_config_id(model_config.node_id, model_config.iface_id)
session.emane.set_model_config(_id, model_config.model, model_config.config)
session.emane.set_config(_id, model_config.model, model_config.config)
return SetEmaneModelConfigResponse(result=True)
def SaveXml(
@ -1192,13 +1124,10 @@ class CoreGrpcServer(core_pb2_grpc.CoreApiServicer):
"""
logger.debug("save xml: %s", request)
session = self.get_session(request.session_id, context)
_, temp_path = tempfile.mkstemp()
session.save_xml(temp_path)
with open(temp_path, "r") as xml_file:
data = xml_file.read()
return core_pb2.SaveXmlResponse(data=data)
def OpenXml(

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
)
hooks: Dict[str, Hook] = field(default_factory=dict)
emane_models: List[str] = field(default_factory=list)
emane_config: Dict[str, ConfigOption] = field(default_factory=dict)
metadata: Dict[str, str] = field(default_factory=dict)
file: Path = None
@ -837,7 +836,6 @@ class Session:
default_services=default_services,
location=SessionLocation.from_proto(proto.location),
hooks=hooks,
emane_models=list(proto.emane_models),
emane_config=ConfigOption.from_dict(proto.emane_config),
metadata=dict(proto.metadata),
file=file_path,
@ -906,12 +904,17 @@ class Session:
class CoreConfig:
services: List[Service] = field(default_factory=list)
config_services: List[ConfigService] = field(default_factory=list)
emane_models: List[str] = field(default_factory=list)
@classmethod
def from_proto(cls, proto: core_pb2.GetConfigResponse) -> "CoreConfig":
services = [Service.from_proto(x) for x in proto.services]
config_services = [ConfigService.from_proto(x) for x in proto.config_services]
return CoreConfig(services=services, config_services=config_services)
return CoreConfig(
services=services,
config_services=config_services,
emane_models=list(proto.emane_models),
)
@dataclass

View file

@ -29,6 +29,7 @@ from core.api.tlv.enumerations import (
NodeTlvs,
SessionTlvs,
)
from core.emane.modelmanager import EmaneModelManager
from core.emulator.data import (
ConfigData,
EventData,
@ -419,8 +420,7 @@ class CoreHandler(socketserver.BaseRequestHandler):
tlv_data += coreapi.CoreRegisterTlv.pack(
self.session.emane.config_type.value, self.session.emane.name
)
for model_name in self.session.emane.models:
model_class = self.session.emane.models[model_name]
for model_name, model_class in EmaneModelManager.models.items():
tlv_data += coreapi.CoreRegisterTlv.pack(
model_class.config_type.value, model_class.name
)
@ -1048,7 +1048,7 @@ class CoreHandler(socketserver.BaseRequestHandler):
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 self.session.emane.models:
elif config_data.object in EmaneModelManager.models:
replies = self.handle_config_emane_models(message_type, config_data)
else:
raise Exception("no handler for configuration: %s", config_data.object)
@ -1393,7 +1393,7 @@ class CoreHandler(socketserver.BaseRequestHandler):
if message_type == ConfigFlags.REQUEST:
logger.info("replying to configure request for %s model", object_name)
typeflags = ConfigFlags.NONE.value
config = self.session.emane.get_configs()
config = self.session.emane.config
config_response = ConfigShim.config_data(
0, node_id, typeflags, self.session.emane.emane_config, config
)
@ -1405,7 +1405,7 @@ class CoreHandler(socketserver.BaseRequestHandler):
if values_str:
config = ConfigShim.str_to_dict(values_str)
self.session.emane.set_configs(config)
self.session.emane.config = config
return replies
@ -1424,12 +1424,12 @@ class CoreHandler(socketserver.BaseRequestHandler):
logger.info("replying to configure request for model: %s", object_name)
typeflags = ConfigFlags.NONE.value
model_class = self.session.emane.models.get(object_name)
model_class = self.session.emane.get_model(object_name)
if not model_class:
logger.warning("model class does not exist: %s", object_name)
return []
config = self.session.emane.get_model_config(node_id, object_name)
config = self.session.emane.get_config(node_id, object_name)
config_response = ConfigShim.config_data(
0, node_id, typeflags, model_class, config
)
@ -1439,12 +1439,11 @@ class CoreHandler(socketserver.BaseRequestHandler):
if not object_name:
logger.warning("no configuration object for node: %s", node_id)
return []
parsed_config = {}
if values_str:
parsed_config = ConfigShim.str_to_dict(values_str)
self.session.emane.set_model_config(node_id, object_name, parsed_config)
self.session.emane.node_models[node_id] = object_name
self.session.emane.set_config(node_id, object_name, parsed_config)
return replies
@ -1853,7 +1852,7 @@ class CoreHandler(socketserver.BaseRequestHandler):
self.session.broadcast_config(config_data)
# send global emane config
config = self.session.emane.get_configs()
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
@ -1861,11 +1860,9 @@ class CoreHandler(socketserver.BaseRequestHandler):
self.session.broadcast_config(config_data)
# send emane model configs
for node_id in self.session.emane.nodes():
emane_configs = self.session.emane.get_all_configs(node_id)
for model_name in emane_configs:
config = emane_configs[model_name]
model_class = self.session.emane.models[model_name]
for node_id, model_configs in self.session.emane.node_configs.items():
for model_name, config in model_configs.items():
model_class = self.session.emane.get_model(model_name)
logger.debug(
"emane config: node(%s) class(%s) values(%s)",
node_id,

View file

@ -31,7 +31,7 @@ class ConfigServiceManager:
"""
service_class = self.services.get(name)
if service_class is None:
raise CoreError(f"service does not exit {name}")
raise CoreError(f"service does not exist {name}")
return service_class
def add(self, service: Type[ConfigService]) -> None:

View file

@ -12,16 +12,12 @@ 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, ModelManager
from core.config import ConfigGroup, Configuration
from core.emane import emanemanifest
from core.emane.bypass import EmaneBypassModel
from core.emane.commeffect import EmaneCommEffectModel
from core.emane.emanemodel import EmaneModel
from core.emane.ieee80211abg import EmaneIeee80211abgModel
from core.emane.linkmonitor import EmaneLinkMonitor
from core.emane.modelmanager import EmaneModelManager
from core.emane.nodes import EmaneNet
from core.emane.rfpipe import EmaneRfPipeModel
from core.emane.tdma import EmaneTdmaModel
from core.emulator.data import LinkData
from core.emulator.enumerations import (
ConfigDataTypes,
@ -55,15 +51,9 @@ except ImportError:
EventServiceException = None
logger.debug("compatible emane python bindings not installed")
EMANE_MODELS = [
EmaneRfPipeModel,
EmaneIeee80211abgModel,
EmaneCommEffectModel,
EmaneBypassModel,
EmaneTdmaModel,
]
DEFAULT_EMANE_PREFIX = "/usr"
DEFAULT_DEV = "ctrl0"
DEFAULT_LOG_LEVEL: int = 3
class EmaneState(Enum):
@ -78,7 +68,7 @@ class StartData:
ifaces: List[CoreInterface] = field(default_factory=list)
class EmaneManager(ModelManager):
class EmaneManager:
"""
EMANE controller object. Lives in a Session instance and is used for
building EMANE config files for all EMANE networks in this emulation, and for
@ -87,9 +77,6 @@ class EmaneManager(ModelManager):
name: str = "emane"
config_type: RegisterTlvs = RegisterTlvs.EMULATION_SERVER
NOT_READY: int = 2
EVENTCFGVAR: str = "LIBEMANEEVENTSERVICECONFIG"
DEFAULT_LOG_LEVEL: int = 3
def __init__(self, session: "Session") -> None:
"""
@ -116,7 +103,9 @@ class EmaneManager(ModelManager):
# model for global EMANE configuration options
self.emane_config: EmaneGlobalModel = EmaneGlobalModel(session)
self.set_configs(self.emane_config.default_values())
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] = {}
# link monitor
self.link_monitor: EmaneLinkMonitor = EmaneLinkMonitor(self)
@ -124,14 +113,63 @@ class EmaneManager(ModelManager):
self.service: Optional[EventService] = None
self.eventchannel: Optional[Tuple[str, int, str]] = None
self.event_device: Optional[str] = None
self.emane_check()
def next_nem_id(self) -> int:
nem_id = int(self.get_config("nem_id_start"))
nem_id = int(self.config["nem_id_start"])
while nem_id in self.nems_to_ifaces:
nem_id += 1
return nem_id
def get_config(
self, key: int, model: str, default: bool = True
) -> Optional[Dict[str, str]]:
"""
Get the current or default configuration for an emane model.
:param key: key to get configuration for
:param model: emane model to get configuration for
:param default: True to return default configuration when none exists, False
otherwise
:return: emane model configuration
:raises CoreError: when model does not exist
"""
model_class = self.get_model(model)
model_configs = self.node_configs.get(key)
config = None
if model_configs:
config = model_configs.get(model)
if config is None and default:
config = model_class.default_values()
return config
def set_config(self, key: int, model: str, config: Dict[str, str] = None) -> None:
"""
Sets and update the provided configuration against the default model
or currently set emane model configuration.
:param key: configuration key to set
:param model: model to set configuration for
:param config: configuration to update current configuration with
:return: nothing
:raises CoreError: when model does not exist
"""
self.get_model(model)
model_config = self.get_config(key, model)
config = config if config else {}
model_config.update(config)
model_configs = self.node_configs.setdefault(key, {})
model_configs[model] = model_config
def get_model(self, model_name: str) -> Type[EmaneModel]:
"""
Convenience method for getting globally loaded emane models.
:param model_name: name of model to retrieve
:return: emane model class
:raises CoreError: when model does not exist
"""
return EmaneModelManager.get(model_name)
def get_iface_config(
self, emane_net: EmaneNet, iface: CoreInterface
) -> Dict[str, str]:
@ -149,46 +187,28 @@ class EmaneManager(ModelManager):
# try to retrieve interface specific configuration
if iface.node_id is not None:
key = utils.iface_config_id(iface.node.id, iface.node_id)
config = self.get_configs(node_id=key, config_type=model_name)
config = self.get_config(key, model_name, default=False)
# attempt to retrieve node specific config, when iface config is not present
if not config:
config = self.get_configs(node_id=iface.node.id, config_type=model_name)
config = self.get_config(iface.node.id, model_name, default=False)
# attempt to get emane net specific config, when node config is not present
if not config:
# with EMANE 0.9.2+, we need an extra NEM XML from
# model.buildnemxmlfiles(), so defaults are returned here
config = self.get_configs(node_id=emane_net.id, config_type=model_name)
config = self.get_config(emane_net.id, model_name, default=False)
# return default config values, when a config is not present
if not config:
config = emane_net.model.default_values()
return config
def config_reset(self, node_id: int = None) -> None:
super().config_reset(node_id)
self.set_configs(self.emane_config.default_values())
def emane_check(self) -> None:
"""
Check if emane is installed and load models.
:return: nothing
"""
# check for emane
path = utils.which("emane", required=False)
if not path:
logger.info("emane is not installed")
return
# get version
emane_version = utils.cmd("emane --version")
logger.info("using emane: %s", emane_version)
# load default emane models
self.load_models(EMANE_MODELS)
# load custom models
custom_models_path = self.session.options.get_config("emane_models_dir")
if custom_models_path is not None:
custom_models_path = Path(custom_models_path)
emane_models = utils.load_classes(custom_models_path, EmaneModel)
self.load_models(emane_models)
if node_id is None:
self.config = self.emane_config.default_values()
self.node_configs.clear()
self.node_models.clear()
else:
self.node_configs.get(node_id, {}).clear()
del self.node_models[node_id]
def deleteeventservice(self) -> None:
if self.service:
@ -207,13 +227,12 @@ class EmaneManager(ModelManager):
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.get_config("eventservicegroup").split(":")
self.event_device = self.get_config("eventservicedevice")
group, port = self.config["eventservicegroup"].split(":")
self.event_device = self.config["eventservicedevice"]
eventnetidx = self.session.get_control_net_index(self.event_device)
if eventnetidx < 0:
logger.error(
@ -238,19 +257,6 @@ class EmaneManager(ModelManager):
except EventServiceException:
logger.exception("error instantiating emane EventService")
def load_models(self, emane_models: List[Type[EmaneModel]]) -> None:
"""
Load EMANE models and make them available.
"""
for emane_model in emane_models:
logger.debug("loading emane model: %s", emane_model.__name__)
emane_prefix = self.session.options.get_config(
"emane_prefix", default=DEFAULT_EMANE_PREFIX
)
emane_prefix = Path(emane_prefix)
emane_model.load(emane_prefix)
self.models[emane_model.name] = emane_model
def add_node(self, emane_net: EmaneNet) -> None:
"""
Add EMANE network object to this manager.
@ -302,7 +308,7 @@ class EmaneManager(ModelManager):
# control network bridge required for EMANE 0.9.2
# - needs to exist when eventservice binds to it (initeventservice)
otadev = self.get_config("otamanagerdevice")
otadev = self.config["otamanagerdevice"]
netidx = self.session.get_control_net_index(otadev)
logger.debug("emane ota manager device: index(%s) otadev(%s)", netidx, otadev)
if netidx < 0:
@ -315,7 +321,7 @@ class EmaneManager(ModelManager):
self.session.add_remove_control_net(
net_index=netidx, remove=False, conf_required=False
)
eventdev = self.get_config("eventservicedevice")
eventdev = self.config["eventservicedevice"]
logger.debug("emane event service device: eventdev(%s)", eventdev)
if eventdev != otadev:
netidx = self.session.get_control_net_index(eventdev)
@ -408,7 +414,7 @@ class EmaneManager(ModelManager):
logger.exception("error writing to emane nem file")
def links_enabled(self) -> bool:
return self.get_config("link_enabled") == "1"
return self.config["link_enabled"] == "1"
def poststartup(self) -> None:
"""
@ -470,14 +476,12 @@ class EmaneManager(ModelManager):
for node_id in self._emane_nets:
emane_net = self._emane_nets[node_id]
logger.debug("checking emane model for node: %s", node_id)
# skip nodes that already have a model set
if emane_net.model:
logger.debug(
"node(%s) already has model(%s)", emane_net.id, emane_net.model.name
)
continue
# set model configured for node, due to legacy messaging configuration
# before nodes exist
model_name = self.node_models.get(node_id)
@ -485,9 +489,9 @@ class EmaneManager(ModelManager):
logger.error("emane node(%s) has no node model", node_id)
raise ValueError("emane node has no model set")
config = self.get_model_config(node_id=node_id, model_name=model_name)
config = self.get_config(node_id, model_name)
logger.debug("setting emane model(%s) config(%s)", model_name, config)
model_class = self.models[model_name]
model_class = self.get_model(model_name)
emane_net.setmodel(model_class, config)
def get_nem_link(
@ -525,22 +529,19 @@ class EmaneManager(ModelManager):
default_values = self.emane_config.default_values()
for name in ["eventservicegroup", "eventservicedevice"]:
a = default_values[name]
b = self.get_config(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.get_config("eventservicegroup").split(":")
group, port = self.config["eventservicegroup"].split(":")
except ValueError:
logger.exception("invalid eventservicegroup in EMANE config")
return
dev = self.get_config("eventservicedevice")
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(
@ -554,7 +555,7 @@ class EmaneManager(ModelManager):
Add a control network even if the user has not configured one.
"""
logger.info("starting emane daemons...")
loglevel = str(EmaneManager.DEFAULT_LOG_LEVEL)
loglevel = str(DEFAULT_LOG_LEVEL)
cfgloglevel = self.session.options.get_config_int("emane_log_level")
realtime = self.session.options.get_config_bool("emane_realtime", default=True)
if cfgloglevel:
@ -564,11 +565,11 @@ class EmaneManager(ModelManager):
if realtime:
emanecmd += " -r"
if isinstance(node, CoreNode):
otagroup, _otaport = self.get_config("otamanagergroup").split(":")
otadev = self.get_config("otamanagerdevice")
otagroup, _otaport = self.config["otamanagergroup"].split(":")
otadev = self.config["otamanagerdevice"]
otanetidx = self.session.get_control_net_index(otadev)
eventgroup, _eventport = self.get_config("eventservicegroup").split(":")
eventdev = self.get_config("eventservicedevice")
eventgroup, _eventport = self.config["eventservicegroup"].split(":")
eventdev = self.config["eventservicedevice"]
eventservicenetidx = self.session.get_control_net_index(eventdev)
# control network not yet started here

View file

@ -189,9 +189,9 @@ class EmaneLinkMonitor:
self.running: bool = False
def start(self) -> None:
self.loss_threshold = int(self.emane_manager.get_config("loss_threshold"))
self.link_interval = int(self.emane_manager.get_config("link_interval"))
self.link_timeout = int(self.emane_manager.get_config("link_timeout"))
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"])
self.initialize()
if not self.clients:
logger.info("no valid emane models to monitor links")

View file

@ -0,0 +1,41 @@
import logging
from pathlib import Path
from typing import Dict, List, Type
from core import utils
from core.emane.emanemodel import EmaneModel
from core.errors import CoreError
logger = logging.getLogger(__name__)
class EmaneModelManager:
models: Dict[str, Type[EmaneModel]] = {}
@classmethod
def load(cls, path: Path, prefix: Path) -> List[str]:
"""
Load EMANE models and make them available.
"""
subdirs = [x for x in path.iterdir() if x.is_dir()]
subdirs.append(path)
errors = []
for subdir in subdirs:
logger.debug("loading emane models from: %s", subdir)
models = utils.load_classes(subdir, EmaneModel)
for model in models:
logger.debug("loading emane model: %s", model.name)
try:
model.load(prefix)
cls.models[model.name] = model
except CoreError as e:
errors.append(model.name)
logger.debug("not loading service(%s): %s", model.name, e)
return errors
@classmethod
def get(cls, name: str) -> Type[EmaneModel]:
model = cls.models.get(name)
if model is None:
raise CoreError(f"emame model does not exist {name}")
return model

View file

View file

@ -51,17 +51,16 @@ class EmaneTdmaModel(emanemodel.EmaneModel):
:return: nothing
"""
# get configured schedule
config = self.session.emane.get_configs(node_id=self.id, config_type=self.name)
config = self.session.emane.get_config(self.id, self.name)
if not config:
return
schedule = config[self.schedule_name]
# get the set event device
event_device = self.session.emane.event_device
schedule = Path(config[self.schedule_name])
if not schedule.is_file():
logger.warning("ignoring invalid tdma schedule: %s", schedule)
return
# initiate tdma schedule
event_device = self.session.emane.event_device
logger.info(
"setting up tdma schedule: schedule(%s) device(%s)", schedule, event_device
)
args = f"emaneevent-tdmaschedule -i {event_device} {schedule}"
utils.cmd(args)
utils.cmd(f"emaneevent-tdmaschedule -i {event_device} {schedule}")

View file

@ -9,12 +9,16 @@ from typing import Dict, List, Type
import core.services
from core import configservices, utils
from core.configservice.manager import ConfigServiceManager
from core.emane import models
from core.emane.modelmanager import EmaneModelManager
from core.emulator.session import Session
from core.executables import get_requirements
from core.services.coreservices import ServiceManager
logger = logging.getLogger(__name__)
DEFAULT_EMANE_PREFIX: str = "/usr"
def signal_handler(signal_number: int, _) -> None:
"""
@ -61,6 +65,10 @@ class CoreEmu:
self.service_manager: ConfigServiceManager = ConfigServiceManager()
self._load_services()
# check and load emane
self.has_emane: bool = False
self._load_emane()
# check executables exist on path
self._validate_env()
@ -103,6 +111,32 @@ class CoreEmu:
custom_dir = Path(custom_dir)
self.service_manager.load(custom_dir)
def _load_emane(self) -> None:
"""
Check if emane is installed and load models.
:return: nothing
"""
# check for emane
path = utils.which("emane", required=False)
self.has_emane = path is not None
if not self.has_emane:
logger.info("emane is not installed, emane functionality disabled")
return
# get version
emane_version = utils.cmd("emane --version")
logger.info("using emane: %s", emane_version)
prefix = self.config.get("emane_prefix", DEFAULT_EMANE_PREFIX)
prefix = Path(prefix)
default_path = Path(models.__file__).resolve().parent
EmaneModelManager.load(default_path, prefix)
# load custom models
custom_path = self.config.get("emane_models_dir")
if custom_path is not None:
logger.info("loading custom emane models: %s", custom_path)
custom_path = Path(custom_path)
EmaneModelManager.load(custom_path, prefix)
def shutdown(self) -> None:
"""
Shutdown all CORE session.

View file

@ -560,12 +560,8 @@ class Session:
# ensure default emane configuration
if isinstance(node, EmaneNet) and options.emane:
model = self.emane.models.get(options.emane)
if not model:
raise CoreError(
f"node({node.name}) emane model({options.emane}) does not exist"
)
node.model = model(self, node.id)
model_class = self.emane.get_model(options.emane)
node.model = model_class(self, node.id)
if self.state == EventTypes.RUNTIME_STATE:
self.emane.add_node(node)
# set default wlan config if needed

View file

@ -77,6 +77,7 @@ class CoreClient:
self.config_services: Dict[str, ConfigService] = {}
# loaded configuration data
self.emane_models: List[str] = []
self.servers: Dict[str, CoreServer] = {}
self.custom_nodes: Dict[str, NodeDraw] = {}
self.custom_observers: Dict[str, Observer] = {}
@ -392,6 +393,7 @@ class CoreClient:
self.client.connect()
# get current core configurations services/config services
core_config = self.client.get_config()
self.emane_models = core_config.emane_models
for service in core_config.services:
group_services = self.services.setdefault(service.group, set())
group_services.add(service.name)
@ -639,11 +641,11 @@ class CoreClient:
image = "ubuntu:latest"
emane = None
if node_type == NodeType.EMANE:
if not self.session.emane_models:
if not self.emane_models:
dialog = EmaneInstallDialog(self.app)
dialog.show()
return
emane = self.session.emane_models[0]
emane = self.emane_models[0]
name = f"emane{node_id}"
elif node_type == NodeType.WIRELESS_LAN:
name = f"wlan{node_id}"

View file

@ -115,7 +115,7 @@ class EmaneConfigDialog(Dialog):
self.radiovar: tk.IntVar = tk.IntVar()
self.radiovar.set(1)
self.emane_models: List[str] = [
x.split("_")[1] for x in self.app.core.session.emane_models
x.split("_")[1] for x in self.app.core.emane_models
]
model = self.node.emane.split("_")[1]
self.emane_model: tk.StringVar = tk.StringVar(value=model)

View file

@ -82,7 +82,7 @@ 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.get_configs()
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]
@ -379,19 +379,15 @@ class CoreXmlWriter:
emane_global_configuration = create_emane_config(self.session)
self.scenario.append(emane_global_configuration)
emane_configurations = etree.Element("emane_configurations")
for node_id in self.session.emane.nodes():
all_configs = self.session.emane.get_all_configs(node_id)
if not all_configs:
continue
for node_id, model_configs in self.session.emane.node_configs.items():
node_id, iface_id = utils.parse_iface_config_id(node_id)
for model_name in all_configs:
config = all_configs[model_name]
for model_name, config in model_configs.items():
logger.debug(
"writing emane config node(%s) model(%s)", node_id, model_name
)
model = self.session.emane.models[model_name]
model_class = self.session.emane.get_model(model_name)
emane_configuration = create_emane_model_config(
node_id, model, config, iface_id
node_id, model_class, config, iface_id
)
emane_configurations.append(emane_configuration)
if emane_configurations.getchildren():
@ -748,7 +744,7 @@ class CoreXmlReader:
name = config.get("name")
value = config.get("value")
configs[name] = value
self.session.emane.set_configs(config=configs)
self.session.emane.config = configs
def read_emane_configs(self) -> None:
emane_configurations = self.scenario.find("emane_configurations")
@ -765,9 +761,7 @@ class CoreXmlReader:
node = self.session.nodes.get(node_id)
if not node:
raise CoreXmlError(f"node for emane config doesn't exist: {node_id}")
model = self.session.emane.models.get(model_name)
if not model:
raise CoreXmlError(f"invalid emane model: {model_name}")
self.session.emane.get_model(model_name)
if iface_id is not None and iface_id not in node.ifaces:
raise CoreXmlError(
f"invalid interface id({iface_id}) for node({node.name})"
@ -796,7 +790,7 @@ class CoreXmlReader:
"reading emane configuration node(%s) model(%s)", node_id, model_name
)
node_id = utils.iface_config_id(node_id, iface_id)
self.session.emane.set_model_config(node_id, model_name, configs)
self.session.emane.set_config(node_id, model_name, configs)
def read_mobility_configs(self) -> None:
mobility_configurations = self.scenario.find("mobility_configurations")

View file

@ -166,7 +166,7 @@ def build_platform_xml(
if not isinstance(data.node, CoreNode) and name in transport_configs:
value = control_net.brname
else:
value = emane_manager.get_config(name)
value = emane_manager.config[name]
add_param(platform_element, name, value)
# create nem xml entries for all interfaces

View file

@ -1,7 +1,7 @@
# required imports
from core.api.grpc import client
from core.api.grpc.wrappers import NodeType, Position
from core.emane.ieee80211abg import EmaneIeee80211abgModel
from core.emane.models.ieee80211abg import EmaneIeee80211abgModel
# interface helper
iface_helper = client.InterfaceHelper(ip4_prefix="10.0.0.0/24", ip6_prefix="2001::/64")

View file

@ -6,7 +6,7 @@ with the GUI.
import argparse
import logging
from core.emane.ieee80211abg import EmaneIeee80211abgModel
from core.emane.models.ieee80211abg import EmaneIeee80211abgModel
from core.emane.nodes import EmaneNet
from core.emulator.coreemu import CoreEmu
from core.emulator.data import IpPrefixes, NodeOptions

View file

@ -1,5 +1,5 @@
# required imports
from core.emane.ieee80211abg import EmaneIeee80211abgModel
from core.emane.models.ieee80211abg import EmaneIeee80211abgModel
from core.emane.nodes import EmaneNet
from core.emulator.coreemu import CoreEmu
from core.emulator.data import IpPrefixes, NodeOptions

View file

@ -123,8 +123,6 @@ service CoreApi {
}
// globals
rpc GetEmaneModels (emane.GetEmaneModelsRequest) returns (emane.GetEmaneModelsResponse) {
}
rpc GetConfig (GetConfigRequest) returns (GetConfigResponse) {
}
}
@ -584,17 +582,16 @@ message Session {
repeated services.ServiceDefaults default_services = 7;
SessionLocation location = 8;
repeated Hook hooks = 9;
repeated string emane_models = 10;
map<string, common.ConfigOption> emane_config = 11;
repeated emane.GetEmaneModelConfig emane_model_configs = 12;
map<int32, common.MappedConfig> wlan_configs = 13;
repeated services.NodeServiceConfig service_configs = 14;
repeated configservices.ConfigServiceConfig config_service_configs = 15;
map<int32, common.MappedConfig> mobility_configs = 16;
map<string, string> metadata = 17;
string file = 18;
map<string, common.ConfigOption> options = 19;
repeated Server servers = 20;
map<string, common.ConfigOption> emane_config = 10;
repeated emane.GetEmaneModelConfig emane_model_configs = 11;
map<int32, common.MappedConfig> wlan_configs = 12;
repeated services.NodeServiceConfig service_configs = 13;
repeated configservices.ConfigServiceConfig config_service_configs = 14;
map<int32, common.MappedConfig> mobility_configs = 15;
map<string, string> metadata = 16;
string file = 17;
map<string, common.ConfigOption> options = 18;
repeated Server servers = 19;
}
message SessionSummary {

View file

@ -21,14 +21,6 @@ message SetEmaneConfigResponse {
bool result = 1;
}
message GetEmaneModelsRequest {
int32 session_id = 1;
}
message GetEmaneModelsResponse {
repeated string models = 1;
}
message GetEmaneModelConfigRequest {
int32 session_id = 1;
int32 node_id = 2;

View file

@ -9,13 +9,13 @@ from xml.etree import ElementTree
import pytest
from core import utils
from core.emane.bypass import EmaneBypassModel
from core.emane.commeffect import EmaneCommEffectModel
from core.emane.emanemodel import EmaneModel
from core.emane.ieee80211abg import EmaneIeee80211abgModel
from core.emane.models.bypass import EmaneBypassModel
from core.emane.models.commeffect import EmaneCommEffectModel
from core.emane.models.ieee80211abg import EmaneIeee80211abgModel
from core.emane.models.rfpipe import EmaneRfPipeModel
from core.emane.models.tdma import EmaneTdmaModel
from core.emane.nodes import EmaneNet
from core.emane.rfpipe import EmaneRfPipeModel
from core.emane.tdma import EmaneTdmaModel
from core.emulator.data import IpPrefixes, NodeOptions
from core.emulator.session import Session
from core.errors import CoreCommandError, CoreError
@ -100,14 +100,13 @@ class TestEmane:
# create emane node for networking the core nodes
session.set_location(47.57917, -122.13232, 2.00000, 1.0)
options = NodeOptions()
options = NodeOptions(emane=model.name)
options.set_position(80, 50)
emane_network = session.add_node(EmaneNet, options=options)
session.emane.set_model(emane_network, model)
# configure tdma
if model == EmaneTdmaModel:
session.emane.set_model_config(
session.emane.set_config(
emane_network.id, EmaneTdmaModel.name, {"schedule": str(_SCHEDULE)}
)
@ -142,13 +141,13 @@ class TestEmane:
"""
# create emane node for networking the core nodes
session.set_location(47.57917, -122.13232, 2.00000, 1.0)
options = NodeOptions()
options = NodeOptions(emane=EmaneIeee80211abgModel.name)
options.set_position(80, 50)
emane_network = session.add_node(EmaneNet, options=options)
config_key = "txpower"
config_value = "10"
session.emane.set_model(
emane_network, EmaneIeee80211abgModel, {config_key: config_value}
session.emane.set_config(
emane_network.id, EmaneIeee80211abgModel.name, {config_key: config_value}
)
# create nodes
@ -174,7 +173,7 @@ class TestEmane:
# save xml
xml_file = tmpdir.join("session.xml")
file_path = xml_file.strpath
session.save_xml(file_path)
session.save_xml(Path(file_path))
# verify xml file was created and can be parsed
assert xml_file.isfile()
@ -190,12 +189,11 @@ class TestEmane:
assert not session.get_node(node2_id, CoreNode)
# load saved xml
session.open_xml(file_path, start=True)
session.open_xml(Path(file_path), start=True)
# retrieve configuration we set originally
value = str(
session.emane.get_config(config_key, emane_id, EmaneIeee80211abgModel.name)
)
config = session.emane.get_config(emane_id, EmaneIeee80211abgModel.name)
value = config[config_key]
# verify nodes and configuration were restored
assert session.get_node(node1_id, CoreNode)
@ -221,9 +219,9 @@ class TestEmane:
session.add_link(node1.id, emane_node.id, iface1_data)
session.add_link(node2.id, emane_node.id, iface2_data)
# set node specific conifg
# set node specific config
datarate = "101"
session.emane.set_model_config(
session.emane.set_config(
node1.id, EmaneRfPipeModel.name, {"datarate": datarate}
)
@ -233,7 +231,7 @@ class TestEmane:
# save xml
xml_file = tmpdir.join("session.xml")
file_path = xml_file.strpath
session.save_xml(file_path)
session.save_xml(Path(file_path))
# verify xml file was created and can be parsed
assert xml_file.isfile()
@ -251,7 +249,7 @@ class TestEmane:
assert not session.get_node(emane_node.id, EmaneNet)
# load saved xml
session.open_xml(file_path, start=True)
session.open_xml(Path(file_path), start=True)
# verify nodes have been recreated
assert session.get_node(node1.id, CoreNode)
@ -262,7 +260,7 @@ class TestEmane:
node = session.nodes[node_id]
links += node.links()
assert len(links) == 2
config = session.emane.get_model_config(node1.id, EmaneRfPipeModel.name)
config = session.emane.get_config(node1.id, EmaneRfPipeModel.name)
assert config["datarate"] == datarate
def test_xml_emane_interface_config(
@ -286,7 +284,7 @@ class TestEmane:
# set node specific conifg
datarate = "101"
config_id = utils.iface_config_id(node1.id, iface1_data.id)
session.emane.set_model_config(
session.emane.set_config(
config_id, EmaneRfPipeModel.name, {"datarate": datarate}
)
@ -296,7 +294,7 @@ class TestEmane:
# save xml
xml_file = tmpdir.join("session.xml")
file_path = xml_file.strpath
session.save_xml(file_path)
session.save_xml(Path(file_path))
# verify xml file was created and can be parsed
assert xml_file.isfile()
@ -314,7 +312,7 @@ class TestEmane:
assert not session.get_node(emane_node.id, EmaneNet)
# load saved xml
session.open_xml(file_path, start=True)
session.open_xml(Path(file_path), start=True)
# verify nodes have been recreated
assert session.get_node(node1.id, CoreNode)
@ -325,5 +323,5 @@ class TestEmane:
node = session.nodes[node_id]
links += node.links()
assert len(links) == 2
config = session.emane.get_model_config(config_id, EmaneRfPipeModel.name)
config = session.emane.get_config(config_id, EmaneRfPipeModel.name)
assert config["datarate"] == datarate

View file

@ -6,7 +6,7 @@ from core.config import (
Configuration,
ModelManager,
)
from core.emane.ieee80211abg import EmaneIeee80211abgModel
from core.emane.models.ieee80211abg import EmaneIeee80211abgModel
from core.emulator.enumerations import ConfigDataTypes
from core.emulator.session import Session
from core.location.mobility import BasicRangeModel

View file

@ -33,7 +33,7 @@ from core.api.grpc.wrappers import (
)
from core.api.tlv.dataconversion import ConfigShim
from core.api.tlv.enumerations import ConfigFlags
from core.emane.ieee80211abg import EmaneIeee80211abgModel
from core.emane.models.ieee80211abg import EmaneIeee80211abgModel
from core.emane.nodes import EmaneNet
from core.emulator.data import EventData, IpPrefixes, NodeData, NodeOptions
from core.emulator.enumerations import EventTypes, ExceptionLevels
@ -151,7 +151,7 @@ class TestGrpc:
location_alt,
)
assert real_session.location.refscale == location_scale
assert real_session.emane.get_config(emane_config_key) == emane_config_value
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
)
@ -543,7 +543,7 @@ class TestGrpc:
# then
assert result is True
config = session.emane.get_configs()
config = session.emane.config
assert len(config) > 1
assert config[config_key] == config_value
@ -554,7 +554,7 @@ class TestGrpc:
session.set_location(47.57917, -122.13232, 2.00000, 1.0)
options = NodeOptions(emane=EmaneIeee80211abgModel.name)
emane_network = session.add_node(EmaneNet, options=options)
session.emane.set_model(emane_network, EmaneIeee80211abgModel)
session.emane.node_models[emane_network.id] = EmaneIeee80211abgModel.name
config_key = "bandwidth"
config_value = "900000"
option = ConfigOption(
@ -574,9 +574,7 @@ class TestGrpc:
# then
assert result is True
config = session.emane.get_model_config(
emane_network.id, EmaneIeee80211abgModel.name
)
config = session.emane.get_config(emane_network.id, EmaneIeee80211abgModel.name)
assert config[config_key] == config_value
def test_get_emane_model_config(self, grpc_server: CoreGrpcServer):
@ -586,7 +584,7 @@ class TestGrpc:
session.set_location(47.57917, -122.13232, 2.00000, 1.0)
options = NodeOptions(emane=EmaneIeee80211abgModel.name)
emane_network = session.add_node(EmaneNet, options=options)
session.emane.set_model(emane_network, EmaneIeee80211abgModel)
session.emane.node_models[emane_network.id] = EmaneIeee80211abgModel.name
# then
with client.context_connect():
@ -597,18 +595,6 @@ class TestGrpc:
# then
assert len(config) > 0
def test_get_emane_models(self, grpc_server: CoreGrpcServer):
# given
client = CoreGrpcClient()
session = grpc_server.coreemu.create_session()
# then
with client.context_connect():
models = client.get_emane_models(session.id)
# then
assert len(models) > 0
def test_get_mobility_config(self, grpc_server: CoreGrpcServer):
# given
client = CoreGrpcClient()

View file

@ -22,7 +22,7 @@ from core.api.tlv.enumerations import (
NodeTlvs,
SessionTlvs,
)
from core.emane.ieee80211abg import EmaneIeee80211abgModel
from core.emane.models.ieee80211abg import EmaneIeee80211abgModel
from core.emulator.enumerations import EventTypes, MessageFlags, NodeTypes, RegisterTlvs
from core.errors import CoreError
from core.location.mobility import BasicRangeModel
@ -939,9 +939,7 @@ class TestGui:
coretlv.handle_message(message)
config = coretlv.session.emane.get_model_config(
wlan.id, EmaneIeee80211abgModel.name
)
config = coretlv.session.emane.get_config(wlan.id, EmaneIeee80211abgModel.name)
assert config[config_key] == config_value
def test_config_emane_request(self, coretlv: CoreHandler):
@ -973,5 +971,5 @@ class TestGui:
coretlv.handle_message(message)
config = coretlv.session.emane.get_configs()
config = coretlv.session.emane.config
assert config[config_key] == config_value

View file

@ -300,7 +300,7 @@ will use the defaults. When no configuration is used, the defaults are used.
# required imports
from core.api.grpc import client
from core.api.grpc.core_pb2 import NodeType, Position
from core.emane.ieee80211abg import EmaneIeee80211abgModel
from core.emane.models.ieee80211abg import EmaneIeee80211abgModel
# interface helper
iface_helper = client.InterfaceHelper(ip4_prefix="10.0.0.0/24", ip6_prefix="2001::/64")

View file

@ -278,7 +278,7 @@ will use the defaults. When no configuration is used, the defaults are used.
```python
# required imports
from core.emane.ieee80211abg import EmaneIeee80211abgModel
from core.emane.models.ieee80211abg import EmaneIeee80211abgModel
from core.emane.nodes import EmaneNet
from core.emulator.coreemu import CoreEmu
from core.emulator.data import IpPrefixes, NodeOptions